* Re: [PATCH v13 31/48] KVM: arm64: WARN on injected undef exceptions
From: Suzuki K Poulose @ 2026-03-30 10:50 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Zenghui Yu, linux-arm-kernel, linux-kernel,
Joey Gouly, Alexandru Elisei, Christoffer Dall, Fuad Tabba,
linux-coco, Ganapatrao Kulkarni, Gavin Shan, Shanker Donthineni,
Alper Gun, Aneesh Kumar K . V, Emi Kisanuki, Vishal Annapurve
In-Reply-To: <20260318155413.793430-32-steven.price@arm.com>
On 18/03/2026 15:53, Steven Price wrote:
> The RMM doesn't allow injection of a undefined exception into a realm
> guest. Add a WARN to catch if this ever happens.
>
> Signed-off-by: Steven Price <steven.price@arm.com>
> Reviewed-by: Gavin Shan <gshan@redhat.com>
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> Changes since v6:
> * if (x) WARN(1, ...) makes no sense, just WARN(x, ...)!
> ---
> arch/arm64/kvm/inject_fault.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c
> index 6492397b73d7..613f223bc7a3 100644
> --- a/arch/arm64/kvm/inject_fault.c
> +++ b/arch/arm64/kvm/inject_fault.c
> @@ -327,6 +327,7 @@ void kvm_inject_size_fault(struct kvm_vcpu *vcpu)
> */
> void kvm_inject_undefined(struct kvm_vcpu *vcpu)
> {
> + WARN(vcpu_is_rec(vcpu), "Unexpected undefined exception injection to REC");
> if (vcpu_el1_is_32bit(vcpu))
> inject_undef32(vcpu);
> else
^ permalink raw reply
* Re: [PATCH v2 03/31] x86/virt/tdx: Add tdx_page_array helpers for new TDX Module objects
From: Xu Yilun @ 2026-03-30 10:25 UTC (permalink / raw)
To: Edgecombe, Rick P
Cc: Williams, Dan J, linux-pci@vger.kernel.org,
linux-coco@lists.linux.dev, x86@kernel.org, Gao, Chao, Xu, Yilun,
dave.hansen@linux.intel.com, kas@kernel.org,
baolu.lu@linux.intel.com, Jiang, Dave, Li, Xiaoyao,
Verma, Vishal L, Duan, Zhenzhong, kvm@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <1dd0b8f4d1ad5cd07b5139ed8c944e1e1d004661.camel@intel.com>
> On Sat, 2026-03-28 at 00:01 +0800, Xu Yilun wrote:
> > Add struct tdx_page_array definition for new TDX Module object
> > types - HPA_ARRAY_T and HPA_LIST_INFO.
>
> This is unfortunate. I see you agree in the comments.
Yes, basically they are defining the same concept, behave mostly the same
but some differences...
>
> >
> > They are used as input/output
> > parameters in newly defined SEAMCALLs. Also define some helpers to
> > allocate, setup and free tdx_page_array.
> >
> > HPA_ARRAY_T and HPA_LIST_INFO are similar in most aspects. They both
> > represent a list of pages for TDX Module accessing. There are several
> > use cases for these 2 structures:
> >
> > - As SEAMCALL inputs. They are claimed by TDX Module as control pages.
> > Control pages are private pages for TDX Module to hold its internal
> > control structures or private data. TDR, TDCS, TDVPR... are existing
> > control pages, just not added via tdx_page_array.
> > - As SEAMCALL outputs. They were TDX Module control pages and now are
> > released.
> > - As SEAMCALL inputs. They are just temporary buffers for exchanging
> > data blobs in one SEAMCALL. TDX Module will not hold them for long
> > time.
>
> This is kind of verbose for what it seems to be trying to say. It's just that
I assume if you feel the explanation of "what is control page" is off
track. I added it cause the term firstly appears in x86 (only in KVM
TDX previously), and people ask the definition:
https://lore.kernel.org/all/cfcfb160-fcd2-4a75-9639-5f7f0894d14b@intel.com/
> these types can be input or output params. The TDX module could hold on to the
> pages for a long time, or just transiently.
Mm.. I'm trying to ramp up on the kernel API level flow:
For control pages, it would be hold by TDX Module long time, so host
inputs the page array, later TDX Module outputs the page array back.
Host need to verify the outputs.
For shared pages, TDX Module's accessing is transient in one SEAMCALL,
so only as input, TDX Module never needs to output the array.
I think the verboseness makes the following pseudo code easier to
understand.
> For that latter part I think you are
> trying to say sometimes they need flushing and sometimes they don't?
Yeah.
control pages => long term => host verifies and releases => flush on release
shared pages => transient => no verify and releases => no flush
Maybe I should mention the flushing is already covered by releasing
kAPI.
>
> >
> > The 2 structures both need a 'root page' which contains a list of HPAs.
> > They collapse the HPA of the root page and the number of valid HPAs
> > into a 64 bit raw value for SEAMCALL parameters. The root page is
> > always a medium for passing data pages, TDX Module never keeps the
> > root page.
> >
> > A main difference is HPA_ARRAY_T requires singleton mode when
> > containing just 1 functional page (page0). In this mode the root page is
> > not needed and the HPA field of the raw value directly points to the
> > page0. But in this patch, root page is always allocated for user
> > friendly kAPIs.
>
> "singleton mode"? What is it? If it's the case of not needing populate loop, it
It is the SEAMCALL level detail for HPA_ARRAY_T. It is literally as
explained above - the HPA field should be filled by page0, not root page.
> probably deserves more explanation. I'm not sure, but the populate loop seems to
> drive a lot of the struct design?
The caller is not aware of singleton mode. Actually, I'm trying to make
the tdx_page_array independent of HPA_ARRAY_T or HPA_LIST_INFO details
when allocating/populating, root page is still populated even not needed
for singleton mode. The differences only happen when collaping the struct
into u64 SEAMCALL parameters.
>
> >
> > Another small difference is HPA_LIST_INFO contains a 'first entry' field
> > which could be filled by TDX Module. This simplifies host by providing
> > the same structure when re-invoke the interrupted SEAMCALL. No need for
> > host to touch this field.
>
> Not clear what this is talking about. But I'm starting to wonder if we should be
> so bold to claim that the differences between the types really simplify the
> host.
I'm talking about another SEAMCALL level detail. Sometimes TDX Module
got interrupted in the middle of page array processing, it needs an
anchor to resuming from where it stops, TDX Module record the anchor
in the 'first entry'.
By illustrating these SEAMCALL level differences, I want to explain
they don't impact the general SW flow and kAPI cares about them
internally.
Yes in POC code we do write dedicated code for each type, but it ends up
with plenty of similar logics on caller side about root page
manipulation. By now, the differences are not much, but I think we
should not write copies for every type, we should stop new types.
Please allow me to stop here, will continue later...
Thanks.
^ permalink raw reply
* Re: [PATCH v13 30/48] KVM: arm64: Handle Realm PSCI requests
From: Suzuki K Poulose @ 2026-03-30 10:36 UTC (permalink / raw)
To: Steven Price, kvm, kvmarm
Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
Oliver Upton, Zenghui Yu, linux-arm-kernel, linux-kernel,
Joey Gouly, Alexandru Elisei, Christoffer Dall, Fuad Tabba,
linux-coco, Ganapatrao Kulkarni, Gavin Shan, Shanker Donthineni,
Alper Gun, Aneesh Kumar K . V, Emi Kisanuki, Vishal Annapurve
In-Reply-To: <20260318155413.793430-31-steven.price@arm.com>
On 18/03/2026 15:53, Steven Price wrote:
> The RMM needs to be informed of the target REC when a PSCI call is made
> with an MPIDR argument. Expose an ioctl to the userspace in case the PSCI
> is handled by it.
>
> [NOTE: A future version of the RMM specification is likely to remove the
> need for this ioctl.]
This will need to stay for the PSCI_CPU_ON case, where the host has to
acknowledge the onlining of a vCPU.
>
> Co-developed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> Signed-off-by: Steven Price <steven.price@arm.com>
For the record, we can drop the UAPI following our discussions and
implicitly do the PSCI complete before REC_ENTER, similar to what
we do for the SET_RIPAS request. The VMM/KVM can treat the case
as a normal PSCI_CPU_ON request and return the result as in normal
VMs.
The target REC may ENTER before we complete the reporting, but we can
handle this error case (RMI_ERROR_REC) and return -EAGAIN to the
userspace.
Suzuki
> ---
> Changes since v12:
> * Chance return code for non-realms to -ENXIO to better represent that
> the ioctl is invalid for non-realms (checkpatch is insistent that
> "ENOSYS means 'invalid syscall nr' and nothing else").
> Changes since v11:
> * RMM->RMI renaming.
> Changes since v6:
> * Use vcpu_is_rec() rather than kvm_is_realm(vcpu->kvm).
> * Minor renaming/formatting fixes.
> ---
> arch/arm64/include/asm/kvm_rmi.h | 3 +++
> arch/arm64/kvm/arm.c | 25 +++++++++++++++++++++++++
> arch/arm64/kvm/psci.c | 30 ++++++++++++++++++++++++++++++
> arch/arm64/kvm/rmi.c | 14 ++++++++++++++
> 4 files changed, 72 insertions(+)
>
> diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
> index 38208be3c602..1ee5ed0f5ab2 100644
> --- a/arch/arm64/include/asm/kvm_rmi.h
> +++ b/arch/arm64/include/asm/kvm_rmi.h
> @@ -117,6 +117,9 @@ int realm_map_non_secure(struct realm *realm,
> unsigned long size,
> enum kvm_pgtable_prot prot,
> struct kvm_mmu_memory_cache *memcache);
> +int realm_psci_complete(struct kvm_vcpu *source,
> + struct kvm_vcpu *target,
> + unsigned long status);
>
> static inline bool kvm_realm_is_private_address(struct realm *realm,
> unsigned long addr)
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 304fb1f2b3ff..61182eb0cf70 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -1846,6 +1846,22 @@ static int kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
> return __kvm_arm_vcpu_set_events(vcpu, events);
> }
>
> +static int kvm_arm_vcpu_rmi_psci_complete(struct kvm_vcpu *vcpu,
> + struct kvm_arm_rmi_psci_complete *arg)
> +{
> + struct kvm_vcpu *target = kvm_mpidr_to_vcpu(vcpu->kvm, arg->target_mpidr);
> +
> + if (!target)
> + return -EINVAL;
> +
> + /*
> + * RMM v1.0 only supports PSCI_RET_SUCCESS or PSCI_RET_DENIED
> + * for the status. But, let us leave it to the RMM to filter
> + * for making this future proof.
> + */
> + return realm_psci_complete(vcpu, target, arg->psci_status);
> +}
> +
> long kvm_arch_vcpu_ioctl(struct file *filp,
> unsigned int ioctl, unsigned long arg)
> {
> @@ -1974,6 +1990,15 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
>
> return kvm_arm_vcpu_finalize(vcpu, what);
> }
> + case KVM_ARM_VCPU_RMI_PSCI_COMPLETE: {
> + struct kvm_arm_rmi_psci_complete req;
> +
> + if (!vcpu_is_rec(vcpu))
> + return -ENXIO;
> + if (copy_from_user(&req, argp, sizeof(req)))
> + return -EFAULT;
> + return kvm_arm_vcpu_rmi_psci_complete(vcpu, &req);
> + }
> default:
> r = -EINVAL;
> }
> diff --git a/arch/arm64/kvm/psci.c b/arch/arm64/kvm/psci.c
> index 3b5dbe9a0a0e..a68f3c1878a5 100644
> --- a/arch/arm64/kvm/psci.c
> +++ b/arch/arm64/kvm/psci.c
> @@ -103,6 +103,12 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
>
> reset_state->reset = true;
> kvm_make_request(KVM_REQ_VCPU_RESET, vcpu);
> + /*
> + * Make sure we issue PSCI_COMPLETE before the VCPU can be
> + * scheduled.
> + */
> + if (vcpu_is_rec(vcpu))
> + realm_psci_complete(source_vcpu, vcpu, PSCI_RET_SUCCESS);
>
> /*
> * Make sure the reset request is observed if the RUNNABLE mp_state is
> @@ -115,6 +121,11 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu)
>
> out_unlock:
> spin_unlock(&vcpu->arch.mp_state_lock);
> + if (vcpu_is_rec(vcpu) && ret != PSCI_RET_SUCCESS) {
> + realm_psci_complete(source_vcpu, vcpu,
> + ret == PSCI_RET_ALREADY_ON ?
> + PSCI_RET_SUCCESS : PSCI_RET_DENIED);
> + }
> return ret;
> }
>
> @@ -142,6 +153,25 @@ static unsigned long kvm_psci_vcpu_affinity_info(struct kvm_vcpu *vcpu)
> /* Ignore other bits of target affinity */
> target_affinity &= target_affinity_mask;
>
> + if (vcpu_is_rec(vcpu)) {
> + struct kvm_vcpu *target_vcpu;
> +
> + /* RMM supports only zero affinity level */
> + if (lowest_affinity_level != 0)
> + return PSCI_RET_INVALID_PARAMS;
> +
> + target_vcpu = kvm_mpidr_to_vcpu(kvm, target_affinity);
> + if (!target_vcpu)
> + return PSCI_RET_INVALID_PARAMS;
> +
> + /*
> + * Provide the references of the source and target RECs to the
> + * RMM so that the RMM can complete the PSCI request.
> + */
> + realm_psci_complete(vcpu, target_vcpu, PSCI_RET_SUCCESS);
> + return PSCI_RET_SUCCESS;
> + }
> +
> /*
> * If one or more VCPU matching target affinity are running
> * then ON else OFF
> diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
> index 30292814b1ec..e56c8af2ad61 100644
> --- a/arch/arm64/kvm/rmi.c
> +++ b/arch/arm64/kvm/rmi.c
> @@ -353,6 +353,20 @@ static void free_rtt(phys_addr_t phys)
> kvm_account_pgtable_pages(phys_to_virt(phys), -1);
> }
>
> +int realm_psci_complete(struct kvm_vcpu *source, struct kvm_vcpu *target,
> + unsigned long status)
> +{
> + int ret;
> +
> + ret = rmi_psci_complete(virt_to_phys(source->arch.rec.rec_page),
> + virt_to_phys(target->arch.rec.rec_page),
> + status);
> + if (ret)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> static int realm_rtt_create(struct realm *realm,
> unsigned long addr,
> int level,
^ permalink raw reply
* Re: [PATCH v2 02/31] x86/virt/tdx: Move bit definitions of TDX_FEATURES0 to public header
From: Xu Yilun @ 2026-03-30 8:07 UTC (permalink / raw)
To: Edgecombe, Rick P
Cc: Williams, Dan J, linux-pci@vger.kernel.org,
linux-coco@lists.linux.dev, x86@kernel.org, Gao, Chao, Xu, Yilun,
dave.hansen@linux.intel.com, kas@kernel.org,
baolu.lu@linux.intel.com, Jiang, Dave, Li, Xiaoyao,
Verma, Vishal L, Duan, Zhenzhong, kvm@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <4228c004ebd59f9737fb39c6c9815bf6e7711acc.camel@intel.com>
On Fri, Mar 27, 2026 at 11:45:39PM +0000, Edgecombe, Rick P wrote:
> On Sat, 2026-03-28 at 00:01 +0800, Xu Yilun wrote:
> > Move bit definitions of TDX_FEATURES0 to TDX core public header.
>
> Patch seems reasonable, but not sure about the "public header" language. Maybe
> more widely accessibly header? Or a better name?
>
Yes, I try to make it more explicit, is it better:
x86/virt/tdx: Move TDX_FEATURES0 bit defines to arch x86 header
Move TDX_FEATURES0 bit definitions to arch x86 header.
...
^ permalink raw reply
* Re: [PATCH v2 01/31] x86/tdx: Move all TDX error defines into <asm/shared/tdx_errno.h>
From: Xu Yilun @ 2026-03-30 7:10 UTC (permalink / raw)
To: Edgecombe, Rick P
Cc: Williams, Dan J, linux-pci@vger.kernel.org,
linux-coco@lists.linux.dev, x86@kernel.org, Gao, Chao, Xu, Yilun,
dave.hansen@linux.intel.com, kas@kernel.org,
baolu.lu@linux.intel.com, Jiang, Dave, Li, Xiaoyao,
Verma, Vishal L, Duan, Zhenzhong, kvm@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <a78f0ac35b137eed6306dd7ad8db37dd9d930aac.camel@intel.com>
> I think it is missing Kai's RB from sys disable v1, and your sign off.
Ah, yes. I picked from the latest post:
https://lore.kernel.org/all/20260323-fuller_tdx_kexec_support-v2-1-87a36409e051@intel.com/
But didn't realize the older thread has different RB set and b4 didn't
catch, will add it manually.
I'll also add my sign off.
Thanks
^ permalink raw reply
* Re: [PATCH v2 01/31] x86/tdx: Move all TDX error defines into <asm/shared/tdx_errno.h>
From: Xu Yilun @ 2026-03-30 7:07 UTC (permalink / raw)
To: Dan Williams
Cc: Edgecombe, Rick P, linux-pci@vger.kernel.org,
linux-coco@lists.linux.dev, x86@kernel.org, Gao, Chao, Xu, Yilun,
dave.hansen@linux.intel.com, kas@kernel.org,
baolu.lu@linux.intel.com, Jiang, Dave, Li, Xiaoyao,
Verma, Vishal L, Duan, Zhenzhong, kvm@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <69c72bdec3ce8_178904100bf@dwillia2-mobl4.notmuch>
> In this case, for the tsm.git#staging branch, I will replace this with a
> fresh application of this:
>
> https://lore.kernel.org/all/20260323-fuller_tdx_kexec_support-v2-1-87a36409e051@intel.com/
This is the exact patch I picked, but Kai's RB is in previous version
https://lore.kernel.org/all/20260307010358.819645-2-rick.p.edgecombe@intel.com/
and b4 didn't catch it. I think should add his RB manually.
>
> Yilun, going forward, if you borrow a patch from another set, be sure to
> both add your own signed-off-by, but also a:
>
> Link: https://patch.msgid.link/20260323-fuller_tdx_kexec_support-v2-1-87a36409e051@intel.com
>
> ...to make it extra clear you are including a patch that is already on
> the list in another set.
Yes. I listed the link in cover letter, but yes add it here should be
clearer and necessary.
>
> I suspect that by the time this set is ready to move from
> tsm.git#staging to tsm.git#next a stable commit-id may be available for
> a rebase.
^ permalink raw reply
* Re: [PATCH v2 09/19] PCI/TSM: Support creating encrypted MMIO descriptors via TDISP Report
From: Alexey Kardashevskiy @ 2026-03-30 5:47 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Xu Yilun, Aneesh Kumar K.V, Dan Williams, linux-coco, linux-pci,
gregkh, bhelgaas, alistair23, lukas, Arnd Bergmann
In-Reply-To: <20260327114921.GL67624@nvidia.com>
On 27/3/26 22:49, Jason Gunthorpe wrote:
> On Fri, Mar 27, 2026 at 10:38:15AM +1100, Alexey Kardashevskiy wrote:
>>
>>
>> On 24/3/26 05:20, Jason Gunthorpe wrote:
>>> On Mon, Mar 16, 2026 at 04:19:30PM +1100, Alexey Kardashevskiy wrote:
>>>
>>>> and btw this only works if the entity generating the MMIO reporting
>>>> offset (==TSM) knows about BARs sizes, which is not the case for AMD
>>>> - the FW has no access to the config space (so the HV needs to feed
>>>> this to the FW? may be). Thanks,
>>>
>>> Then your platform just shouldn't use the mmio offset feature. Set it
>>> to 0 always.
>>
>> pcie r7, Table 11-16 TDI Report Structure, MMIO_RANGE:
>>
>> "Each MMIO Range of the TDI is reported with the MMIO reporting offset added."
>>
>> My english struggles here - can the above be interpreted as "Each reported MMIO Range ..."?
>>
>> as if it is each (except msix), then I know where msix is and can
>> amend the report inside the VM if msix is not locked. Thanks,
>
> To do this you must be convert between the offset'd and phys_addr_t
> versions otherwise you have no idea where the translated ones fall
> within the BAR, so you can't figure out if msix is covered or not.
I know if MSIX is covered because I know (from the PSP) if it is locked so it must be reported, with specific MSIX/PBA flags. If it is not locked, then skipped in the report but I still know where it is. For other ranges, if the device is not skipping them randomly, then, with a preserved order (as PCIe mandates), it can be reconstructed.
PCIe:
===
MMIO ranges assigned via BAR(s) must be reported in ascending order starting with the lowest numbered BAR such that
the first range corresponds to the first BAR and so on. The range ID reports the BAR equivalent Indicator (BEI). Values 0-7
of the Range ID are reserved to indicate the BEI. The device must report the BAR equivalent Indicator (BEI) for ranges
associated with a PCIe BAR.
When reporting the MMIO range for a TDI, the MMIO ranges must be reported in the logical order in which the TDI MMIO
range is configured such that the first range reported corresponds to first range of pages in the TDI and so on.
===
What do I miss? Thanks,
--
Alexey
^ permalink raw reply
* Re: [PATCH v2 19/31] iommu/vt-d: Reserve the MSB domain ID bit for the TDX module
From: kernel test robot @ 2026-03-28 19:58 UTC (permalink / raw)
To: Xu Yilun, linux-coco, linux-pci, dan.j.williams, x86
Cc: oe-kbuild-all, chao.gao, dave.jiang, baolu.lu, yilun.xu, yilun.xu,
zhenzhong.duan, kvm, rick.p.edgecombe, dave.hansen, kas,
xiaoyao.li, vishal.l.verma, linux-kernel
In-Reply-To: <20260327160132.2946114-20-yilun.xu@linux.intel.com>
Hi Xu,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 11439c4635edd669ae435eec308f4ab8a0804808]
url: https://github.com/intel-lab-lkp/linux/commits/Xu-Yilun/x86-tdx-Move-all-TDX-error-defines-into-asm-shared-tdx_errno-h/20260328-151524
base: 11439c4635edd669ae435eec308f4ab8a0804808
patch link: https://lore.kernel.org/r/20260327160132.2946114-20-yilun.xu%40linux.intel.com
patch subject: [PATCH v2 19/31] iommu/vt-d: Reserve the MSB domain ID bit for the TDX module
config: x86_64-defconfig (https://download.01.org/0day-ci/archive/20260329/202603290317.BVIn0aoy-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260329/202603290317.BVIn0aoy-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603290317.BVIn0aoy-lkp@intel.com/
All warnings (new ones prefixed by >>, old ones prefixed by <<):
>> WARNING: modpost: vmlinux: section mismatch in reference: alloc_iommu.cold+0x49 (section: .text.unlikely) -> acpi_table_parse_keyp (section: .init.text)
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v2 19/31] iommu/vt-d: Reserve the MSB domain ID bit for the TDX module
From: kernel test robot @ 2026-03-28 16:57 UTC (permalink / raw)
To: Xu Yilun, linux-coco, linux-pci, dan.j.williams, x86
Cc: oe-kbuild-all, chao.gao, dave.jiang, baolu.lu, yilun.xu, yilun.xu,
zhenzhong.duan, kvm, rick.p.edgecombe, dave.hansen, kas,
xiaoyao.li, vishal.l.verma, linux-kernel
In-Reply-To: <20260327160132.2946114-20-yilun.xu@linux.intel.com>
Hi Xu,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 11439c4635edd669ae435eec308f4ab8a0804808]
url: https://github.com/intel-lab-lkp/linux/commits/Xu-Yilun/x86-tdx-Move-all-TDX-error-defines-into-asm-shared-tdx_errno-h/20260328-151524
base: 11439c4635edd669ae435eec308f4ab8a0804808
patch link: https://lore.kernel.org/r/20260327160132.2946114-20-yilun.xu%40linux.intel.com
patch subject: [PATCH v2 19/31] iommu/vt-d: Reserve the MSB domain ID bit for the TDX module
config: i386-randconfig-141-20260328 (https://download.01.org/0day-ci/archive/20260329/202603290006.za7iiDgF-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
smatch: v0.5.0-9004-gb810ac53
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260329/202603290006.za7iiDgF-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603290006.za7iiDgF-lkp@intel.com/
All warnings (new ones prefixed by >>, old ones prefixed by <<):
>> WARNING: modpost: vmlinux: section mismatch in reference: iommu_max_domain_id+0x55 (section: .text.iommu_max_domain_id) -> acpi_table_parse_keyp (section: .init.text)
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v2 03/31] x86/virt/tdx: Add tdx_page_array helpers for new TDX Module objects
From: Edgecombe, Rick P @ 2026-03-28 1:35 UTC (permalink / raw)
To: Williams, Dan J, linux-pci@vger.kernel.org,
linux-coco@lists.linux.dev, yilun.xu@linux.intel.com,
x86@kernel.org
Cc: Gao, Chao, Xu, Yilun, dave.hansen@linux.intel.com, kas@kernel.org,
baolu.lu@linux.intel.com, Jiang, Dave, Li, Xiaoyao,
Verma, Vishal L, Duan, Zhenzhong, kvm@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <20260327160132.2946114-4-yilun.xu@linux.intel.com>
Hi,
In general I'm struggling to understand the design decisions. It seems a very
specific design and quite a bit of code to manage an array of pages. Questions
below.
On Sat, 2026-03-28 at 00:01 +0800, Xu Yilun wrote:
> Add struct tdx_page_array definition for new TDX Module object
> types - HPA_ARRAY_T and HPA_LIST_INFO.
This is unfortunate. I see you agree in the comments.
>
> They are used as input/output
> parameters in newly defined SEAMCALLs. Also define some helpers to
> allocate, setup and free tdx_page_array.
>
> HPA_ARRAY_T and HPA_LIST_INFO are similar in most aspects. They both
> represent a list of pages for TDX Module accessing. There are several
> use cases for these 2 structures:
>
> - As SEAMCALL inputs. They are claimed by TDX Module as control pages.
> Control pages are private pages for TDX Module to hold its internal
> control structures or private data. TDR, TDCS, TDVPR... are existing
> control pages, just not added via tdx_page_array.
> - As SEAMCALL outputs. They were TDX Module control pages and now are
> released.
> - As SEAMCALL inputs. They are just temporary buffers for exchanging
> data blobs in one SEAMCALL. TDX Module will not hold them for long
> time.
This is kind of verbose for what it seems to be trying to say. It's just that
these types can be input or output params. The TDX module could hold on to the
pages for a long time, or just transiently. For that latter part I think you are
trying to say sometimes they need flushing and sometimes they don't?
>
> The 2 structures both need a 'root page' which contains a list of HPAs.
> They collapse the HPA of the root page and the number of valid HPAs
> into a 64 bit raw value for SEAMCALL parameters. The root page is
> always a medium for passing data pages, TDX Module never keeps the
> root page.
>
> A main difference is HPA_ARRAY_T requires singleton mode when
> containing just 1 functional page (page0). In this mode the root page is
> not needed and the HPA field of the raw value directly points to the
> page0. But in this patch, root page is always allocated for user
> friendly kAPIs.
"singleton mode"? What is it? If it's the case of not needing populate loop, it
probably deserves more explanation. I'm not sure, but the populate loop seems to
drive a lot of the struct design?
>
> Another small difference is HPA_LIST_INFO contains a 'first entry' field
> which could be filled by TDX Module. This simplifies host by providing
> the same structure when re-invoke the interrupted SEAMCALL. No need for
> host to touch this field.
Not clear what this is talking about. But I'm starting to wonder if we should be
so bold to claim that the differences between the types really simplify the
host.
>
> Typical usages of the tdx_page_array:
>
> 1. Add control pages:
> - struct tdx_page_array *array = tdx_page_array_create(nr_pages);
> - seamcall(TDH_XXX_CREATE, array, ...);
>
> 2. Release control pages:
> - seamcall(TDX_XXX_DELETE, array, &nr_released, &released_hpa);
> - tdx_page_array_ctrl_release(array, nr_released, released_hpa);
So release is mostly needed because of the need to do the wbvind seamcall? And
unlike tdx_page_array_free() it returns an error in case that fails. Or other
sanity checking. But all the callers do the same thing on error, call
tdx_page_array_ctrl_leak().
Just wondering if we could simplify it somehow. There are two helpers and the
caller has to know which one to call based on SEAMCALL specifics. What if the
seamcall wrapper set a bit in the page array while passing it out. The bit would
specify to the helper if it needs to do wbinvd or not. Then the wrappers could
encapsulate the type of free needed and not rely on the caller to know. And we
only need to have one function for it instead of two.
BTW, do we expect errors from the tdh_phymem_page_wbinvd_hkid() calls here? How
could the BUSY happen? If we don't think it can happen in normal runtime, we
could just warn and skip the special leak logic. In KVM side there is a place
where we can't really handle it for the wbinvd calls. And one where we can. If
we need a ton of code to handle a bug somewhere (on kernel side or TDX module),
it seems too defensive to me. At least it's not in sync with the rest of TDX.
Especially the quite large tdx_page_array_validate_release() logic should need a
justification that there is something very tricky that needs all this checking.
But maybe you can explain what the special risk is.
>
> 3. Exchange data blobs:
> - struct tdx_page_array *array = tdx_page_array_create(nr_pages);
> - seamcall(TDX_XXX, array, ...);
> - Read data from array.
>
>
> 4. Note the root page contains 512 HPAs at most, if more pages are
> required, re-populate the tdx_page_array is needed.
>
> - struct tdx_page_array *array = tdx_page_array_alloc(nr_pages);
> - for each 512-page bulk
> - tdx_page_array_populate(array, offset);
> - seamcall(TDH_XXX_ADD, array, ...);
>
> In case 2, SEAMCALLs output the released page array in the form of
> HPA_ARRAY_T or PAGE_LIST_INFO. Use tdx_page_array_ctrl_release() to
> check if the output pages match the original input pages. If failed,
> TDX Module is buggy. In this case the safer way is to leak the
> control pages, call tdx_page_array_ctrl_leak().
>
> The usage of tdx_page_array will be in following patches.
>
> Co-developed-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
> Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
> Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
> ---
> arch/x86/include/asm/tdx.h | 37 +++++
> arch/x86/virt/vmx/tdx/tdx.c | 299 ++++++++++++++++++++++++++++++++++++
> 2 files changed, 336 insertions(+)
>
> diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
> index 65c4da396450..9173a432b312 100644
> --- a/arch/x86/include/asm/tdx.h
> +++ b/arch/x86/include/asm/tdx.h
> @@ -139,6 +139,43 @@ void tdx_guest_keyid_free(unsigned int keyid);
>
> void tdx_quirk_reset_page(struct page *page);
>
> +/**
> + * struct tdx_page_array - Represents a list of pages for TDX Module access
> + * @nr_pages: Total number of data pages in the collection
> + * @pages: Array of data page pointers containing all the data
> + *
> + * @offset: Internal: The starting index in @pages, positions the currently
> + * populated page window in @root.
> + * @nents: Internal: Number of valid HPAs for the page window in @root
> + * @root: Internal: A single 4KB page holding the 8-byte HPAs of the page
> + * window. The page window max size is constrained by the root page,
> + * which is 512 HPAs.
> + *
> + * This structure abstracts several TDX Module defined object types, e.g.,
> + * HPA_ARRAY_T and HPA_LIST_INFO. Typically they all use a "root page" as the
> + * medium to exchange a list of data pages between host and TDX Module. This
> + * structure serves as a unified parameter type for SEAMCALL wrappers, where
> + * these hardware object types are needed.
> + */
> +struct tdx_page_array {
> + /* public: */
> + unsigned int nr_pages;
> + struct page **pages;
> +
> + /* private: */
> + unsigned int offset;
> + unsigned int nents;
> + u64 *root;
pages is going to be an array of struct pointers, and root is a single page of
PA's that gets re-used to copy and pass the PA's to the TDX module. Why do we
need both? Like just keep an array of PA's that would be the same size as the
struct page array. And not need the populate loop?
Pausing for now. Still looking through the callers and it's the end of the day.
^ permalink raw reply
* Re: [PATCH v2 01/31] x86/tdx: Move all TDX error defines into <asm/shared/tdx_errno.h>
From: Dan Williams @ 2026-03-28 1:16 UTC (permalink / raw)
To: Edgecombe, Rick P, Williams, Dan J, linux-pci@vger.kernel.org,
linux-coco@lists.linux.dev, yilun.xu@linux.intel.com,
x86@kernel.org
Cc: Gao, Chao, Xu, Yilun, dave.hansen@linux.intel.com, kas@kernel.org,
baolu.lu@linux.intel.com, Jiang, Dave, Li, Xiaoyao,
Verma, Vishal L, Duan, Zhenzhong, kvm@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <a78f0ac35b137eed6306dd7ad8db37dd9d930aac.camel@intel.com>
Edgecombe, Rick P wrote:
> On Sat, 2026-03-28 at 00:01 +0800, Xu Yilun wrote:
> > From: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
> >
> > Today there are two separate locations where TDX error codes are defined:
> > arch/x86/include/asm/tdx.h
> > arch/x86/kvm/vmx/tdx_errno.h
> >
> > They have some overlap that is already defined similarly. Reduce the
> > duplication and prepare to introduce some helpers for these error codes in
> > the central place by unifying them. Join them at:
> > asm/shared/tdx_errno.h
> > ...and update the headers that contained the duplicated definitions to
> > include the new unified header.
> >
> > "asm/shared" is used for sharing TDX code between the early compressed
> > code and the normal kernel code. While the compressed code for the guest
> > doesn't use these error code header definitions today, it does make the
> > types of calls that return the values they define. So place the defines in
> > "shared" location so that it can, but leave such cleanups for future
> > changes.
> >
> > Also, adjust BITUL() -> _BITULL() to address 32 bit build errors after the
> > move.
> >
> > Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
> > [enhance log]
> > Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> > Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
> > Reviewed-by: Chao Gao <chao.gao@intel.com>
>
> I think it is missing Kai's RB from sys disable v1, and your sign off.
>
> This patch is in three series now, hence the long SOB chain. And I think it is
> also the only KVM touch point in the series. If sys disable gets merged ahead of
> time it can be dropped. But if there is any lag there we should see if Dave will
> just take it instead of trying to keep it in sync.
Thanks for the heads up.
I think if we are committed to the idea that some TDX related sets may
go through different upstreams and some of those sets have a small
handful of common infrastructure patches then there are a few options.
Either have someone keep an eye for these and publish stable-commits for
folks to share, accept that duplication collisions will happen and
rebase when they do, or accept that duplication collisions be ok with
that small bit of mess showing up in the history.
In this case, for the tsm.git#staging branch, I will replace this with a
fresh application of this:
https://lore.kernel.org/all/20260323-fuller_tdx_kexec_support-v2-1-87a36409e051@intel.com/
Yilun, going forward, if you borrow a patch from another set, be sure to
both add your own signed-off-by, but also a:
Link: https://patch.msgid.link/20260323-fuller_tdx_kexec_support-v2-1-87a36409e051@intel.com
...to make it extra clear you are including a patch that is already on
the list in another set.
I suspect that by the time this set is ready to move from
tsm.git#staging to tsm.git#next a stable commit-id may be available for
a rebase.
^ permalink raw reply
* Re: [PATCH v2 02/31] x86/virt/tdx: Move bit definitions of TDX_FEATURES0 to public header
From: Edgecombe, Rick P @ 2026-03-27 23:45 UTC (permalink / raw)
To: Williams, Dan J, linux-pci@vger.kernel.org,
linux-coco@lists.linux.dev, yilun.xu@linux.intel.com,
x86@kernel.org
Cc: Gao, Chao, Xu, Yilun, dave.hansen@linux.intel.com, kas@kernel.org,
baolu.lu@linux.intel.com, Jiang, Dave, Li, Xiaoyao,
Verma, Vishal L, Duan, Zhenzhong, kvm@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <20260327160132.2946114-3-yilun.xu@linux.intel.com>
On Sat, 2026-03-28 at 00:01 +0800, Xu Yilun wrote:
> Move bit definitions of TDX_FEATURES0 to TDX core public header.
Patch seems reasonable, but not sure about the "public header" language. Maybe
more widely accessibly header? Or a better name?
>
> Kernel users get TDX_FEATURES0 bitmap via tdx_get_sysinfo(). It is
> reasonable to also public the definitions of each bit. TDX Connect (a
> new TDX feature to enable Trusted I/O virtualization) will add new bits
> and check them in separate kernel modules.
>
> Take the opportunity to change its type to BIT_ULL since TDX_FEATURES0
> is explicitly defined as 64-bit in both TDX Module Specification and
> TDX core code.
>
> Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
> ---
^ permalink raw reply
* Re: [PATCH v2 01/31] x86/tdx: Move all TDX error defines into <asm/shared/tdx_errno.h>
From: Edgecombe, Rick P @ 2026-03-27 23:37 UTC (permalink / raw)
To: Williams, Dan J, linux-pci@vger.kernel.org,
linux-coco@lists.linux.dev, yilun.xu@linux.intel.com,
x86@kernel.org
Cc: Gao, Chao, Xu, Yilun, dave.hansen@linux.intel.com, kas@kernel.org,
baolu.lu@linux.intel.com, Jiang, Dave, Li, Xiaoyao,
Verma, Vishal L, Duan, Zhenzhong, kvm@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <20260327160132.2946114-2-yilun.xu@linux.intel.com>
On Sat, 2026-03-28 at 00:01 +0800, Xu Yilun wrote:
> From: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
>
> Today there are two separate locations where TDX error codes are defined:
> arch/x86/include/asm/tdx.h
> arch/x86/kvm/vmx/tdx_errno.h
>
> They have some overlap that is already defined similarly. Reduce the
> duplication and prepare to introduce some helpers for these error codes in
> the central place by unifying them. Join them at:
> asm/shared/tdx_errno.h
> ...and update the headers that contained the duplicated definitions to
> include the new unified header.
>
> "asm/shared" is used for sharing TDX code between the early compressed
> code and the normal kernel code. While the compressed code for the guest
> doesn't use these error code header definitions today, it does make the
> types of calls that return the values they define. So place the defines in
> "shared" location so that it can, but leave such cleanups for future
> changes.
>
> Also, adjust BITUL() -> _BITULL() to address 32 bit build errors after the
> move.
>
> Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
> [enhance log]
> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
> Reviewed-by: Chao Gao <chao.gao@intel.com>
I think it is missing Kai's RB from sys disable v1, and your sign off.
This patch is in three series now, hence the long SOB chain. And I think it is
also the only KVM touch point in the series. If sys disable gets merged ahead of
time it can be dropped. But if there is any lag there we should see if Dave will
just take it instead of trying to keep it in sync.
^ permalink raw reply
* Re: [GIT PULL] Confidential Computing: Attestation fixes for 7.0-rc6
From: pr-tracker-bot @ 2026-03-27 23:32 UTC (permalink / raw)
To: Dan Williams
Cc: Linus Torvalds, linux-coco, Zubin Mithra, Kiryl Shutsemau,
Kuppuswamy Sathyanarayanan, Dave Hansen, Xiaoyao Li, Mikko Ylinen,
linux-kernel
In-Reply-To: <69c6eea861b3a_1b0cc610021@dwillia2-mobl4.notmuch>
The pull request you sent on Fri, 27 Mar 2026 13:55:04 -0700:
> git://git.kernel.org/pub/scm/linux/kernel/git/devsec/tsm tags/tsm-fixes-7.0-rc6
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/dd09eb443372f9390d36051d86ebe06e9919aeec
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
* Re: [PATCH v2 00/16] fs,x86/resctrl: Add kernel-mode (e.g., PLZA) support to the resctrl subsystem
From: Reinette Chatre @ 2026-03-27 22:11 UTC (permalink / raw)
To: Babu Moger, corbet, tony.luck, Dave.Martin, james.morse, tglx,
mingo, bp, dave.hansen
Cc: skhan, x86, hpa, peterz, juri.lelli, vincent.guittot,
dietmar.eggemann, rostedt, bsegall, mgorman, vschneid, kas,
rick.p.edgecombe, akpm, pmladek, rdunlap, dapeng1.mi, kees, elver,
paulmck, lirongqing, safinaskar, fvdl, seanjc, pawan.kumar.gupta,
xin, tiala, Neeraj.Upadhyay, chang.seok.bae, thomas.lendacky,
elena.reshetova, linux-doc, linux-kernel, linux-coco, kvm,
eranian, peternewman
In-Reply-To: <47c0db32-d0e0-4c53-90bd-b74863d233dc@amd.com>
Hi Babu,
On 3/26/26 10:12 AM, Babu Moger wrote:
> Hi Reinette,
>
> Thanks for the review comments. Will address one by one.
>
> On 3/24/26 17:51, Reinette Chatre wrote:
>> Hi Babu,
>>
>> On 3/12/26 1:36 PM, Babu Moger wrote:
>>> This series adds support for Privilege-Level Zero Association (PLZA) to the
>>> resctrl subsystem. PLZA is an AMD feature that allows specifying a CLOSID
>>> and/or RMID for execution in kernel mode (privilege level zero), so that
>>> kernel work is not subject to the same resource constrains as the current
>>> user-space task. This avoids kernel operations being aggressively throttled
>>> when a task's memory bandwidth is heavily limited.
>>>
>>> The feature documentation is not yet publicly available, but it is expected
>>> to be released in the next few weeks. In the meantime, a brief description
>>> of the features is provided below.
>>>
>>> Privilege Level Zero Association (PLZA)
>>>
>>> Privilege Level Zero Association (PLZA) allows the hardware to
>>> automatically associate execution in Privilege Level Zero (CPL=0) with a
>>> specific COS (Class of Service) and/or RMID (Resource Monitoring
>>> Identifier). The QoS feature set already has a mechanism to associate
>>> execution on each logical processor with an RMID or COS. PLZA allows the
>>> system to override this per-thread association for a thread that is
>>> executing with CPL=0.
>>> ------------------------------------------------------------------------
>>>
>>> The series introduces the feature in a way that supports the interface in
>>> a generic manner to accomodate MPAM or other vendor specific implimentation.
>>>
>>> Below is the detailed requirements provided by Reinette:
>>> https://lore.kernel.org/lkml/2ab556af-095b-422b-9396-f845c6fd0342@intel.com/
>> Our discussion considered how resctrl could support PLZA in a generic way while
>> also preparing to support MPAM's variants and how PLZA may evolve to have similar
>> capabilities when considering the capabilities of its registers.
>>
>> This does not mean that your work needs to implement everything that was discussed.
>> Instead, this work is expected to just support what PLZA is capable of today but
>> do so in a way that the future enhancements could be added to.
>>
>> This series is quite difficult to follow since it appears to implement a full
>> featured generic interface while PLZA cannot take advantage of it.
>>
>> Could you please simplify this work to focus on just enabling PLZA and only
>> add interfaces needed to do so?
> Sure. Will try. Lets continue the discussion.
>>
>>> Summary:
>>> 1. Kernel-mode/PLZA controls and status should be exposed under the resctrl
>>> info directory:/sys/fs/resctrl/info/, not as a separate or arch-specific path.
>>>
>>> 2. Add two info files
>>>
>>> a. kernel_mode
>>> Purpose: Control how resource allocation and monitoring apply in kernel mode
>>> (e.g. inherit from task vs global assign).
>>>
>>> Read: List supported modes and show current one (e.g. with [brackets]).
>>> Write: Set current mode by name (e.g. inherit_ctrl_and_mon, global_assign_ctrl_assign_mon).
>>>
>>> b. kernel_mode_assignment
>>>
>>> Purpose: When a “global assign” kernel mode is active, specify which resctrl group
>>> (CLOSID/RMID) is used for kernel work.
>>>
>>> Read: Show the assigned group in a path-like form (e.g. //, ctrl1//, ctrl1/mon1/).
>>> Write: Assign or clear the group used for kernel mode (and optionally clear with an empty write).
>>>
>>> The patches are based on top of commit (v7.0.0-rc3)
>>> 839e91ce3f41b (tip/master) Merge branch into tip/master: 'x86/tdx'
>>> ------------------------------------------------------------------------
>>>
>>> Examples: kernel_mode and kernel_mode_assignment
>>>
>>> All paths below are under /sys/fs/resctrl/ (e.g. info/kernel_mode means
>>> /sys/fs/resctrl/info/kernel_mode). Resctrl must be mounted and the platform
>>> must support the relevant modes (e.g. AMD with PLZA).
>>>
>>> 1) kernel_mode — show and set the current kernel mode
>>>
>>> Read supported modes and which one is active (current in brackets):
>>>
>>> $ cat info/kernel_mode
>>> [inherit_ctrl_and_mon]
>>> global_assign_ctrl_inherit_mon
>>> global_assign_ctrl_assign_mon
>>>
>>> Set the active mode (e.g. use one CLOSID+RMID for all kernel work):
>>>
>>> $ echo "global_assign_ctrl_assign_mon" > info/kernel_mode
>>> $ cat info/kernel_mode
>>> inherit_ctrl_and_mon
>>> global_assign_ctrl_inherit_mon
>>> [global_assign_ctrl_assign_mon]
>>>
>>> Mode meanings:
>>> - inherit_ctrl_and_mon: kernel uses same CLOSID/RMID as the current task (default).
>>> - global_assign_ctrl_inherit_mon: one CLOSID for all kernel work; RMID inherited from user.
>>> - global_assign_ctrl_assign_mon: one resource group (CLOSID+RMID) for all kernel work.
>>>
>>> 2) kernel_mode_assignment — show and set which group is used for kernel work
>>>
>>> Only relevant when kernel_mode is not "inherit_ctrl_and_mon". Read the
>> To help with future usages please connect visibility of this file with the mode in
>> info/kernel_mode. This helps us to support future modes with other resctrl files, possible
>> within each resource group.
>> Specifically, kernel_mode_assignment is not visible to user space if mode is "inherit_ctrl_and_mon",
>> while it is visible when mode is global_assign_ctrl_inherit_mon or global_assign_ctrl_assign_mon.
>
> Sure. Will do.
>
>>
>>> currently assigned group (path format is "CTRL_MON/MON/"):
>> The format depends on the mode, right? If the mode is "global_assign_ctrl_inherit_mon"
>> then it should only contain a control group, alternatively, if the mode is
>> "global_assign_ctrl_assign_mon" then it contains control and mon group. This gives
>> resctrl future flexibility to change format for future modes.
>
> This can be done both ways. Whole purpose of these groups is to get CLOSID and RMID to enable PLZA. User can echo CTRL_MON or MON group to kernel_mode_assignment in any of the modes. We can decide what needs to be updated in MSR (PQR_PLZA_ASSOC) based on what kernel mode is selected.
The "both ways" are specific to one of the two active modes though.
PLZA only needs the RMID when the mode is "global_assign_ctrl_assign_mon".
Displaying and parsing monitor group when the mode is
"global_assign_ctrl_inherit_mon" creates an inconsistent interface since the mode
only uses a control group. The interface to user space should match the mode otherwise
it becomes confusing.
...
>>>
>>> Tony suggested using global variables to store the kernel mode
>>> CLOSID and RMID. However, the kernel mode CLOSID and RMID are
>>> coming from rdtgroup structure with the new interface. Accessing
>>> them requires holding the associated lock, which would make the
>>> context switch path unnecessarily expensive. So, dropped the idea.
>>> https://lore.kernel.org/lkml/aXuxVSbk1GR2ttzF@agluck-desk3/
>>> Let me know if there are other ways to optimize this.
>> I do not see why the context switch path needs to be touched at all with this
>> implementation. Since PLZA only supports global assignment does it not mean that resctrl
>> only needs to update PQR_PLZA_ASSOC when user writes to info/kernel_mode and
>> info/kernel_mode_assignment?
>
> Each thread has an MSR to configure whether to associate privilege level zero execution with a separate COS and/or RMID, and the value of the COS and/or RMID. PLZA may be enabled or disabled on a per-thread basis. However, the COS and RMID association and configuration must be the same for all threads in the QOS Domain.
Based on previous comment in https://lore.kernel.org/lkml/abb049fa-3a3d-4601-9ae3-61eeb7fd8fcf@amd.com/
and this implementation all fields of PQR_PLZA_ASSOC except PQR_PLZA_ASSOC.plza_en must be the
same for all CPUs on the system, not just per QoS domain. Could you please confirm?
>
> So, PQR_PLZA_ASSOC is a per thread MSR just like PQR_ASSOC.
>
> Privilege-Level Zero Association (PLZA) allows the user to specify a COS and/or RMID associated with execution in Privilege-Level Zero. When enabled on a HW thread, when that thread enters Privilige-Level Zero, transactions associated with that thread will be associated with the PLZA COS and/or RMID. Otherwise, the HW thread will be associated with the COS and RMID identified by PQR_ASSOC.
>
> More below.
>
>>
>> Consider some of the scenarios:
>>
>> resctrl mount with default state:
>>
>> # cat info/kernel_mode
>> [inherit_ctrl_and_mon]
>> global_assign_ctrl_inherit_mon
>> global_assign_ctrl_assign_mon
>> # ls info/kernel_mode_assignment
>> ls: cannot access 'info/kernel_mode_assignment': No such file or directory
>>
>> enable global_assign_ctrl_assign_mon mode:
>> # echo "global_assign_ctrl_assign_mon" > info/kernel_mode
>>
>> Expectation here is that when user space sets this mode as above then resctrl would
>> in turn program MSR_IA32_PQR_PLZA_ASSOC on all CPUs to be:
>> MSR_IA32_PQR_PLZA_ASSOC.rmid=0
>> MSR_IA32_PQR_PLZA_ASSOC.rmid_en=1
>> MSR_IA32_PQR_PLZA_ASSOC.closid=0
>> MSR_IA32_PQR_PLZA_ASSOC.closid_en=1
>> MSR_IA32_PQR_PLZA_ASSOC.plza_en=1
>>
>> I do not see why it is necessary to maintain any per-CPU or per-task state or needing
>> to touch the context switch code. Since PLZA only supports global could it not
>> just set MSR_IA32_PQR_PLZA_ASSOC on all online CPUs and be done with it?
>> Only caveat is that if a CPU is offline then this setting needs to be stashed
>> so that MSR_IA32_PQR_PLZA_ASSOC can be set when new CPU comes online.
>>
>> The way that rdtgroup_config_kmode() introduced in patch #11 assumes it is dealing
>> with RDT_RESOURCE_L3 and traverses the resource domain list and resource group
>> CPU mask seems unnecessary to me as well as error prone since the system may only
>> have, for example, RDT_RESOURCE_MBA enabled or even just monitoring. Why not just set
>> MSR_IA32_PQR_PLZA_ASSOC on all CPUs and be done?
>>
>> To continue the scenarios ...
>>
>> After user's setting above related files read:
>> # cat info/kernel_mode
>> inherit_ctrl_and_mon
>> global_assign_ctrl_inherit_mon
>> [global_assign_ctrl_assign_mon]
>> # cat info/kernel_mode_assignment
>> //
>>
>> Modify group used by global_assign_ctrl_assign_mon mode:
>> # echo 'ctrl1/mon1/' > info/kernel_mode_assignment
>>
>> Expectation here is that when user space sets this then resctrl would
>> program MSR_IA32_PQR_PLZA_ASSOC on all CPUs to be:
>> MSR_IA32_PQR_PLZA_ASSOC.rmid=<rmid of mon1>
>> MSR_IA32_PQR_PLZA_ASSOC.rmid_en=1
>> MSR_IA32_PQR_PLZA_ASSOC.closid=<closid of ctrl1>
>> MSR_IA32_PQR_PLZA_ASSOC.closid_en=1
>> MSR_IA32_PQR_PLZA_ASSOC.plza_en=1
>
>
> This works correctly when PLZA associations are defined by per CPU. For example, lets assume that *ctrl1* is assigned *CLOSID 1*.
>
> In this scenario, every task in the system running on a any CPU will use the limits associated with *CLOSID 1* whenever it enters Privilege-Level Zero, because the CPU's *PQR_PLZA_ASSOC* register has PLZA enabled and CLOSID is 1.
>
> Now consider task-based association:
>
> We have two resctrl groups:
>
> * *ctrl1 -> CLOSID 1 -> task1.plza = 1 : *User wants PLZA be enabled
> for this task.
> * *ctrl2 -> CLOSID 2 -> task2.plza = 0 : *User wants PLZA
> disabled for this task.
>
> Suppose *task1* is first scheduled on *CPU 0*. This behaves as expected: since CPU 0 's *PQR_PLZA_ASSOC* contains *CLOSID 1, plza_en =1*, task1 will use the limits from CLOSID 1 when it enters Privilege-Level Zero.
>
> However, if *task2* later runs on *CPU 0*, we expect it to use *CLOSID 2* in both user mode and kernel mode, because user has PLZA disabled for this task. But CPU 0 still has *CLOSID 1, **plza_en =1* in its PQR_PLZA_ASSOC register.
>
> As a result, task2 will incorrectly run with *CLOSID 1* when entering Privilege-Level Zero something we explicitly want to avoid.
>
> At that point, PLZA must be disabled on CPU 0 to prevent the unintended association. Hope this explanation makes the issue clear.
>
A couple of points:
- Looks like we still need to come to agreement what is meant by "global" when it
comes to kernel mode.
In your description there is a "global" configuration, but the assignment is "per-task".
To me this sounds like a new and distinct kernel_mode from the "global" modes
considered so far. This seems to move to the "per_task" mode mentioned in but
the implementation does not take into account any of the earlier discussions
surrounding it:
https://lore.kernel.org/lkml/2ab556af-095b-422b-9396-f845c6fd0342@intel.com/
We only learned about one use case in https://lore.kernel.org/lkml/CABPqkBSq=cgn-am4qorA_VN0vsbpbfDePSi7gubicpROB1=djw@mail.gmail.com/
As I understand this use case requires PLZA globally enabled for all tasks. Thus
I consider task assignment to be "global" when in the "global_*" kernel modes.
If this is indeed a common use case then supporting only global configuration
but then requiring user space to manually assign all tasks afterwards sounds
cumbersome for user space and also detrimental to system performance with all
the churn to modify all the task_structs involved. The accompanying documentation
does not mention all this additional user space interactions required by user
space to use this implementation.
I find this implementation difficult and inefficient to use in the one use case
we know of. I would suggest that resctrl optimizes for the one known use case.
- This implementation ignores discussion on how existing resctrl files should
not be repurposed.
This implementation allows user space to set a resource group in
kernel_mode_assignment with the consequence that this resource group's
"tasks" file changes behavior. I consider this a break of resctrl interface.
We did briefly consider per-task configuration/assignment in previous discussion
and the proposal was for it to use a new file (only when and if needed!).
- Now a user is required to write the task id of every task that participates
in PLZA. Apart from the churn already mentioned this also breaks existing
usage since it is no longer possible for new tasks to be added to this
resource group. This creates an awkward interface where all tasks belonging
to a resource group inherits the allocations/monitoring for their user space
work and will get PLZA enabled whether user requested it or not while
tasks from other resource groups need to be explicitly enabled. This creates
an inconsistency when it comes to task assignment. The only way to "remove"
PLZA from such a task would be to assign it to another resource group which
may not have the user space allocations ... and once this is done the task
cannot be moved back.
There is no requirement that CLOSID/RMID should be dedicated to kernel work
but this implementation does so in an inconsistent way.
- Apart from the same issues as with repurposing of tasks file, why should same
CPU allocation be used for kernel and user space?
Reinette
^ permalink raw reply
* [GIT PULL] Confidential Computing: Attestation fixes for 7.0-rc6
From: Dan Williams @ 2026-03-27 20:55 UTC (permalink / raw)
To: Linus Torvalds
Cc: linux-coco, Zubin Mithra, Kiryl Shutsemau,
Kuppuswamy Sathyanarayanan, Dave Hansen, Xiaoyao Li, Mikko Ylinen,
linux-kernel
Hi Linus, please pull from:
git://git.kernel.org/pub/scm/linux/kernel/git/devsec/tsm tags/tsm-fixes-7.0-rc6
...to receive a fix for the tdx-guest driver. It has appeared in linux-next and
collides with a fix coming from tip/x86/tdx (resolution below). Going forward
all tdx-guest updates should move to the tip/x86/tdx topic, and tsm.git can
remain focused on core attestation report infrastructure.
My conflict resolution matches linux-next's:
diff --cc drivers/virt/coco/tdx-guest/tdx-guest.c
index 23ef3991c4d5,7cee97559ba2..a9ecc46df187
--- a/drivers/virt/coco/tdx-guest/tdx-guest.c
+++ b/drivers/virt/coco/tdx-guest/tdx-guest.c
@@@ -306,12 -309,12 +309,17 @@@ static int tdx_report_new_locked(struc
return ret;
}
+ if (quote_buf->status != GET_QUOTE_SUCCESS) {
+ pr_debug("GetQuote request failed, status:%llx\n", quote_buf->status);
+ return -EIO;
+ }
+
- buf = kvmemdup(quote_buf->data, quote_buf->out_len, GFP_KERNEL);
+ out_len = READ_ONCE(quote_buf->out_len);
+
+ if (out_len > TDX_QUOTE_MAX_LEN)
+ return -EFBIG;
+
+ buf = kvmemdup(quote_buf->data, out_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
---
The following changes since commit f338e77383789c0cae23ca3d48adcc5e9e137e3c:
Linux 7.0-rc4 (2026-03-15 13:52:05 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/devsec/tsm tags/tsm-fixes-7.0-rc6
for you to fetch changes up to c3fd16c3b98ed726294feab2f94f876290bf7b61:
virt: tdx-guest: Fix handling of host controlled 'quote' buffer length (2026-03-20 21:05:50 -0700)
----------------------------------------------------------------
tsm fixes for v7.0-rc6
- Fix a VMM controlled buffer length used to emit TDX attestation
reports.
----------------------------------------------------------------
Zubin Mithra (1):
virt: tdx-guest: Fix handling of host controlled 'quote' buffer length
drivers/virt/coco/tdx-guest/tdx-guest.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
^ permalink raw reply
* Re: [PATCH v5 0/2] dma-buf: heaps: system: add an option to allocate explicitly shared/decrypted memory
From: T.J. Mercier @ 2026-03-27 19:43 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Marek Szyprowski, Jiri Pirko, dri-devel, linaro-mm-sig, iommu,
linux-media, sumit.semwal, benjamin.gaignard, Brian.Starkey,
jstultz, christian.koenig, robin.murphy, leon, sean.anderson,
ptesarik, catalin.marinas, aneesh.kumar, suzuki.poulose,
steven.price, thomas.lendacky, john.allen, ashish.kalra,
suravee.suthikulpanit, linux-coco
In-Reply-To: <20260327121021.GB246076@ziepe.ca>
On Fri, Mar 27, 2026 at 5:10 AM Jason Gunthorpe <jgg@ziepe.ca> wrote:
>
> On Fri, Mar 27, 2026 at 10:38:10AM +0100, Marek Szyprowski wrote:
> > On 25.03.2026 20:23, Jiri Pirko wrote:
> > > From: Jiri Pirko <jiri@nvidia.com>
> > >
> > > Confidential computing (CoCo) VMs/guests, such as AMD SEV and Intel TDX,
> > > run with private/encrypted memory which creates a challenge
> > > for devices that do not support DMA to it (no TDISP support).
> > >
> > > For kernel-only DMA operations, swiotlb bounce buffering provides a
> > > transparent solution by copying data through shared memory.
> > > However, the only way to get this memory into userspace is via the DMA
> > > API's dma_alloc_pages()/dma_mmap_pages() type interfaces which limits
> > > the use of the memory to a single DMA device, and is incompatible with
> > > pin_user_pages().
> > >
> > > These limitations are particularly problematic for the RDMA subsystem
> > > which makes heavy use of pin_user_pages() and expects flexible memory
> > > usage between many different DMA devices.
> > >
> > > This patch series enables userspace to explicitly request shared
> > > (decrypted) memory allocations from new dma-buf system_cc_shared heap.
> > > Userspace can mmap this memory and pass the dma-buf fd to other
> > > existing importers such as RDMA or DRM devices to access the
> > > memory. The DMA API is improved to allow the dma heap exporter to DMA
> > > map the shared memory to each importing device.
> > >
> > > Based on dma-mapping-for-next e7442a68cd1ee797b585f045d348781e9c0dde0d
> >
> > I would like to merge this to dma-mapping-next, but I feel a bit
> > uncomfortable with my lack of knowledge about CoCo and friends. Could
> > those who know a bit more about it provide some Reviewed-by tags?
>
> I'm confident in the CC stuff, I was hoping to see someone from dmabuf
> heap land ack that the uAPI design is OK.. TJ?
>
> Jason
Hi, yes LGTM. From a uAPI perspective it's just another dma-buf heap.
^ permalink raw reply
* Re: [PATCH v5 2/2] dma-buf: heaps: system: add system_cc_shared heap for explicitly shared memory
From: T.J. Mercier @ 2026-03-27 19:43 UTC (permalink / raw)
To: Jiri Pirko
Cc: dri-devel, linaro-mm-sig, iommu, linux-media, sumit.semwal,
benjamin.gaignard, Brian.Starkey, jstultz, christian.koenig,
m.szyprowski, robin.murphy, jgg, leon, sean.anderson, ptesarik,
catalin.marinas, aneesh.kumar, suzuki.poulose, steven.price,
thomas.lendacky, john.allen, ashish.kalra, suravee.suthikulpanit,
linux-coco
In-Reply-To: <20260325192352.437608-3-jiri@resnulli.us>
On Wed, Mar 25, 2026 at 12:23 PM Jiri Pirko <jiri@resnulli.us> wrote:
>
> From: Jiri Pirko <jiri@nvidia.com>
>
> Add a new "system_cc_shared" dma-buf heap to allow userspace to
> allocate shared (decrypted) memory for confidential computing (CoCo)
> VMs.
>
> On CoCo VMs, guest memory is private by default. The hardware uses an
> encryption bit in page table entries (C-bit on AMD SEV, "shared" bit on
> Intel TDX) to control whether a given memory access is private or
> shared. The kernel's direct map is set up as private,
> so pages returned by alloc_pages() are private in the direct map
> by default. To make this memory usable for devices that do not support
> DMA to private memory (no TDISP support), it has to be explicitly
> shared. A couple of things are needed to properly handle
> shared memory for the dma-buf use case:
>
> - set_memory_decrypted() on the direct map after allocation:
> Besides clearing the encryption bit in the direct map PTEs, this
> also notifies the hypervisor about the page state change. On free,
> the inverse set_memory_encrypted() must be called before returning
> pages to the allocator. If re-encryption fails, pages
> are intentionally leaked to prevent shared memory from being
> reused as private.
>
> - pgprot_decrypted() for userspace and kernel virtual mappings:
> Any new mapping of the shared pages, be it to userspace via
> mmap or to kernel vmalloc space via vmap, creates PTEs independent
> of the direct map. These must also have the encryption bit cleared,
> otherwise accesses through them would see encrypted (garbage) data.
>
> - DMA_ATTR_CC_SHARED for DMA mapping:
> Since the pages are already shared, the DMA API needs to be
> informed via DMA_ATTR_CC_SHARED so it can map them correctly
> as unencrypted for device access.
>
> On non-CoCo VMs, the system_cc_shared heap is not registered
> to prevent misuse by userspace that does not understand
> the security implications of explicitly shared memory.
>
> Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Reviewed-by: T.J. Mercier <tjmercier@google.com>
^ permalink raw reply
* Re: [PATCH v2 5/7] KVM: guest_memfd: Add cleanup interface for guest teardown
From: Ackerley Tng @ 2026-03-27 17:16 UTC (permalink / raw)
To: Kalra, Ashish, tglx, mingo, bp, dave.hansen, x86, hpa, seanjc,
peterz, thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
Nathan.Fontenot, jackyli, pgonda, rientjes, jacobhxu, xin,
pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen, darwi,
linux-kernel, linux-crypto, kvm, linux-coco
In-Reply-To: <75cd28a5-fb51-47ae-97c7-191fe9a6e045@amd.com>
"Kalra, Ashish" <ashish.kalra@amd.com> writes:
> Hello Ackerley,
>
> On 3/11/2026 1:00 AM, Ackerley Tng wrote:
>> "Kalra, Ashish" <ashish.kalra@amd.com> writes:
>>
>>> Hello Ackerley,
>>>
>>> On 3/9/2026 4:01 AM, Ackerley Tng wrote:
>>>> Ashish Kalra <Ashish.Kalra@amd.com> writes:
>>>>
>>>>> From: Ashish Kalra <ashish.kalra@amd.com>
>>>>>
>>>>> Introduce kvm_arch_gmem_cleanup() to perform architecture-specific
>>>>> cleanups when the last file descriptor for the guest_memfd inode is
>>>>> closed. This typically occurs during guest shutdown and termination
>>>>> and allows for final resource release.
>>>>>
>>>>> Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
>>>>> ---
>>>>>
>>>>> [...snip...]
>>>>>
>>>>> diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
>>>>> index 017d84a7adf3..2724dd1099f2 100644
>>>>> --- a/virt/kvm/guest_memfd.c
>>>>> +++ b/virt/kvm/guest_memfd.c
>>>>> @@ -955,6 +955,14 @@ static void kvm_gmem_destroy_inode(struct inode *inode)
>>>>>
>>>>> static void kvm_gmem_free_inode(struct inode *inode)
>>>>> {
>>>>> +#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CLEANUP
>>>>> + /*
>>>>> + * Finalize cleanup for the inode once the last guest_memfd
>>>>> + * reference is released. This usually occurs after guest
>>>>> + * termination.
>>>>> + */
>>>>> + kvm_arch_gmem_cleanup();
>>>>> +#endif
>>>>
>>>> Folks have already talked about the performance implications of doing
>>>> the scan and rmpopt, I just want to call out that one VM could have more
>>>> than one associated guest_memfd too.
>>>
>>> Yes, i have observed that kvm_gmem_free_inode() gets invoked multiple times
>>> at SNP guest shutdown.
>>>
>>> And the same is true for kvm_gmem_destroy_inode() too.
>>>
>>>>
>>>> I think the cleanup function should be thought of as cleanup for the
>>>> inode (even if it doesn't take an inode pointer since it's not (yet)
>>>> required).
>>>>
>>>> So, the gmem cleanup function should not handle deduplicating cleanup
>>>> requests, but the arch function should, if the cleanup needs
>>>> deduplicating.
>>>
>>> I agree, the arch function will have to handle deduplicating, and for that
>>> the arch function will probably need to be passed the inode pointer,
>>> to have a parameter to assist with deduplicating.
>>>
>>
>> By the time .free_folio() is called, folio->mapping may no longer exist,
>> so if we definitely want to deduplicate using something in the inode,
>> .free_folio() won't be the right callback to use.
>
> Ok.
>
>>
>> I was thinking that deduplicating using something in the folio would be
>> better. Can rmpopt take a PFN range? Then there's really no
>> deduplication, the cleanup would be nicely narrowed to whatever was just
>> freed. Perhaps the PFNs could be aligned up to the nearest PMD or PUD
>> size for rmpopt to do the right thing.
>>
>
> It will really be ideal if the cleanup can be narrowed down to whatever was just freed.
>
> RMPOPT takes a SPA which is GB aligned, so if the PFNs are aligned to the nearest
> PUD, then RMPOPT will be perfectly aligned to optimize the 1G regions that contained
> memory associated with that guest being freed.
>
> This will also be the most optimal way to use RMPOPT, as we only optimize the 1G regions
> that contains memory associated with that guest, which should be much smaller than
> optimizing the whole 2TB RAM.
>
> And that's what the actual plans for RMPOPT are.
>
> We had planned for a phased RMPOPT implementation.
>
> In the first phase, we were planning to do RMP re-optimizations for entire 2TB
> RAM.
>
> Once 1GB hugetlb guest_memfd support is merged, we planned to support re-enabling
> RMPOPT optimizations during 1GB page cleanup as a follow-on series.
>
> But i believe this support is dependent on:
> 1). in-place conversion for guest_memfd,
> 2). 2M hugepage support for guest_memfd.
>
You're right about this dependency. Do you meant guest_memfd THP support
for "2M hugepage"?
> Another alternative we are considering is implementing a bitmap of 1GB regions in guest_memfd
> that tracks when they are being freed and then issue RMPOPT on those 1GB regions.
> (and this will be independent of the 1GB hugeTLB support for guest_memfd).
>
>> Or perhaps some more tracking is required to check that the entire
>> aligned range is freed before doing the rmpopt.
>>
>> I need to implement some of this tracking for guest_memfd HugeTLB
>> support, so if the tracking is useful for you, we should discuss!
>
> Yes, this tracking is going to be useful for RMPOPT.
>
> Is this going to be implemented as part of the 1GB hugeTLB support for guest_memfd ?
>
Yes, this is going to be implemented as part of the HugeTLB support
for guest_memfd. HugeTLB support for guest_memfd extends to any HugeTLB
page size the host supports, so not just 1G, 2M as well. :)
>>
>>>>
>>>> Also, .free_inode() is called through RCU, so it could be called after
>>>> some delay. Could it be possible that .free_inode() ends up being called
>>>> way after the associated VM gets torn down, or after KVM the module gets
>>>> unloaded? Does rmpopt still work fine if KVM the module got unloaded?
>>>
>>> Yes, .free_inode() can probably get called after the associated VM has
>>> been torn down and which should be fine for issuing RMPOPT to do
>>> RMP re-optimizations.
>>>
>>> As far as about KVM module getting unloaded, then as part of the forthcoming patch-series,
>>> during KVM module unload, X86_SNP_SHUTDOWN would be issued which means SNP would get
>>> disabled and therefore, RMP checks are also disabled.
>>>
>>> And as CC_ATTR_HOST_SEV_SNP would then be cleared, therefore, snp_perform_rmp_optimization()
>>> will simply return.
>>>
>>
>> I think relying on CC_ATTR_HOST_SEV_SNP to skip optimization should be
>> best as long as there are no races (like the .free_inode() will
>> definitely not try to optimize when SNP is half shut down or something
>> like that.
>
> Yeah, i will have to take a look at such races.
>
>>
>>> Another option is to add a new guest_memfd superblock operation, and then do the
>>> final guest_memfd cleanup using the .evict_inode() callback. This will then ensure
>>> that the cleanup is not called through RCU and avoids any kind of delays, as following:
>>>
>>> +static void kvm_gmem_evict_inode(struct inode *inode)
>>> +{
>>> +#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CLEANUP
>>> + kvm_arch_gmem_cleanup();
>>> +#endif
>>> + truncate_inode_pages_final(&inode->i_data);
>>> + clear_inode(inode);
>>> +}
>>> +
>>>
>>
>> At the point of .evict_inode(), CoCo-shared guest_memfd pages could
>> still be pinned (for DMA or whatever, accidentally or maliciously), can
>> rmpopt work on shared pages that might still be used for DMA?
>>
>
> Yes, RMPOPT should be safe to work here, as it checks the RMP table for assigned
> or private pages in the 1GB range specified. For a 1GB range full of shared pages,
> it will mark that range to be RMP optimized.
>
> If all RMPUPDATE's for all private->shared pages conversion have been completed at
> the point of .evict_inode(), then RMPOPT re-optimizations will work nicely.
>
Ah okay. The kvm_arch_gmem_invalidate() call in .free_folio is the part
that updates the RMP table to make anything private become shared.
So the RMPOPT probably needs to happen after the invalidate in .free_folio
The RMPOPT stuff is still useful even if the host never uses huge pages
for guest_memfd, right? If so, I think we still need a solution
regardless of when huge page support for guest_memfd lands.
What if we do it this way: in .free_folio, after doing the invalidate,
take the pfn of the folio being freed, align that to the GB containing
that pfn, then RMPOPT that? This way there is no dependency on the inode
being around.
RMPOPT looks up the shared/private-ness of the page in the RMP table
anyway so as long as the RMP table is updated, we should be good?
The awkward part is if RMPOPT is run twice when the RMP table state
hasn't changed. Is my understanding right that there will be no
correctness issues, just performance?
We can perhaps optimize (away or otherwise) unnecessary RMPOPTs later?
With this aligning-up-to-the-GB, at least we're not iterating the entire
host memory.
>> .invalidate_folio() and .free_folio() both actually happen on removal
>> from guest_memfd ownership, though both are not exactly when the folio
>> is completely not in use.
>>
>> Is the best time to optimize when the pages are truly freed?
>>
>
> Yes.
>
> Thanks,
> Ashish
>
Thank you!
>>> @@ -971,6 +979,7 @@ static const struct super_operations kvm_gmem_super_operations = {
>>> .alloc_inode = kvm_gmem_alloc_inode,
>>> .destroy_inode = kvm_gmem_destroy_inode,
>>> .free_inode = kvm_gmem_free_inode,
>>> + .evict_inode = kvm_gmem_evict_inode,
>>> };
>>>
>>>
>>> Thanks,
>>> Ashish
>>>
>>>>
>>>> IIUC the current kmem_cache_free(kvm_gmem_inode_cachep, GMEM_I(inode));
>>>> is fine because in kvm_gmem_exit(), there is a rcu_barrier() before
>>>> kmem_cache_destroy(kvm_gmem_inode_cachep);.
>>>>
>>>>> kmem_cache_free(kvm_gmem_inode_cachep, GMEM_I(inode));
>>>>> }
>>>>>
>>>>> --
>>>>> 2.43.0
^ permalink raw reply
* [PATCH v2 31/31] coco/tdx-host: Finally enable SPDM session and IDE Establishment
From: Xu Yilun @ 2026-03-27 16:01 UTC (permalink / raw)
To: linux-coco, linux-pci, dan.j.williams, x86
Cc: chao.gao, dave.jiang, baolu.lu, yilun.xu, yilun.xu,
zhenzhong.duan, kvm, rick.p.edgecombe, dave.hansen, kas,
xiaoyao.li, vishal.l.verma, linux-kernel
In-Reply-To: <20260327160132.2946114-1-yilun.xu@linux.intel.com>
The basic SPDM session and IDE functionalities are all implemented,
enable them.
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
---
drivers/virt/coco/tdx-host/tdx-host.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/virt/coco/tdx-host/tdx-host.c b/drivers/virt/coco/tdx-host/tdx-host.c
index 0f6056945788..7800afb0893d 100644
--- a/drivers/virt/coco/tdx-host/tdx-host.c
+++ b/drivers/virt/coco/tdx-host/tdx-host.c
@@ -883,7 +883,7 @@ static int tdx_iommu_enable_all(void)
return ret;
}
-static int __maybe_unused tdx_connect_init(struct device *dev)
+static int tdx_connect_init(struct device *dev)
{
struct tsm_dev *link;
int ret;
@@ -914,8 +914,7 @@ static int __maybe_unused tdx_connect_init(struct device *dev)
static int tdx_host_probe(struct faux_device *fdev)
{
- /* TODO: do tdx_connect_init() when it is fully implemented. */
- return 0;
+ return tdx_connect_init(&fdev->dev);
}
static struct faux_device_ops tdx_host_ops = {
--
2.25.1
^ permalink raw reply related
* [PATCH v2 30/31] coco/tdx-host: Implement IDE stream setup/teardown
From: Xu Yilun @ 2026-03-27 16:01 UTC (permalink / raw)
To: linux-coco, linux-pci, dan.j.williams, x86
Cc: chao.gao, dave.jiang, baolu.lu, yilun.xu, yilun.xu,
zhenzhong.duan, kvm, rick.p.edgecombe, dave.hansen, kas,
xiaoyao.li, vishal.l.verma, linux-kernel
In-Reply-To: <20260327160132.2946114-1-yilun.xu@linux.intel.com>
Implementation for a most straightforward Selective IDE stream setup.
Hard code all parameters for Stream Control Register. And no IDE Key
Refresh support.
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
---
include/linux/pci-ide.h | 2 +
drivers/pci/ide.c | 5 +-
drivers/virt/coco/tdx-host/tdx-host.c | 226 ++++++++++++++++++++++++++
3 files changed, 231 insertions(+), 2 deletions(-)
diff --git a/include/linux/pci-ide.h b/include/linux/pci-ide.h
index 381a1bf22a95..f0c6975fd429 100644
--- a/include/linux/pci-ide.h
+++ b/include/linux/pci-ide.h
@@ -106,6 +106,8 @@ struct pci_ide {
void pci_ide_set_nr_streams(struct pci_host_bridge *hb, u16 nr);
struct pci_ide_partner *pci_ide_to_settings(struct pci_dev *pdev,
struct pci_ide *ide);
+void pci_ide_stream_to_regs(struct pci_dev *pdev, struct pci_ide *ide,
+ struct pci_ide_regs *regs);
struct pci_ide *pci_ide_stream_alloc(struct pci_dev *pdev);
void pci_ide_stream_free(struct pci_ide *ide);
int pci_ide_stream_register(struct pci_ide *ide);
diff --git a/drivers/pci/ide.c b/drivers/pci/ide.c
index b35e8aba7ecb..1337608448c2 100644
--- a/drivers/pci/ide.c
+++ b/drivers/pci/ide.c
@@ -556,8 +556,8 @@ static void mem_assoc_to_regs(struct pci_bus_region *region,
* @ide: registered IDE settings descriptor
* @regs: output register values
*/
-static void pci_ide_stream_to_regs(struct pci_dev *pdev, struct pci_ide *ide,
- struct pci_ide_regs *regs)
+void pci_ide_stream_to_regs(struct pci_dev *pdev, struct pci_ide *ide,
+ struct pci_ide_regs *regs)
{
struct pci_ide_partner *settings = pci_ide_to_settings(pdev, ide);
int assoc_idx = 0;
@@ -586,6 +586,7 @@ static void pci_ide_stream_to_regs(struct pci_dev *pdev, struct pci_ide *ide,
regs->nr_addr = assoc_idx;
}
+EXPORT_SYMBOL_GPL(pci_ide_stream_to_regs);
/**
* pci_ide_stream_setup() - program settings to Selective IDE Stream registers
diff --git a/drivers/virt/coco/tdx-host/tdx-host.c b/drivers/virt/coco/tdx-host/tdx-host.c
index d5072a68b81a..0f6056945788 100644
--- a/drivers/virt/coco/tdx-host/tdx-host.c
+++ b/drivers/virt/coco/tdx-host/tdx-host.c
@@ -72,6 +72,10 @@ struct tdx_tsm_link {
struct tdx_page_array *spdm_mt;
unsigned int dev_info_size;
void *dev_info_data;
+
+ struct pci_ide *ide;
+ struct tdx_page_array *stream_mt;
+ unsigned int stream_id;
};
static struct tdx_tsm_link *to_tdx_tsm_link(struct pci_tsm *tsm)
@@ -351,6 +355,219 @@ static void tdx_spdm_session_teardown(struct tdx_tsm_link *tlink)
DEFINE_FREE(tdx_spdm_session_teardown, struct tdx_tsm_link *,
if (!IS_ERR_OR_NULL(_T)) tdx_spdm_session_teardown(_T))
+enum tdx_ide_stream_km_op {
+ TDX_IDE_STREAM_KM_SETUP = 0,
+ TDX_IDE_STREAM_KM_REFRESH = 1,
+ TDX_IDE_STREAM_KM_STOP = 2,
+};
+
+static int tdx_ide_stream_km(struct tdx_tsm_link *tlink,
+ enum tdx_ide_stream_km_op op)
+{
+ u64 r, out_msg_sz;
+ int ret;
+
+ do {
+ r = tdh_ide_stream_km(tlink->spdm_id, tlink->stream_id, op,
+ tlink->in_msg, tlink->out_msg,
+ &out_msg_sz);
+ ret = tdx_tsm_link_event_handler(tlink, r, out_msg_sz);
+ } while (ret == -EAGAIN);
+
+ return ret;
+}
+
+static struct tdx_tsm_link *
+tdx_ide_stream_key_program(struct tdx_tsm_link *tlink)
+{
+ int ret;
+
+ ret = tdx_ide_stream_km(tlink, TDX_IDE_STREAM_KM_SETUP);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return tlink;
+}
+
+static void tdx_ide_stream_key_stop(struct tdx_tsm_link *tlink)
+{
+ tdx_ide_stream_km(tlink, TDX_IDE_STREAM_KM_STOP);
+}
+
+DEFINE_FREE(tdx_ide_stream_key_stop, struct tdx_tsm_link *,
+ if (!IS_ERR_OR_NULL(_T)) tdx_ide_stream_key_stop(_T))
+
+static void sel_stream_block_regs(struct pci_dev *pdev, struct pci_ide *ide,
+ struct pci_ide_regs *regs)
+{
+ struct pci_dev *rp = pcie_find_root_port(pdev);
+ struct pci_ide_partner *setting = pci_ide_to_settings(rp, ide);
+
+ /* only support address association for prefetchable memory */
+ setting->mem_assoc = (struct pci_bus_region) { 0, -1 };
+ pci_ide_stream_to_regs(rp, ide, regs);
+}
+
+#define STREAM_INFO_RP_DEVFN GENMASK_ULL(7, 0)
+#define STREAM_INFO_TYPE BIT_ULL(8)
+#define STREAM_INFO_TYPE_LINK 0
+#define STREAM_INFO_TYPE_SEL 1
+
+static struct tdx_tsm_link *tdx_ide_stream_create(struct tdx_tsm_link *tlink,
+ struct pci_ide *ide)
+{
+ u64 stream_info, stream_ctrl;
+ u64 stream_id, rp_ide_id;
+ unsigned int nr_pages = tdx_sysinfo->connect.ide_mt_page_count;
+ struct pci_dev *pdev = tlink->pci.base_tsm.pdev;
+ struct pci_dev *rp = pcie_find_root_port(pdev);
+ struct pci_ide_regs regs;
+ u64 r;
+
+ struct tdx_page_array *stream_mt __free(tdx_page_array_free) =
+ tdx_page_array_create(nr_pages);
+ if (!stream_mt)
+ return ERR_PTR(-ENOMEM);
+
+ stream_info = FIELD_PREP(STREAM_INFO_RP_DEVFN, rp->devfn);
+ stream_info |= FIELD_PREP(STREAM_INFO_TYPE, STREAM_INFO_TYPE_SEL);
+
+ /*
+ * For Selective IDE stream, below values must be 0:
+ * NPR_AGG/PR_AGG/CPL_AGG/CONF_REQ/ALGO/DEFAULT/STREAM_ID
+ *
+ * below values are configurable but now hardcode to 0:
+ * PCRC/TC
+ */
+ stream_ctrl = FIELD_PREP(PCI_IDE_SEL_CTL_EN, 0) |
+ FIELD_PREP(PCI_IDE_SEL_CTL_TX_AGGR_NPR, 0) |
+ FIELD_PREP(PCI_IDE_SEL_CTL_TX_AGGR_PR, 0) |
+ FIELD_PREP(PCI_IDE_SEL_CTL_TX_AGGR_CPL, 0) |
+ FIELD_PREP(PCI_IDE_SEL_CTL_PCRC_EN, 0) |
+ FIELD_PREP(PCI_IDE_SEL_CTL_CFG_EN, 0) |
+ FIELD_PREP(PCI_IDE_SEL_CTL_ALG, 0) |
+ FIELD_PREP(PCI_IDE_SEL_CTL_TC, 0) |
+ FIELD_PREP(PCI_IDE_SEL_CTL_ID, 0);
+
+ sel_stream_block_regs(pdev, ide, ®s);
+ if (regs.nr_addr != 1)
+ return ERR_PTR(-EFAULT);
+
+ r = tdh_ide_stream_create(stream_info, tlink->spdm_id,
+ stream_mt, stream_ctrl,
+ regs.rid1, regs.rid2, regs.addr[0].assoc1,
+ regs.addr[0].assoc2, regs.addr[0].assoc3,
+ &stream_id, &rp_ide_id);
+ if (r)
+ return ERR_PTR(-EFAULT);
+
+ tlink->stream_id = stream_id;
+ tlink->stream_mt = no_free_ptr(stream_mt);
+
+ pci_dbg(pdev, "%s stream id 0x%x rp ide_id 0x%llx\n", __func__,
+ tlink->stream_id, rp_ide_id);
+ return tlink;
+}
+
+static void tdx_ide_stream_delete(struct tdx_tsm_link *tlink)
+{
+ struct pci_dev *pdev = tlink->pci.base_tsm.pdev;
+ unsigned int nr_released;
+ u64 released_hpa, r;
+
+ r = tdh_ide_stream_block(tlink->spdm_id, tlink->stream_id);
+ if (r) {
+ pci_err(pdev, "ide stream block fail 0x%llx\n", r);
+ goto leak;
+ }
+
+ r = tdh_ide_stream_delete(tlink->spdm_id, tlink->stream_id,
+ tlink->stream_mt, &nr_released,
+ &released_hpa);
+ if (r) {
+ pci_err(pdev, "ide stream delete fail 0x%llx\n", r);
+ goto leak;
+ }
+
+ if (tdx_page_array_ctrl_release(tlink->stream_mt, nr_released,
+ released_hpa)) {
+ pci_err(pdev, "fail to release IDE stream_mt pages\n");
+ goto leak;
+ }
+
+ return;
+
+leak:
+ tdx_page_array_ctrl_leak(tlink->stream_mt);
+}
+
+DEFINE_FREE(tdx_ide_stream_delete, struct tdx_tsm_link *,
+ if (!IS_ERR_OR_NULL(_T)) tdx_ide_stream_delete(_T))
+
+static struct tdx_tsm_link *tdx_ide_stream_setup(struct tdx_tsm_link *tlink)
+{
+ struct pci_dev *pdev = tlink->pci.base_tsm.pdev;
+ int ret;
+
+ struct pci_ide *ide __free(pci_ide_stream_release) =
+ pci_ide_stream_alloc(pdev);
+ if (!ide)
+ return ERR_PTR(-ENOMEM);
+
+ /* Configure IDE capability for RP & get stream_id */
+ struct tdx_tsm_link *tlink_create __free(tdx_ide_stream_delete) =
+ tdx_ide_stream_create(tlink, ide);
+ if (IS_ERR(tlink_create))
+ return tlink_create;
+
+ ide->stream_id = tlink->stream_id;
+ ret = pci_ide_stream_register(ide);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /*
+ * Configure IDE capability for target device
+ *
+ * Some test devices work only with DEFAULT_STREAM enabled. For
+ * simplicity, enable DEFAULT_STREAM for all devices. A future decent
+ * solution may be to have a quirk table to specify which devices need
+ * DEFAULT_STREAM.
+ */
+ ide->partner[PCI_IDE_EP].default_stream = 1;
+ pci_ide_stream_setup(pdev, ide);
+
+ /* Key Programming for RP & target device, enable IDE stream for RP */
+ struct tdx_tsm_link *tlink_program __free(tdx_ide_stream_key_stop) =
+ tdx_ide_stream_key_program(tlink);
+ if (IS_ERR(tlink_program))
+ return tlink_program;
+
+ ret = tsm_ide_stream_register(ide);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* Enable IDE stream for target device */
+ ret = pci_ide_stream_enable(pdev, ide);
+ if (ret)
+ return ERR_PTR(ret);
+
+ retain_and_null_ptr(tlink_create);
+ retain_and_null_ptr(tlink_program);
+ tlink->ide = no_free_ptr(ide);
+
+ return tlink;
+}
+
+static void tdx_ide_stream_teardown(struct tdx_tsm_link *tlink)
+{
+ tdx_ide_stream_key_stop(tlink);
+ tdx_ide_stream_delete(tlink);
+ pci_ide_stream_release(tlink->ide);
+}
+
+DEFINE_FREE(tdx_ide_stream_teardown, struct tdx_tsm_link *,
+ if (!IS_ERR_OR_NULL(_T)) tdx_ide_stream_teardown(_T))
+
static int tdx_tsm_link_connect(struct pci_dev *pdev)
{
struct tdx_tsm_link *tlink = to_tdx_tsm_link(pdev->tsm);
@@ -362,7 +579,15 @@ static int tdx_tsm_link_connect(struct pci_dev *pdev)
return PTR_ERR(tlink_spdm);
}
+ struct tdx_tsm_link *tlink_ide __free(tdx_ide_stream_teardown) =
+ tdx_ide_stream_setup(tlink);
+ if (IS_ERR(tlink_ide)) {
+ pci_err(pdev, "fail to setup ide stream\n");
+ return PTR_ERR(tlink_ide);
+ }
+
retain_and_null_ptr(tlink_spdm);
+ retain_and_null_ptr(tlink_ide);
return 0;
}
@@ -371,6 +596,7 @@ static void tdx_tsm_link_disconnect(struct pci_dev *pdev)
{
struct tdx_tsm_link *tlink = to_tdx_tsm_link(pdev->tsm);
+ tdx_ide_stream_teardown(tlink);
tdx_spdm_session_teardown(tlink);
}
--
2.25.1
^ permalink raw reply related
* [PATCH v2 29/31] x86/virt/tdx: Add SEAMCALL wrappers for IDE stream management
From: Xu Yilun @ 2026-03-27 16:01 UTC (permalink / raw)
To: linux-coco, linux-pci, dan.j.williams, x86
Cc: chao.gao, dave.jiang, baolu.lu, yilun.xu, yilun.xu,
zhenzhong.duan, kvm, rick.p.edgecombe, dave.hansen, kas,
xiaoyao.li, vishal.l.verma, linux-kernel
In-Reply-To: <20260327160132.2946114-1-yilun.xu@linux.intel.com>
Add several SEAMCALL wrappers for IDE stream management.
- TDH.IDE.STREAM.CREATE creates IDE stream metadata buffers for TDX
Module, and does root port side IDE configuration.
- TDH.IDE.STREAM.BLOCK clears the root port side IDE configuration.
- TDH.IDE.STREAM.DELETE releases the IDE stream metadata buffers.
- TDH.IDE.STREAM.KM deals with the IDE Key Management protocol (IDE-KM)
More information see Intel TDX Connect ABI Specification [1]
Section 3.2 TDX Connect Host-Side (SEAMCALL) Interface Functions.
[1]: https://cdrdv2.intel.com/v1/dl/getContent/858625
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
---
arch/x86/include/asm/tdx.h | 14 ++++++
arch/x86/virt/vmx/tdx/tdx.h | 4 ++
arch/x86/virt/vmx/tdx/tdx.c | 86 +++++++++++++++++++++++++++++++++++++
3 files changed, 104 insertions(+)
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index 8abdad084972..7bdd66acda5b 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -260,6 +260,20 @@ u64 tdh_exec_spdm_mng(u64 spdm_id, u64 spdm_op, struct page *spdm_param,
struct page *spdm_rsp, struct page *spdm_req,
struct tdx_page_array *spdm_out,
u64 *spdm_req_or_out_len);
+u64 tdh_ide_stream_create(u64 stream_info, u64 spdm_id,
+ struct tdx_page_array *stream_mt, u64 stream_ctrl,
+ u64 rid_assoc1, u64 rid_assoc2,
+ u64 addr_assoc1, u64 addr_assoc2,
+ u64 addr_assoc3,
+ u64 *stream_id,
+ u64 *rp_ide_id);
+u64 tdh_ide_stream_block(u64 spdm_id, u64 stream_id);
+u64 tdh_ide_stream_delete(u64 spdm_id, u64 stream_id,
+ struct tdx_page_array *stream_mt,
+ unsigned int *nr_released, u64 *released_hpa);
+u64 tdh_ide_stream_km(u64 spdm_id, u64 stream_id, u64 operation,
+ struct page *spdm_rsp, struct page *spdm_req,
+ u64 *spdm_req_len);
#else
static inline void tdx_init(void) { }
static inline int tdx_cpu_enable(void) { return -ENODEV; }
diff --git a/arch/x86/virt/vmx/tdx/tdx.h b/arch/x86/virt/vmx/tdx/tdx.h
index 4784db2d1d92..d0a9694432de 100644
--- a/arch/x86/virt/vmx/tdx/tdx.h
+++ b/arch/x86/virt/vmx/tdx/tdx.h
@@ -66,6 +66,10 @@
#define TDH_IOMMU_CLEAR 129
#define TDH_SPDM_CREATE 130
#define TDH_SPDM_DELETE 131
+#define TDH_IDE_STREAM_CREATE 132
+#define TDH_IDE_STREAM_BLOCK 133
+#define TDH_IDE_STREAM_DELETE 134
+#define TDH_IDE_STREAM_KM 135
#define TDH_SPDM_CONNECT 142
#define TDH_SPDM_DISCONNECT 143
#define TDH_SPDM_MNG 144
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 02882c2ad177..72d836b25bd6 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -2616,3 +2616,89 @@ u64 tdh_exec_spdm_mng(u64 spdm_id, u64 spdm_op, struct page *spdm_param,
return r;
}
EXPORT_SYMBOL_FOR_MODULES(tdh_exec_spdm_mng, "tdx-host");
+
+u64 tdh_ide_stream_create(u64 stream_info, u64 spdm_id,
+ struct tdx_page_array *stream_mt, u64 stream_ctrl,
+ u64 rid_assoc1, u64 rid_assoc2,
+ u64 addr_assoc1, u64 addr_assoc2,
+ u64 addr_assoc3,
+ u64 *stream_id,
+ u64 *rp_ide_id)
+{
+ struct tdx_module_args args = {
+ .rcx = stream_info,
+ .rdx = spdm_id,
+ .r8 = hpa_array_t_assign_raw(stream_mt),
+ .r9 = stream_ctrl,
+ .r10 = rid_assoc1,
+ .r11 = rid_assoc2,
+ .r12 = addr_assoc1,
+ .r13 = addr_assoc2,
+ .r14 = addr_assoc3,
+ };
+ u64 r;
+
+ tdx_clflush_page_array(stream_mt);
+
+ r = seamcall_saved_ret(TDH_IDE_STREAM_CREATE, &args);
+
+ *stream_id = args.rcx;
+ *rp_ide_id = args.rdx;
+
+ return r;
+}
+EXPORT_SYMBOL_FOR_MODULES(tdh_ide_stream_create, "tdx-host");
+
+u64 tdh_ide_stream_block(u64 spdm_id, u64 stream_id)
+{
+ struct tdx_module_args args = {
+ .rcx = spdm_id,
+ .rdx = stream_id,
+ };
+
+ return seamcall(TDH_IDE_STREAM_BLOCK, &args);
+}
+EXPORT_SYMBOL_FOR_MODULES(tdh_ide_stream_block, "tdx-host");
+
+u64 tdh_ide_stream_delete(u64 spdm_id, u64 stream_id,
+ struct tdx_page_array *stream_mt,
+ unsigned int *nr_released, u64 *released_hpa)
+{
+ struct tdx_module_args args = {
+ .rcx = spdm_id,
+ .rdx = stream_id,
+ .r8 = hpa_array_t_release_raw(stream_mt),
+ };
+ u64 r;
+
+ r = seamcall_ret(TDH_IDE_STREAM_DELETE, &args);
+ if (r != TDX_SUCCESS)
+ return r;
+
+ *nr_released = FIELD_GET(HPA_ARRAY_T_SIZE, args.rcx) + 1;
+ *released_hpa = FIELD_GET(HPA_ARRAY_T_PFN, args.rcx) << PAGE_SHIFT;
+
+ return r;
+}
+EXPORT_SYMBOL_FOR_MODULES(tdh_ide_stream_delete, "tdx-host");
+
+u64 tdh_ide_stream_km(u64 spdm_id, u64 stream_id, u64 operation,
+ struct page *spdm_rsp, struct page *spdm_req,
+ u64 *spdm_req_len)
+{
+ struct tdx_module_args args = {
+ .rcx = spdm_id,
+ .rdx = stream_id,
+ .r8 = operation,
+ .r9 = page_to_phys(spdm_rsp),
+ .r10 = page_to_phys(spdm_req),
+ };
+ u64 r;
+
+ r = seamcall_ret_ir_resched(TDH_IDE_STREAM_KM, &args);
+
+ *spdm_req_len = args.rcx;
+
+ return r;
+}
+EXPORT_SYMBOL_FOR_MODULES(tdh_ide_stream_km, "tdx-host");
--
2.25.1
^ permalink raw reply related
* [PATCH v2 28/31] coco/tdx-host: Parse ACPI KEYP table to init IDE for PCI host bridges
From: Xu Yilun @ 2026-03-27 16:01 UTC (permalink / raw)
To: linux-coco, linux-pci, dan.j.williams, x86
Cc: chao.gao, dave.jiang, baolu.lu, yilun.xu, yilun.xu,
zhenzhong.duan, kvm, rick.p.edgecombe, dave.hansen, kas,
xiaoyao.li, vishal.l.verma, linux-kernel
In-Reply-To: <20260327160132.2946114-1-yilun.xu@linux.intel.com>
Parse the KEYP Key Configuration Units (KCU), to decide the max IDE
streams supported for each host bridge.
The KEYP table points to a number of KCU structures that each associates
with a list of root ports (RP) via segment, bus, and devfn. Sanity check
the KEYP table, ensure all RPs listed for each KCU are included in one
host bridge. Then extact the max IDE streams supported to
pci_host_bridge via pci_ide_set_nr_streams().
Co-developed-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
---
drivers/virt/coco/tdx-host/tdx-host.c | 111 ++++++++++++++++++++++++++
1 file changed, 111 insertions(+)
diff --git a/drivers/virt/coco/tdx-host/tdx-host.c b/drivers/virt/coco/tdx-host/tdx-host.c
index 4d127b7c2591..d5072a68b81a 100644
--- a/drivers/virt/coco/tdx-host/tdx-host.c
+++ b/drivers/virt/coco/tdx-host/tdx-host.c
@@ -5,6 +5,7 @@
* Copyright (C) 2025 Intel Corporation
*/
+#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/device/faux.h>
#include <linux/dmar.h>
@@ -12,6 +13,7 @@
#include <linux/mod_devicetable.h>
#include <linux/pci.h>
#include <linux/pci-doe.h>
+#include <linux/pci-ide.h>
#include <linux/pci-tsm.h>
#include <linux/tsm.h>
#include <linux/vmalloc.h>
@@ -474,6 +476,111 @@ static void unregister_link_tsm(void *link)
tsm_unregister(link);
}
+#define KCU_STR_CAP_NUM_STREAMS GENMASK(8, 0)
+
+/* The bus_end is inclusive */
+struct keyp_hb_info {
+ /* input */
+ u16 segment;
+ u8 bus_start;
+ u8 bus_end;
+ /* output */
+ u8 nr_ide_streams;
+};
+
+static bool keyp_info_match(struct acpi_keyp_rp_info *rp,
+ struct keyp_hb_info *hb)
+{
+ return rp->segment == hb->segment && rp->bus >= hb->bus_start &&
+ rp->bus <= hb->bus_end;
+}
+
+static int keyp_config_unit_handler(union acpi_subtable_headers *header,
+ void *arg, const unsigned long end)
+{
+ struct acpi_keyp_config_unit *acpi_cu =
+ (struct acpi_keyp_config_unit *)&header->keyp;
+ struct keyp_hb_info *hb_info = arg;
+ int rp_size, rp_count, i;
+ void __iomem *addr;
+ bool match = false;
+ u32 cap;
+
+ rp_size = acpi_cu->header.length - sizeof(*acpi_cu);
+ if (rp_size % sizeof(struct acpi_keyp_rp_info))
+ return -EINVAL;
+
+ rp_count = rp_size / sizeof(struct acpi_keyp_rp_info);
+ if (!rp_count || rp_count != acpi_cu->root_port_count)
+ return -EINVAL;
+
+ for (i = 0; i < rp_count; i++) {
+ struct acpi_keyp_rp_info *rp_info = &acpi_cu->rp_info[i];
+
+ if (i == 0) {
+ match = keyp_info_match(rp_info, hb_info);
+ /* The host bridge already matches another KCU */
+ if (match && hb_info->nr_ide_streams)
+ return -EINVAL;
+
+ continue;
+ }
+
+ if (match ^ keyp_info_match(rp_info, hb_info))
+ return -EINVAL;
+ }
+
+ if (!match)
+ return 0;
+
+ addr = ioremap(acpi_cu->register_base_address, sizeof(cap));
+ if (!addr)
+ return -ENOMEM;
+ cap = ioread32(addr);
+ iounmap(addr);
+
+ hb_info->nr_ide_streams = FIELD_GET(KCU_STR_CAP_NUM_STREAMS, cap) + 1;
+
+ return 0;
+}
+
+static u8 keyp_find_nr_ide_stream(u16 segment, u8 bus_start, u8 bus_end)
+{
+ struct keyp_hb_info hb_info = {
+ .segment = segment,
+ .bus_start = bus_start,
+ .bus_end = bus_end,
+ };
+ int rc;
+
+ rc = acpi_table_parse_keyp(ACPI_KEYP_TYPE_CONFIG_UNIT,
+ keyp_config_unit_handler, &hb_info);
+ if (rc < 0)
+ return 0;
+
+ return hb_info.nr_ide_streams;
+}
+
+static void keyp_setup_nr_ide_stream(struct pci_bus *bus)
+{
+ struct pci_host_bridge *hb = pci_find_host_bridge(bus);
+ u8 nr_ide_streams;
+
+ nr_ide_streams = keyp_find_nr_ide_stream(pci_domain_nr(bus),
+ bus->busn_res.start,
+ bus->busn_res.end);
+
+ pci_ide_set_nr_streams(hb, nr_ide_streams);
+}
+
+static void tdx_setup_nr_ide_stream(void)
+{
+ struct pci_bus *bus = NULL;
+
+ while ((bus = pci_find_next_bus(bus)))
+ keyp_setup_nr_ide_stream(bus);
+}
+
static DEFINE_XARRAY(tlink_iommu_xa);
static void tdx_iommu_clear(u64 iommu_id, struct tdx_page_array *iommu_mt)
@@ -569,6 +676,8 @@ static int __maybe_unused tdx_connect_init(struct device *dev)
if (ret)
return ret;
+ tdx_setup_nr_ide_stream();
+
link = tsm_register(dev, &tdx_tsm_link_ops);
if (IS_ERR(link))
return dev_err_probe(dev, PTR_ERR(link),
@@ -612,5 +721,7 @@ static void __exit tdx_host_exit(void)
}
module_exit(tdx_host_exit);
+MODULE_IMPORT_NS("ACPI");
+MODULE_IMPORT_NS("PCI_IDE");
MODULE_DESCRIPTION("TDX Host Services");
MODULE_LICENSE("GPL");
--
2.25.1
^ permalink raw reply related
* [PATCH v2 27/31] coco/tdx-host: Implement SPDM session setup
From: Xu Yilun @ 2026-03-27 16:01 UTC (permalink / raw)
To: linux-coco, linux-pci, dan.j.williams, x86
Cc: chao.gao, dave.jiang, baolu.lu, yilun.xu, yilun.xu,
zhenzhong.duan, kvm, rick.p.edgecombe, dave.hansen, kas,
xiaoyao.li, vishal.l.verma, linux-kernel
In-Reply-To: <20260327160132.2946114-1-yilun.xu@linux.intel.com>
From: Zhenzhong Duan <zhenzhong.duan@intel.com>
Implementation for a most straightforward SPDM session setup, using all
default session options. Retrieve device info data from TDX Module which
contains the SPDM negotiation results.
TDH.SPDM.CONNECT/DISCONNECT are TDX Module Extension introduced
SEAMCALLs which can run for longer periods and interruptible. But there
is resource constraints that limit how many SEAMCALLs of this kind can
run simultaneously. The current situation is One SEAMCALL at a time.
Otherwise TDX_OPERAND_BUSY is returned. To avoid "broken indefinite"
retry, a tdx_ext_lock is used to guard these SEAMCALLs.
Co-developed-by: Xu Yilun <yilun.xu@linux.intel.com>
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Signed-off-by: Zhenzhong Duan <zhenzhong.duan@intel.com>
---
arch/x86/include/asm/shared/tdx_errno.h | 2 +
drivers/virt/coco/tdx-host/tdx-host.c | 301 +++++++++++++++++++++++-
2 files changed, 299 insertions(+), 4 deletions(-)
diff --git a/arch/x86/include/asm/shared/tdx_errno.h b/arch/x86/include/asm/shared/tdx_errno.h
index 8bf6765cf082..7db04fe30378 100644
--- a/arch/x86/include/asm/shared/tdx_errno.h
+++ b/arch/x86/include/asm/shared/tdx_errno.h
@@ -29,6 +29,8 @@
#define TDX_EPT_WALK_FAILED 0xC0000B0000000000ULL
#define TDX_EPT_ENTRY_STATE_INCORRECT 0xC0000B0D00000000ULL
#define TDX_METADATA_FIELD_NOT_READABLE 0xC0000C0200000000ULL
+#define TDX_SPDM_SESSION_KEY_REQUIRE_REFRESH 0xC0000F4500000000ULL
+#define TDX_SPDM_REQUEST 0xC0000F5700000000ULL
/*
* SW-defined error codes.
diff --git a/drivers/virt/coco/tdx-host/tdx-host.c b/drivers/virt/coco/tdx-host/tdx-host.c
index 06f3d194e0a8..4d127b7c2591 100644
--- a/drivers/virt/coco/tdx-host/tdx-host.c
+++ b/drivers/virt/coco/tdx-host/tdx-host.c
@@ -14,6 +14,7 @@
#include <linux/pci-doe.h>
#include <linux/pci-tsm.h>
#include <linux/tsm.h>
+#include <linux/vmalloc.h>
#include <asm/cpu_device_id.h>
#include <asm/tdx.h>
@@ -32,8 +33,43 @@ MODULE_DEVICE_TABLE(x86cpu, tdx_host_ids);
*/
static const struct tdx_sys_info *tdx_sysinfo;
+#define TDISP_FUNC_ID GENMASK(15, 0)
+#define TDISP_FUNC_ID_SEGMENT GENMASK(23, 16)
+#define TDISP_FUNC_ID_SEG_VALID BIT(24)
+
+static inline u32 tdisp_func_id(struct pci_dev *pdev)
+{
+ u32 func_id;
+
+ func_id = FIELD_PREP(TDISP_FUNC_ID_SEGMENT, pci_domain_nr(pdev->bus));
+ if (func_id)
+ func_id |= TDISP_FUNC_ID_SEG_VALID;
+ func_id |= FIELD_PREP(TDISP_FUNC_ID,
+ PCI_DEVID(pdev->bus->number, pdev->devfn));
+
+ return func_id;
+}
+
+struct spdm_config_info_t {
+ u32 vmm_spdm_cap;
+#define SPDM_CAP_HBEAT BIT(13)
+#define SPDM_CAP_KEY_UPD BIT(14)
+ u8 spdm_session_policy;
+ u8 certificate_slot_mask;
+ u8 raw_bitstream_requested;
+} __packed;
+
struct tdx_tsm_link {
struct pci_tsm_pf0 pci;
+ u32 func_id;
+ struct page *in_msg;
+ struct page *out_msg;
+
+ u64 spdm_id;
+ struct page *spdm_conf;
+ struct tdx_page_array *spdm_mt;
+ unsigned int dev_info_size;
+ void *dev_info_data;
};
static struct tdx_tsm_link *to_tdx_tsm_link(struct pci_tsm *tsm)
@@ -48,9 +84,9 @@ static struct tdx_tsm_link *to_tdx_tsm_link(struct pci_tsm *tsm)
#define PCI_DOE_PROTOCOL_SECURE_SPDM 2
-static int __maybe_unused tdx_spdm_msg_exchange(struct tdx_tsm_link *tlink,
- void *request, size_t request_sz,
- void *response, size_t response_sz)
+static int tdx_spdm_msg_exchange(struct tdx_tsm_link *tlink,
+ void *request, size_t request_sz,
+ void *response, size_t response_sz)
{
struct pci_dev *pdev = tlink->pci.base_tsm.pdev;
void *req_pl_addr, *resp_pl_addr;
@@ -100,18 +136,246 @@ static int __maybe_unused tdx_spdm_msg_exchange(struct tdx_tsm_link *tlink,
return ret;
}
+static int tdx_spdm_session_keyupdate(struct tdx_tsm_link *tlink);
+
+static int tdx_tsm_link_event_handler(struct tdx_tsm_link *tlink,
+ u64 tdx_ret, u64 out_msg_sz)
+{
+ int ret;
+
+ if (tdx_ret == TDX_SUCCESS)
+ return 0;
+
+ if (tdx_ret == TDX_SPDM_REQUEST) {
+ ret = tdx_spdm_msg_exchange(tlink,
+ page_address(tlink->out_msg),
+ out_msg_sz,
+ page_address(tlink->in_msg),
+ PAGE_SIZE);
+ if (ret < 0)
+ return ret;
+
+ return -EAGAIN;
+ }
+
+ if (tdx_ret == TDX_SPDM_SESSION_KEY_REQUIRE_REFRESH) {
+ /* keyupdate won't trigger this error again, no recursion risk */
+ ret = tdx_spdm_session_keyupdate(tlink);
+ if (ret)
+ return ret;
+
+ return -EAGAIN;
+ }
+
+ return -EFAULT;
+}
+
+/*
+ * TDX Module extension introduced SEAMCALLs work like a request queue.
+ * The caller is responsible for grabbing a queue slot before SEAMCALL,
+ * otherwise will fail with TDX_OPERAND_BUSY. Currently the queue depth is 1.
+ * So a mutex could work for simplicity.
+ */
+static DEFINE_MUTEX(tdx_ext_lock);
+
+enum tdx_spdm_mng_op {
+ TDX_SPDM_MNG_HEARTBEAT = 0,
+ TDX_SPDM_MNG_KEY_UPDATE = 1,
+ TDX_SPDM_MNG_RECOLLECT = 2,
+};
+
+static int tdx_spdm_session_mng(struct tdx_tsm_link *tlink,
+ enum tdx_spdm_mng_op op)
+{
+ u64 r, out_msg_sz;
+ int ret;
+
+ guard(mutex)(&tdx_ext_lock);
+ do {
+ r = tdh_exec_spdm_mng(tlink->spdm_id, op, NULL, tlink->in_msg,
+ tlink->out_msg, NULL, &out_msg_sz);
+ ret = tdx_tsm_link_event_handler(tlink, r, out_msg_sz);
+ } while (ret == -EAGAIN);
+
+ return ret;
+}
+
+static int tdx_spdm_session_keyupdate(struct tdx_tsm_link *tlink)
+{
+ return tdx_spdm_session_mng(tlink, TDX_SPDM_MNG_KEY_UPDATE);
+}
+
+static void *tdx_dup_array_data(struct tdx_page_array *array,
+ unsigned int data_size)
+{
+ unsigned int npages = (data_size + PAGE_SIZE - 1) / PAGE_SIZE;
+ void *data, *dup_data;
+
+ if (npages > array->nr_pages)
+ return NULL;
+
+ data = vm_map_ram(array->pages, npages, -1);
+ if (!data)
+ return NULL;
+
+ dup_data = kmemdup(data, data_size, GFP_KERNEL);
+ vm_unmap_ram(data, npages);
+
+ return dup_data;
+}
+
+static struct tdx_tsm_link *
+tdx_spdm_session_connect(struct tdx_tsm_link *tlink,
+ struct tdx_page_array *dev_info)
+{
+ u64 r, out_msg_sz;
+ int ret;
+
+ guard(mutex)(&tdx_ext_lock);
+ do {
+ r = tdh_exec_spdm_connect(tlink->spdm_id, tlink->spdm_conf,
+ tlink->in_msg, tlink->out_msg,
+ dev_info, &out_msg_sz);
+ ret = tdx_tsm_link_event_handler(tlink, r, out_msg_sz);
+ } while (ret == -EAGAIN);
+
+ if (ret)
+ return ERR_PTR(ret);
+
+ tlink->dev_info_size = out_msg_sz;
+ return tlink;
+}
+
+static void tdx_spdm_session_disconnect(struct tdx_tsm_link *tlink)
+{
+ u64 r, out_msg_sz;
+ int ret;
+
+ guard(mutex)(&tdx_ext_lock);
+ do {
+ r = tdh_exec_spdm_disconnect(tlink->spdm_id, tlink->in_msg,
+ tlink->out_msg, &out_msg_sz);
+ ret = tdx_tsm_link_event_handler(tlink, r, out_msg_sz);
+ } while (ret == -EAGAIN);
+
+ WARN_ON(ret);
+}
+
+DEFINE_FREE(tdx_spdm_session_disconnect, struct tdx_tsm_link *,
+ if (!IS_ERR_OR_NULL(_T)) tdx_spdm_session_disconnect(_T))
+
+static struct tdx_tsm_link *tdx_spdm_create(struct tdx_tsm_link *tlink)
+{
+ unsigned int nr_pages = tdx_sysinfo->connect.spdm_mt_page_count;
+ u64 spdm_id, r;
+
+ struct tdx_page_array *spdm_mt __free(tdx_page_array_free) =
+ tdx_page_array_create(nr_pages);
+ if (!spdm_mt)
+ return ERR_PTR(-ENOMEM);
+
+ r = tdh_spdm_create(tlink->func_id, spdm_mt, &spdm_id);
+ if (r)
+ return ERR_PTR(-EFAULT);
+
+ tlink->spdm_id = spdm_id;
+ tlink->spdm_mt = no_free_ptr(spdm_mt);
+ return tlink;
+}
+
+static void tdx_spdm_delete(struct tdx_tsm_link *tlink)
+{
+ struct pci_dev *pdev = tlink->pci.base_tsm.pdev;
+ unsigned int nr_released;
+ u64 released_hpa, r;
+
+ r = tdh_spdm_delete(tlink->spdm_id, tlink->spdm_mt, &nr_released, &released_hpa);
+ if (r) {
+ pci_err(pdev, "fail to delete spdm 0x%llx\n", r);
+ goto leak;
+ }
+
+ if (tdx_page_array_ctrl_release(tlink->spdm_mt, nr_released, released_hpa)) {
+ pci_err(pdev, "fail to release spdm_mt pages\n");
+ goto leak;
+ }
+
+ return;
+
+leak:
+ tdx_page_array_ctrl_leak(tlink->spdm_mt);
+}
+
+DEFINE_FREE(tdx_spdm_delete, struct tdx_tsm_link *, if (!IS_ERR_OR_NULL(_T)) tdx_spdm_delete(_T))
+
+static struct tdx_tsm_link *tdx_spdm_session_setup(struct tdx_tsm_link *tlink)
+{
+ unsigned int nr_pages = tdx_sysinfo->connect.spdm_max_dev_info_pages;
+
+ struct tdx_tsm_link *tlink_create __free(tdx_spdm_delete) =
+ tdx_spdm_create(tlink);
+ if (IS_ERR(tlink_create))
+ return tlink_create;
+
+ struct tdx_page_array *dev_info __free(tdx_page_array_free) =
+ tdx_page_array_create(nr_pages);
+ if (!dev_info)
+ return ERR_PTR(-ENOMEM);
+
+ struct tdx_tsm_link *tlink_connect __free(tdx_spdm_session_disconnect) =
+ tdx_spdm_session_connect(tlink, dev_info);
+ if (IS_ERR(tlink_connect))
+ return tlink_connect;
+
+ tlink->dev_info_data = tdx_dup_array_data(dev_info,
+ tlink->dev_info_size);
+ if (!tlink->dev_info_data)
+ return ERR_PTR(-ENOMEM);
+
+ retain_and_null_ptr(tlink_create);
+ retain_and_null_ptr(tlink_connect);
+
+ return tlink;
+}
+
+static void tdx_spdm_session_teardown(struct tdx_tsm_link *tlink)
+{
+ kfree(tlink->dev_info_data);
+
+ tdx_spdm_session_disconnect(tlink);
+ tdx_spdm_delete(tlink);
+}
+
+DEFINE_FREE(tdx_spdm_session_teardown, struct tdx_tsm_link *,
+ if (!IS_ERR_OR_NULL(_T)) tdx_spdm_session_teardown(_T))
+
static int tdx_tsm_link_connect(struct pci_dev *pdev)
{
- return -ENXIO;
+ struct tdx_tsm_link *tlink = to_tdx_tsm_link(pdev->tsm);
+
+ struct tdx_tsm_link *tlink_spdm __free(tdx_spdm_session_teardown) =
+ tdx_spdm_session_setup(tlink);
+ if (IS_ERR(tlink_spdm)) {
+ pci_err(pdev, "fail to setup spdm session\n");
+ return PTR_ERR(tlink_spdm);
+ }
+
+ retain_and_null_ptr(tlink_spdm);
+
+ return 0;
}
static void tdx_tsm_link_disconnect(struct pci_dev *pdev)
{
+ struct tdx_tsm_link *tlink = to_tdx_tsm_link(pdev->tsm);
+
+ tdx_spdm_session_teardown(tlink);
}
static struct pci_tsm *tdx_tsm_link_pf0_probe(struct tsm_dev *tsm_dev,
struct pci_dev *pdev)
{
+ struct spdm_config_info_t *spdm_conf;
int rc;
struct tdx_tsm_link *tlink __free(kfree) = kzalloc_obj(*tlink);
@@ -122,6 +386,32 @@ static struct pci_tsm *tdx_tsm_link_pf0_probe(struct tsm_dev *tsm_dev,
if (rc)
return NULL;
+ tlink->func_id = tdisp_func_id(pdev);
+
+ struct page *in_msg_page __free(__free_page) =
+ alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!in_msg_page)
+ return NULL;
+
+ struct page *out_msg_page __free(__free_page) =
+ alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!out_msg_page)
+ return NULL;
+
+ struct page *spdm_conf_page __free(kfree) =
+ alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!spdm_conf_page)
+ return NULL;
+
+ /* use a default configuration, may require user input later */
+ spdm_conf = page_address(spdm_conf_page);
+ spdm_conf->vmm_spdm_cap = SPDM_CAP_KEY_UPD;
+ spdm_conf->certificate_slot_mask = 0xff;
+
+ tlink->in_msg = no_free_ptr(in_msg_page);
+ tlink->out_msg = no_free_ptr(out_msg_page);
+ tlink->spdm_conf = no_free_ptr(spdm_conf_page);
+
return &no_free_ptr(tlink)->pci.base_tsm;
}
@@ -129,6 +419,9 @@ static void tdx_tsm_link_pf0_remove(struct pci_tsm *tsm)
{
struct tdx_tsm_link *tlink = to_tdx_tsm_link(tsm);
+ __free_page(tlink->spdm_conf);
+ __free_page(tlink->out_msg);
+ __free_page(tlink->in_msg);
pci_tsm_pf0_destructor(&tlink->pci);
kfree(tlink);
}
--
2.25.1
^ permalink raw reply related
* [PATCH v2 26/31] mm: Add __free() support for __free_page()
From: Xu Yilun @ 2026-03-27 16:01 UTC (permalink / raw)
To: linux-coco, linux-pci, dan.j.williams, x86
Cc: chao.gao, dave.jiang, baolu.lu, yilun.xu, yilun.xu,
zhenzhong.duan, kvm, rick.p.edgecombe, dave.hansen, kas,
xiaoyao.li, vishal.l.verma, linux-kernel
In-Reply-To: <20260327160132.2946114-1-yilun.xu@linux.intel.com>
Allow for the declaration of struct page * variables that trigger
__free_page() when they go out of scope.
A example usage would be in the following patch:
static struct pci_tsm *tdx_tsm_link_pf0_probe(...)
{
...
struct page *in_msg_page __free(__free_page) =
alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!in_msg_page)
return NULL;
struct page *out_msg_page __free(__free_page) =
alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!out_msg_page)
return NULL;
...
tlink->in_msg = no_free_ptr(in_msg_page);
tlink->out_msg = no_free_ptr(out_msg_page);
...
}
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
---
include/linux/gfp.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index 51ef13ed756e..d37e5564234e 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -391,6 +391,7 @@ extern void free_pages_nolock(struct page *page, unsigned int order);
extern void free_pages(unsigned long addr, unsigned int order);
#define __free_page(page) __free_pages((page), 0)
+DEFINE_FREE(__free_page, struct page *, if (_T) __free_page(_T))
#define free_page(addr) free_pages((addr), 0)
void page_alloc_init_cpuhp(void);
--
2.25.1
^ permalink raw reply related
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