* Re: [PATCH v2 10/31] x86/virt/tdx: Add extra memory to TDX Module for Extensions
From: Xu Yilun @ 2026-04-24 10:41 UTC (permalink / raw)
To: Huang, Kai
Cc: kvm@vger.kernel.org, linux-coco@lists.linux.dev, Li, Xiaoyao,
dave.hansen@linux.intel.com, baolu.lu@linux.intel.com,
linux-kernel@vger.kernel.org, kas@kernel.org, Xu, Yilun,
Verma, Vishal L, Jiang, Dave, Duan, Zhenzhong, Gao, Chao,
Edgecombe, Rick P, linux-pci@vger.kernel.org, x86@kernel.org,
dan.j.williams@intel.com
In-Reply-To: <668a903ba2dccff2d641ac15b74deacc95ef19a6.camel@intel.com>
On Fri, Apr 24, 2026 at 08:09:16AM +0000, Huang, Kai wrote:
> On Fri, 2026-04-24 at 11:07 +0800, Xu Yilun wrote:
> > On Thu, Apr 23, 2026 at 10:29:31PM +0000, Huang, Kai wrote:
> > > On Thu, 2026-04-23 at 17:05 +0000, Edgecombe, Rick P wrote:
> > > > On Thu, 2026-04-23 at 00:59 +0000, Huang, Kai wrote:
> > > > > Ditto here. I don't think we should introduce any more cond_resched().
> > > > >
> > > > > Btw, I think technically we can reuse the seamcall_ir_resched() you introduced
> > > > > later, albeit in which a local '_args' is used as a copy of the original 'args',
> > > > > but that has no harm for the case where we can just use the 'args' to loop.
> > > > >
> > > > > I am wondering whether we can just use that here, or we just get rid of that
> > > > > helper (then open retry by the callers of these SEAMCALL wrappers), since there
> > > > > will be more cases where we need to manually set 'resume=1' in the SEAMCALL
> > > > > input 'args' when retrying TDX_INTERRUPTED_RESUMABLE.
> > > >
> > > > I kind of like the latter option to open code more of this stuff. The stacks of
> > > > seamcall wrapper macros is already too much.
> > >
> > > Agreed.
> > >
> > > And SEAMCALL *users* can actually come up with their own version of wrapper(s)
> > > to do the retry. E.g., currently seamcall_ir_resched() is only used for IOMMU
> > > SEAMCALLs, and we can put this wrapper in the IOMMU code or coco/tdx-host.
> >
> > After we have introduced TDX Module Extension, irq preemptable
> > EXT-SEAMCALLs become a common concept.
> >
>
> It has been a concept since before the EXT-SEAMCALLs actually. For instance,
No, they look similar but different. EXT-SEAMCALLs are truly irq
preemptable and resumable to its context. Other SEAMCALLs just
periodically yield and don't have a generic way to save/resume their
context. Sometime you need to pass in resume flag on 2nd time, which
means the secure world forget where they were and can't really resume
all by itself.
What I mean is, EXT-SEAMCALLs should never need to play tricks on
input parameters. Just input what is originally inputted, the secure
world doesn't need hint to resume itself. So the int-retry process
should be common.
> TDX live migration using blocking export doesn't need any opt-in via module
> extension (only the non-blocking way needs), but the SEAMCALLs to export/import
> TD/vCPU/memory are all interruptible.
>
> In fact, they had the "latency requirement" behind the INTERRUPT_RESUMABLE in
> mind at the very beginning. It's just at that point all SEAMCALLs were not that
> heavy.
>
> > It is irq preemptable so that the
> > secure world remembers and resumes the context, no need for host to
> > remind via resume lag.
>
> The fact is the aforementioned live migration related export/import SEAMCALLs
> (there are 8 at least, but maybe more) all requires the explicit setting of
> 'resume=1' (plus using the SEAMCALL output as input for retry). I don't know
Yes, so they are not truly interrupt resumable and should be specially
treated.
> the story behind this, though. There might be some tricky thing here for the
> module to remember and manage (e.g., migration has a concept of "migration
> stream", and the resume is per-stream).
>
> >
> > Today there are 3 EXT-SEAMCALLs, TDH_SPDM_CONNECT/DISCONNECT/MNG,
> > irq preemption handling is a general requirement for them, and I think
> > it is still true for any further EXT-SEAMCALLs.
> >
> > So I think a general helper for EXT-SEAMCALLs makes sense.
>
> Yes conceptually I agree, but not need to distinguish EXT-SEAMCALLs or not IMHO.
>
> The problem is there isn't a common rule to follow.
>
> E.g., let's say "the module can remember thus no resume flag is needed", how
> about the SEAMCALL inputs? Can the "output" args be directly used as input for
> retry, or the original input should always be used?
Since EXT-SEAMCALLs don't depend on input tricks to resume, there could
be a common rule, now it is defined as "the original input should always
be used".
>
> Not to mention there's existing SEAMCALLs which require explicitly setting
> 'resume=1'.
>
> I believe we can use some smart hack to implement a common one to cover all
> cases above, but I am not sure whether it's worth to do (maybe we can have a try
> to see how does it look like, though, I think).
>
> Given the SEAMCALLs for TDX Connect seem to follow one rule to retry, and live
> migration SEAMCALLs follow another rule, it seems for now the simplest way is to
> introduce the needed retry helper in the layer of SEAMCALL *user* (TDX Connect
> and migration).
>
> > TDH.IOMMU.SETUP, however, is another case. It is not a EXT-SEAMCALL but
> > happened to follow the same irq-retry handling process. To avoid code
> > duplication we have:
> >
> > /*
> > * seamcall_ret_ir_exec() aliases seamcall_ret_ir_resched() for
> > * documentation purposes. It documents the TDX Module extension
> > * seamcalls that are long running / hard-irq preemptible flows that
> > * generate events. The calls using seamcall_ret_ir_resched() are long
> > * running flows, that periodically yield.
> > */
> > #define seamcall_ret_ir_exec seamcall_ret_ir_resched
> >
> > TDH.IOMMU.SETUP uses seamcall_ret_ir_resched(), and EXT-SEAMCALLs use
> > seamcall_ret_ir_exec().
> >
> > How do you think?
>
> Sorry I don't quite get. What does "exec" postfix mean?
It is 'execution', means EXT-SEAMCALLs can resume their execution. But
since you have concern, maybe some better name?
>
> From patch 25, they are all in TDX core, so I don't quite get why we need to
> distinguish EXT-SEAMCALLs vs normal ones. IMHO it's an additional layer which
EXT-SEAMCALLs have generic way to resume, while others don't. So we need
a helper for EXT-SEAMCALLs. For other SEAMCALLs that happens to process
the same way, we are avoiding code duplication, but should clearly
distinguish the purpose so make another name as documentation.
But if any concern, we could delete the int-retry support for normal
SEAMCALLs, they are not generic as you said.
> doesn't actually help address any problem.
>
> Btw, we should really get rid of the "resched()" postfix from the function name
> since cond_resched() is no longer needed and possibility of rescheduling is
> implied pretty much all places in the kernel code now (except some special code
> such as code in IRQ context).
Yes, thanks to remind me again.
^ permalink raw reply
* Re: [PATCH v5 1/2] dma-mapping: introduce DMA_ATTR_CC_SHARED for shared memory
From: Jason Gunthorpe @ 2026-04-24 22:55 UTC (permalink / raw)
To: Aneesh Kumar K.V
Cc: Jiri Pirko, dri-devel, linaro-mm-sig, iommu, linux-media,
sumit.semwal, benjamin.gaignard, Brian.Starkey, jstultz,
tjmercier, christian.koenig, m.szyprowski, robin.murphy, leon,
sean.anderson, ptesarik, catalin.marinas, suzuki.poulose,
steven.price, thomas.lendacky, john.allen, ashish.kalra,
suravee.suthikulpanit, linux-coco
In-Reply-To: <yq5aik9jcpzm.fsf@kernel.org>
On Wed, Apr 22, 2026 at 02:48:37PM +0530, Aneesh Kumar K.V wrote:
> Jason Gunthorpe <jgg@ziepe.ca> writes:
>
> > On Tue, Apr 21, 2026 at 01:53:31PM +0200, Jiri Pirko wrote:
> >> >> You reach there when is_swiotlb_force_bounce(dev) is true and
> >> >> DMA_ATTR_CC_SHARED is set. What am I missing?
> >> >>
> >> >
> >> >So a swiotlb_force_bounce will not use swiotlb bouncing if
> >> >DMA_ATTR_CC_SHARED is set ?
> >>
> >> Correct. Bouncing does not make sense in this case, as shared memory is
> >> already being mapped.
> >
> > It is a little bit mangled, there are many reasons force_swiotlb can
> > be set, but we loose them as it flows through - swiotlb_init()
> > just has a simple SWIOTLB_FORCE
> >
> > Ideally DMA_ATTR_CC_SHARED would skip swiotlb only if it is being
> > selected for CC reasons. For instance if you have the swiotlb force
> > command line parameter I would still expect it bounce shared memory.
> >
> > Arguably I think this arch flow is misdesigned, the
> > is_swiotlb_force_bounce() should not be used for CC. dma_capable() is
> > the correct API to check if the device can DMA to the presented
> > address, and it will trigger swiotlb_map() just the same without
> > creating this gap.
> >
> > Jason
>
> Something like this?
Yeah that reads pretty sanely.
> static inline dma_addr_t dma_direct_map_phys(struct device *dev,
> phys_addr_t phys, size_t size, enum dma_data_direction dir,
> unsigned long attrs, bool flush)
> {
> dma_addr_t dma_addr;
>
> if (is_swiotlb_force_bounce(dev)) {
> if (attrs & (DMA_ATTR_MMIO | DMA_ATTR_REQUIRE_COHERENT))
> return DMA_MAPPING_ERROR;
>
> return swiotlb_map(dev, phys, size, dir, attrs);
> }
>
> if (attrs & DMA_ATTR_MMIO) {
> dma_addr = phys;
> if (unlikely(!dma_capable(dev, dma_addr, size, false, attrs)))
> goto err_overflow;
> goto dma_mapped;
I suspect P2P is probably broken on CC because this doesn't make
sense..
This should flow into the
phys_to_dma_unencrypted/phys_to_dma_encrypted block as well AFAICT, it
shouldn't just assign phys. Assigning phys to dma on a CC system is
always wrong, right?
It is is more like
/* To be updated, callers should specify MMIO | CC_SHARED instead of
* implying it. */
if (attrs & DMA_ATTR_MMIO)
attrs |= DMA_ATTR_CC_SHARED;
if (attrs & DMA_ATTR_CC_SHARED) {
dma_addr = phys_to_dma_unencrypted(dev, phys);
} else {
dma_addr = phys_to_dma_encrypted(dev, phys);
}
if (!dma_capable()) {
if (attrs & (DMA_ATTR_MMIO | DMA_ATTR_REQUIRE_COHERENT)
fail
}
> and dma_capable() now does
> static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size,
> bool is_ram, unsigned long attrs)
> {
> ....
>
> /*
> * if phys addr attribute is encrypted but the
> * device is forcing an encrypted dma addr
> */
> if (!(attrs & DMA_ATTR_CC_SHARED) && force_dma_unencrypted(dev))
> return false;
Yeah
And with the above little edits it works for MMIO now too.
Jason
^ permalink raw reply
* Re: [PATCH v5 1/2] dma-mapping: introduce DMA_ATTR_CC_SHARED for shared memory
From: Jason Gunthorpe @ 2026-04-26 13:05 UTC (permalink / raw)
To: Aneesh Kumar K.V
Cc: Jiri Pirko, dri-devel, linaro-mm-sig, iommu, linux-media,
sumit.semwal, benjamin.gaignard, Brian.Starkey, jstultz,
tjmercier, christian.koenig, m.szyprowski, robin.murphy, leon,
sean.anderson, ptesarik, catalin.marinas, suzuki.poulose,
steven.price, thomas.lendacky, john.allen, ashish.kalra,
suravee.suthikulpanit, linux-coco
In-Reply-To: <20260424225514.GE804026@ziepe.ca>
> > static inline dma_addr_t dma_direct_map_phys(struct device *dev,
> > phys_addr_t phys, size_t size, enum dma_data_direction dir,
> > unsigned long attrs, bool flush)
> > {
> > dma_addr_t dma_addr;
> >
> > if (is_swiotlb_force_bounce(dev)) {
> > if (attrs & (DMA_ATTR_MMIO | DMA_ATTR_REQUIRE_COHERENT))
> > return DMA_MAPPING_ERROR;
> >
> > return swiotlb_map(dev, phys, size, dir, attrs);
> > }
> >
> > if (attrs & DMA_ATTR_MMIO) {
> > dma_addr = phys;
> > if (unlikely(!dma_capable(dev, dma_addr, size, false, attrs)))
> > goto err_overflow;
> > goto dma_mapped;
>
> I suspect P2P is probably broken on CC because this doesn't make
> sense..
Actually, I suppose it is fully broken because it will jump to swiotlb
and then should fail.
> This should flow into the
> phys_to_dma_unencrypted/phys_to_dma_encrypted block as well AFAICT, it
> shouldn't just assign phys. Assigning phys to dma on a CC system is
> always wrong, right?
>
> It is is more like
>
> /* To be updated, callers should specify MMIO | CC_SHARED instead of
> * implying it. */
> if (attrs & DMA_ATTR_MMIO)
> attrs |= DMA_ATTR_CC_SHARED;
So no need for this if, we can go directly to marking the MMIO callers
with DMA_ATTR_CC_SHARED once this is fixed for mmio:
> if (attrs & DMA_ATTR_CC_SHARED) {
> dma_addr = phys_to_dma_unencrypted(dev, phys);
> } else {
> dma_addr = phys_to_dma_encrypted(dev, phys);
> }
Jasn
^ permalink raw reply
* [GIT PULL] Trusted Security Manager (PCIe TSM) Update for 7.1
From: Dan Williams @ 2026-04-26 16:11 UTC (permalink / raw)
To: Linus Torvalds; +Cc: linux-coco, linux-kernel
Hi Linus, please pull from:
git://git.kernel.org/pub/scm/linux/kernel/git/devsec/tsm tags/tsm-for-7.1
...to receive a small update for the TSM core. It is arguably a fix and
coming in late as I have been offline the past few weeks.
Recall that you asked for searchable help with the TLA disease last time
[1]. "PCIe TSM" turns up useful results.
The other motivation for sending this one-patch pull request is to test
that you have my key with updated email.
This has been in linux-next for a while with no reported issues.
[1]: http://lore.kernel.org/CAHk-=whjvmBiZ=oMnR-R9rqzEPnGCaU7dNLkY1RHXwjRCAR5YQ@mail.gmail.com
--
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-for-7.1
for you to fetch changes up to 3177779ae17db4c66c851f799505fb95c7530c03:
virt: coco: change tsm_class to a const struct (2026-04-02 15:45:18 -0700)
----------------------------------------------------------------
tsm for 7.1
- Drop class_create() for the "tsm" class
----------------------------------------------------------------
Jori Koolstra (1):
virt: coco: change tsm_class to a const struct
drivers/virt/coco/tsm-core.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
^ permalink raw reply
* Re: [GIT PULL] Trusted Security Manager (PCIe TSM) Update for 7.1
From: Linus Torvalds @ 2026-04-26 16:58 UTC (permalink / raw)
To: Dan Williams; +Cc: linux-coco, linux-kernel
In-Reply-To: <69ee3920ef32e_fe08310024@djbw-dev.notmuch>
On Sun, 26 Apr 2026 at 09:11, Dan Williams <djbw@kernel.org> wrote:
>
> The other motivation for sending this one-patch pull request is to test
> that you have my key with updated email.
I didn't, but honestly, I don't match key issuer ID's or the email
address the pull is sent from anyway.
So I just want the signature to be valid, and from a key I know.
And it all verified with the old key, just with a
issuer "djbw@kernel.org" does not match any User ID
warning.
I wouldn't have cared - or noticed - had you not mentioned it.
But I did update the key so the issuer warning is gone too.
Linus
^ permalink raw reply
* Re: [GIT PULL] Trusted Security Manager (PCIe TSM) Update for 7.1
From: pr-tracker-bot @ 2026-04-26 19:35 UTC (permalink / raw)
To: Dan Williams; +Cc: Linus Torvalds, linux-coco, linux-kernel
In-Reply-To: <69ee3920ef32e_fe08310024@djbw-dev.notmuch>
The pull request you sent on Sun, 26 Apr 2026 09:11:12 -0700:
> git://git.kernel.org/pub/scm/linux/kernel/git/devsec/tsm tags/tsm-for-7.1
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/20b64cf8705a0f6268bb9a320eb6b4c425f3ec6c
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
* Re: [PATCH v2 19/31] iommu/vt-d: Reserve the MSB domain ID bit for the TDX module
From: Xu Yilun @ 2026-04-27 2:50 UTC (permalink / raw)
To: Tian, Kevin
Cc: linux-coco@lists.linux.dev, linux-pci@vger.kernel.org,
Williams, Dan J, x86@kernel.org, Gao, Chao, Jiang, Dave,
baolu.lu@linux.intel.com, Xu, Yilun, Duan, Zhenzhong,
kvm@vger.kernel.org, Edgecombe, Rick P,
dave.hansen@linux.intel.com, kas@kernel.org, Li, Xiaoyao,
Verma, Vishal L, linux-kernel@vger.kernel.org
In-Reply-To: <BN9PR11MB52763F59424C6E4AFD7F63988C2B2@BN9PR11MB5276.namprd11.prod.outlook.com>
> > > btw in patch23 commit msg:
> > >
> > > "
> > > There is no dedicated way to enumerate which IOMMU devices support
> > > trusted operations. The host has to call TDH.IOMMU.SETUP on all IOMMU
> > > devices and tell their trusted capability by the return value.
> > > "
> > >
> > > which implies that ecap_tdxc() alone doesn't really report the capability?
> >
> > Ah, good catch. Let me explain:
> >
> > ecap_tdxc does report the capability. This bit is special cause both
> > trusted part & untrusted part access it.
> >
> > For IOMMU driver (which now handles the untrusted part), it can directly
> > query to this bit and decide what to do.
> >
> > But for tdx-host driver which handles the trusted part, it shouldn't
> > speculate into the IOMMU for capability enumeration. TDX Module has more
> > concerns about trusted capability, including the related I/O stack
>
> I guess "more concerns" means that there are more conditions for
> TDX module to look at beyond ecap_tdxc(), so it's not appropriate
> for tdx-host driver to check ecap alone.
Exactly.
>
> > capabilities e.g. SPDM/IDE cap... So in patch23 I actually mean we
> > don't have an enumeration SEAMCALL for trusted capability, I will
> > refactor that message:
> >
> > There is no dedicated *SEAMCALL* to enumerate which IOMMU devices
> > support
> > trusted operations...
^ permalink raw reply
* Re: [PATCH v2 23/31] coco/tdx-host: Setup all trusted IOMMUs on TDX Connect init
From: Xu Yilun @ 2026-04-27 3:10 UTC (permalink / raw)
To: Tian, Kevin
Cc: linux-coco@lists.linux.dev, linux-pci@vger.kernel.org,
Williams, Dan J, x86@kernel.org, Gao, Chao, Jiang, Dave,
baolu.lu@linux.intel.com, Xu, Yilun, Duan, Zhenzhong,
kvm@vger.kernel.org, Edgecombe, Rick P,
dave.hansen@linux.intel.com, kas@kernel.org, Li, Xiaoyao,
Verma, Vishal L, linux-kernel@vger.kernel.org
In-Reply-To: <BN9PR11MB5276916B241153121738EB208C2B2@BN9PR11MB5276.namprd11.prod.outlook.com>
On Fri, Apr 24, 2026 at 06:54:54AM +0000, Tian, Kevin wrote:
> > From: Xu Yilun <yilun.xu@linux.intel.com>
> > Sent: Wednesday, April 22, 2026 5:27 PM
> >
> > On Thu, Apr 09, 2026 at 07:51:56AM +0000, Tian, Kevin wrote:
> > > > From: Xu Yilun <yilun.xu@linux.intel.com>
> > > > Sent: Saturday, March 28, 2026 12:01 AM
> > > >
> > > > Setup all trusted IOMMUs on TDX Connect initialization and clear all on
> > > > TDX Connect removal.
> > > >
> > > > Trusted IOMMU setup is the pre-condition for all following TDX Connect
> > > > operations such as SPDM/IDE setup. It is more of a platform
> > > > configuration than a standalone IOMMU configuration, so put the
> > > > implementation in tdx-host driver.
> > > >
> > >
> > > not sure what above tries to tell. why is it a platform configuration
> > > when you have seamcalls on each IOMMU?
> >
> > This is to say the TDH.IOMMU.SETUP relates to PCIe SPDM/IDE, it is not
> > just about IOMMU. By identifying the
> >
> > for_each_iommu(iommu)
> > tdh.iommu.setup(iommu)
> >
> > as a platform configuration, it justifies why we trigger this
> > configuration at tdx-host driver probe, rather than in some
> > IOMMU/IOMMUFD API.
>
> iommu drivers also involve PCI, e.g. call pci_enable_ats(), etc.
>
> so having relation to PCIe SPDM/IDE is not an argument of
> platform vs. IOMMU.
OK, I think I could delete the platform vs. IOMMU thing in commit log.
>
> Actually I'm OK to put that logic in tdx-host. Just the explanation
> here doesn't make much sense...
>
^ permalink raw reply
* Re: [PATCH v2 24/31] coco/tdx-host: Add a helper to exchange SPDM messages through DOE
From: Xu Yilun @ 2026-04-27 3:34 UTC (permalink / raw)
To: Tian, Kevin
Cc: linux-coco@lists.linux.dev, linux-pci@vger.kernel.org,
Williams, Dan J, x86@kernel.org, Gao, Chao, Jiang, Dave,
baolu.lu@linux.intel.com, Xu, Yilun, Duan, Zhenzhong,
kvm@vger.kernel.org, Edgecombe, Rick P,
dave.hansen@linux.intel.com, kas@kernel.org, Li, Xiaoyao,
Verma, Vishal L, linux-kernel@vger.kernel.org
In-Reply-To: <BN9PR11MB52767B17432AF4A2622091608C2B2@BN9PR11MB5276.namprd11.prod.outlook.com>
On Fri, Apr 24, 2026 at 07:01:32AM +0000, Tian, Kevin wrote:
> > From: Xu Yilun <yilun.xu@linux.intel.com>
> > Sent: Wednesday, April 22, 2026 5:41 PM
> >
> > On Thu, Apr 09, 2026 at 07:56:06AM +0000, Tian, Kevin wrote:
> > > > From: Xu Yilun <yilun.xu@linux.intel.com>
> > > > Sent: Saturday, March 28, 2026 12:01 AM
> > > > +
> > > > +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)
> > > > +{
> > > > + struct pci_dev *pdev = tlink->pci.base_tsm.pdev;
> > >
> > > call it pci_spdm_msg_exchange() and pass in struct pci_dev directly.
> >
> > I don't think so. There is kernel managed spdm transfer support WIP,
> > which is another topic. We don't want to mix the namespace with that
> > one.
>
> pci_spdm_raw_msg_exchange() then, since you said currently only
> one user i.e. tdx?
>
> If the kernel managed spdm doesn't support the raw format, then there
> won't be conflict.
I think kernel managed spdm APIs should not support the raw format (with
DOE header). SPDM protocol could be run on various transit so make no
sense to have special care for DOE.
So this "raw_msg_exchange" is dedicated for PCI/TSM. Yes there won't be
conflict, but it's important "pci_spdm_" prefix only serve kernel
managed spdm. PCI_TSM managed SPDM has fundamental logical differences
with the kernel managed one.
>
> if it supports (i.e. the 2nd user), then this should be moved to pci core.
>
> >
> > And we also don't name it tsm_spdm_msg_exchange, cause TSM firmwares
> > output different blobs for vendor TSM drivers to transfer. E.g. TDX
> > Module outputs buffers with DOE header & SPDM header, other vendors
> > (AMD IIRC) outputs buffers with only SPDM header. So this function is
> > TDX specific.
> >
>
> TDX is just an user of that. All the logic here is about handling the
> raw format, nothing specific to tdx.
It is special, kernel managed spdm won't expect an all-in-one format,
other vendors either. It is TDX's decision to output this all-in-one
format for OS to transfer.
^ permalink raw reply
* Re: [PATCH v2 30/31] coco/tdx-host: Implement IDE stream setup/teardown
From: Xu Yilun @ 2026-04-27 3:54 UTC (permalink / raw)
To: Tian, Kevin
Cc: linux-coco@lists.linux.dev, linux-pci@vger.kernel.org,
Williams, Dan J, x86@kernel.org, Gao, Chao, Jiang, Dave,
baolu.lu@linux.intel.com, Xu, Yilun, Duan, Zhenzhong,
kvm@vger.kernel.org, Edgecombe, Rick P,
dave.hansen@linux.intel.com, kas@kernel.org, Li, Xiaoyao,
Verma, Vishal L, linux-kernel@vger.kernel.org
In-Reply-To: <BN9PR11MB5276DBD859A8122620EB86CE8C2B2@BN9PR11MB5276.namprd11.prod.outlook.com>
On Fri, Apr 24, 2026 at 07:05:32AM +0000, Tian, Kevin wrote:
> > From: Xu Yilun <yilun.xu@linux.intel.com>
> > Sent: Wednesday, April 22, 2026 5:58 PM
> >
> > On Thu, Apr 09, 2026 at 08:02:33AM +0000, Tian, Kevin wrote:
> > > > From: Xu Yilun <yilun.xu@linux.intel.com>
> > > > Sent: Saturday, March 28, 2026 12:02 AM
> > > >
> > > > Implementation for a most straightforward Selective IDE stream setup.
> > > > Hard code all parameters for Stream Control Register. And no IDE Key
> > > > Refresh support.
> > > >
> > >
> > > 'more straightforward', compared to what?
>
> a typo.
>
> >
> > Actually it is " *most* straightforward", I just mean "very".
>
> When you say "most straightforward", then I want to know what are
> other options to compare. If you think that the thought practice
OK, I think the use of "a most" is somewhat misleading. I don't want to
emphasize on comparison. I just want to give a summary about the
implementation: hard code all parameters, give no option for
configurations, no optional features supported e.g. KEY Refresh.
So is it better I just s/most/very:
Implementation for a very straightforward Selective IDE stream setup...
> leading to the 'most' definition is important, then please elaborate.
>
> otherwise I'd just remove that sentence.
>
^ permalink raw reply
* [PATCH v4 0/3] Enforce host page-size alignment for shared buffers
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 6:31 UTC (permalink / raw)
To: linux-kernel, iommu, linux-coco, linux-arm-kernel, kvmarm
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
Suzuki K Poulose, Thomas Gleixner, Will Deacon
Hi all,
This patch series addresses alignment requirements for buffers shared between
private-memory guests and the host.
When running private-memory guests, the guest kernel must apply additional
constraints when allocating buffers that are shared with the hypervisor. These
shared buffers are also accessed by the host kernel and therefore must be
aligned to the host’s page size.
Architectures such as Arm can tolerate realm physical address space PFNs being
mapped as shared memory, as incorrect accesses are detected and reported as GPC
faults. However, relying on this mechanism alone is unsafe and can still lead to
kernel crashes.
This is particularly likely when guest_memfd allocations are mmapped and
accessed from userspace. Once exposed to userspace, it is not possible to
guarantee that applications will only access the intended 4K shared region
rather than the full 64K page mapped into their address space. Such userspace
addresses may also be passed back into the kernel and accessed via the linear
map, potentially resulting in a GPC fault and a kernel crash.
To address this, the series introduces a new helpers,
mem_decrypt_granule_size() and mem_decrypt_align(), which allows callers to
enforce the required alignment for shared buffers.
Changes from v3:
https://lore.kernel.org/all/20260309102625.2315725-1-aneesh.kumar@kernel.org
* Fix build error reported by kernel test robot <lkp@intel.com>
Changes from v2:
https://lore.kernel.org/all/20251221160920.297689-1-aneesh.kumar@kernel.org
* Rebase to latest kernel
* Consider swiotlb always decrypted and don't align when allocating from swiotlb.
Changes from v1:
* Rename the helper to mem_encrypt_align
* Improve the commit message
* Handle DMA allocations from contiguous memory
* Handle DMA allocations from the pool
* swiotlb is still considered unencrypted. Support for an encrypted swiotlb pool
is left as TODO and is independent of this series.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Steven Price <steven.price@arm.com>
Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
Cc: Thomas Gleixner <tglx@kernel.org>
Cc: Will Deacon <will@kernel.org>
Aneesh Kumar K.V (Arm) (3):
dma-direct: swiotlb: handle swiotlb alloc/free outside
__dma_direct_alloc_pages
swiotlb: dma: its: Enforce host page-size alignment for shared buffers
coco: guest: arm64: Query host IPA-change alignment via RHI
arch/arm64/include/asm/mem_encrypt.h | 3 ++
arch/arm64/include/asm/rhi.h | 24 ++++++++++++
arch/arm64/include/asm/rsi.h | 2 +
arch/arm64/include/asm/rsi_cmds.h | 10 +++++
arch/arm64/include/asm/rsi_smc.h | 7 ++++
arch/arm64/kernel/Makefile | 2 +-
arch/arm64/kernel/rhi.c | 54 ++++++++++++++++++++++++++
arch/arm64/kernel/rsi.c | 13 +++++++
arch/arm64/mm/mem_encrypt.c | 27 +++++++++++--
drivers/irqchip/irq-gic-v3-its.c | 20 ++++++----
include/linux/mem_encrypt.h | 14 +++++++
kernel/dma/contiguous.c | 10 +++++
kernel/dma/direct.c | 58 ++++++++++++++++++++++++----
kernel/dma/pool.c | 4 +-
kernel/dma/swiotlb.c | 21 ++++++----
15 files changed, 240 insertions(+), 29 deletions(-)
create mode 100644 arch/arm64/include/asm/rhi.h
create mode 100644 arch/arm64/kernel/rhi.c
--
2.43.0
^ permalink raw reply
* [PATCH v4 1/3] dma-direct: swiotlb: handle swiotlb alloc/free outside __dma_direct_alloc_pages
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 6:31 UTC (permalink / raw)
To: linux-kernel, iommu, linux-coco, linux-arm-kernel, kvmarm
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260427063108.909019-1-aneesh.kumar@kernel.org>
Move swiotlb allocation out of __dma_direct_alloc_pages() and handle it in
dma_direct_alloc() / dma_direct_alloc_pages().
This is needed for follow-up changes that align shared decrypted buffers to
hypervisor page size. swiotlb pool memory is decrypted as a whole and does
not need per-allocation alignment handling.
swiotlb backing pages are already mapped decrypted by
swiotlb_update_mem_attributes() and rmem_swiotlb_device_init(), so
dma-direct should not call dma_set_decrypted() on allocation nor
dma_set_encrypted() on free for swiotlb-backed memory.
Update alloc/free paths to detect swiotlb-backed pages and skip
encrypt/decrypt transitions for those paths. Keep the existing highmem
rejection in dma_direct_alloc_pages() for swiotlb allocations.
Only for "restricted-dma-pool", we currently set `for_alloc = true`, while
rmem_swiotlb_device_init() decrypts the whole pool up front. This pool is
typically used together with "shared-dma-pool", where the shared region is
accessed after remap/ioremap and the returned address is suitable for
decrypted memory access. So existing code paths remain valid.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
kernel/dma/direct.c | 44 +++++++++++++++++++++++++++++++++++++-------
1 file changed, 37 insertions(+), 7 deletions(-)
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 8f43a930716d..c2a43e4ef902 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -125,9 +125,6 @@ static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
WARN_ON_ONCE(!PAGE_ALIGNED(size));
- if (is_swiotlb_for_alloc(dev))
- return dma_direct_alloc_swiotlb(dev, size);
-
gfp |= dma_direct_optimal_gfp_mask(dev, &phys_limit);
page = dma_alloc_contiguous(dev, size, gfp);
if (page) {
@@ -204,6 +201,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
{
bool remap = false, set_uncached = false;
+ bool mark_mem_decrypt = true;
struct page *page;
void *ret;
@@ -250,11 +248,21 @@ void *dma_direct_alloc(struct device *dev, size_t size,
dma_direct_use_pool(dev, gfp))
return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);
+ if (is_swiotlb_for_alloc(dev)) {
+ page = dma_direct_alloc_swiotlb(dev, size);
+ if (page) {
+ mark_mem_decrypt = false;
+ goto setup_page;
+ }
+ return NULL;
+ }
+
/* we always manually zero the memory once we are done */
page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, true);
if (!page)
return NULL;
+setup_page:
/*
* dma_alloc_contiguous can return highmem pages depending on a
* combination the cma= arguments and per-arch setup. These need to be
@@ -281,7 +289,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
goto out_free_pages;
} else {
ret = page_address(page);
- if (dma_set_decrypted(dev, ret, size))
+ if (mark_mem_decrypt && dma_set_decrypted(dev, ret, size))
goto out_leak_pages;
}
@@ -298,7 +306,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
return ret;
out_encrypt_pages:
- if (dma_set_encrypted(dev, page_address(page), size))
+ if (mark_mem_decrypt && dma_set_encrypted(dev, page_address(page), size))
return NULL;
out_free_pages:
__dma_direct_free_pages(dev, page, size);
@@ -310,6 +318,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
void dma_direct_free(struct device *dev, size_t size,
void *cpu_addr, dma_addr_t dma_addr, unsigned long attrs)
{
+ bool mark_mem_encrypted = true;
unsigned int page_order = get_order(size);
if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) &&
@@ -338,12 +347,15 @@ void dma_direct_free(struct device *dev, size_t size,
dma_free_from_pool(dev, cpu_addr, PAGE_ALIGN(size)))
return;
+ if (swiotlb_find_pool(dev, dma_to_phys(dev, dma_addr)))
+ mark_mem_encrypted = false;
+
if (is_vmalloc_addr(cpu_addr)) {
vunmap(cpu_addr);
} else {
if (IS_ENABLED(CONFIG_ARCH_HAS_DMA_CLEAR_UNCACHED))
arch_dma_clear_uncached(cpu_addr, size);
- if (dma_set_encrypted(dev, cpu_addr, size))
+ if (mark_mem_encrypted && dma_set_encrypted(dev, cpu_addr, size))
return;
}
@@ -359,6 +371,19 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
if (force_dma_unencrypted(dev) && dma_direct_use_pool(dev, gfp))
return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);
+ if (is_swiotlb_for_alloc(dev)) {
+ page = dma_direct_alloc_swiotlb(dev, size);
+ if (!page)
+ return NULL;
+
+ if (PageHighMem(page)) {
+ swiotlb_free(dev, page, size);
+ return NULL;
+ }
+ ret = page_address(page);
+ goto setup_page;
+ }
+
page = __dma_direct_alloc_pages(dev, size, gfp, false);
if (!page)
return NULL;
@@ -366,6 +391,7 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
ret = page_address(page);
if (dma_set_decrypted(dev, ret, size))
goto out_leak_pages;
+setup_page:
memset(ret, 0, size);
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
return page;
@@ -378,13 +404,17 @@ void dma_direct_free_pages(struct device *dev, size_t size,
enum dma_data_direction dir)
{
void *vaddr = page_address(page);
+ bool mark_mem_encrypted = true;
/* If cpu_addr is not from an atomic pool, dma_free_from_pool() fails */
if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) &&
dma_free_from_pool(dev, vaddr, size))
return;
- if (dma_set_encrypted(dev, vaddr, size))
+ if (swiotlb_find_pool(dev, page_to_phys(page)))
+ mark_mem_encrypted = false;
+
+ if (mark_mem_encrypted && dma_set_encrypted(dev, vaddr, size))
return;
__dma_direct_free_pages(dev, page, size);
}
--
2.43.0
^ permalink raw reply related
* [PATCH v4 2/3] swiotlb: dma: its: Enforce host page-size alignment for shared buffers
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 6:31 UTC (permalink / raw)
To: linux-kernel, iommu, linux-coco, linux-arm-kernel, kvmarm
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260427063108.909019-1-aneesh.kumar@kernel.org>
When running private-memory guests, the guest kernel must apply additional
constraints when allocating buffers that are shared with the hypervisor.
These shared buffers are also accessed by the host kernel and therefore
must be aligned to the host’s page size, and have a size that is a multiple
of the host page size.
On non-secure hosts, set_guest_memory_attributes() tracks memory at the
host PAGE_SIZE granularity. This creates a mismatch when the guest applies
attributes at 4K boundaries while the host uses 64K pages. In such cases,
set_guest_memory_attributes() call returns -EINVAL, preventing the
conversion of memory regions from private to shared.
Architectures such as Arm can tolerate realm physical address space
(protected memory) PFNs being mapped as shared memory, as incorrect
accesses are detected and reported as GPC faults. However, relying on this
mechanism is unsafe and can still lead to kernel crashes.
This is particularly likely when guest_memfd allocations are mmapped and
accessed from userspace. Once exposed to userspace, we cannot guarantee
that applications will only access the intended 4K shared region rather
than the full 64K page mapped into their address space. Such userspace
addresses may also be passed back into the kernel and accessed via the
linear map, resulting in a GPC fault and a kernel crash.
With CCA, although Stage-2 mappings managed by the RMM still operate at a
4K granularity, shared pages must nonetheless be aligned to the
host-managed page size and sized as whole host pages to avoid the issues
described above.
Introduce a new helper, mem_decrypt_align(), to allow callers to enforce
the required alignment and size constraints for shared buffers.
The architecture-specific implementation of mem_decrypt_align() will be
provided in a follow-up patch.
Note on restricted-dma-pool:
rmem_swiotlb_device_init() uses reserved-memory regions described by
firmware. Those regions are not changed in-kernel to satisfy host granule
alignment. This is intentional: we do not expect restricted-dma-pool
allocations to be used with CCA. If restricted-dma-pool is intended for CCA
shared use, firmware must provide base/size aligned to the host IPA-change
granule.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/mm/mem_encrypt.c | 19 +++++++++++++++----
drivers/irqchip/irq-gic-v3-its.c | 20 +++++++++++++-------
include/linux/mem_encrypt.h | 14 ++++++++++++++
kernel/dma/contiguous.c | 10 ++++++++++
kernel/dma/direct.c | 16 ++++++++++++++--
kernel/dma/pool.c | 4 +++-
kernel/dma/swiotlb.c | 21 +++++++++++++--------
7 files changed, 82 insertions(+), 22 deletions(-)
diff --git a/arch/arm64/mm/mem_encrypt.c b/arch/arm64/mm/mem_encrypt.c
index ee3c0ab04384..38c62c9e4e74 100644
--- a/arch/arm64/mm/mem_encrypt.c
+++ b/arch/arm64/mm/mem_encrypt.c
@@ -17,8 +17,7 @@
#include <linux/compiler.h>
#include <linux/err.h>
#include <linux/mm.h>
-
-#include <asm/mem_encrypt.h>
+#include <linux/mem_encrypt.h>
static const struct arm64_mem_crypt_ops *crypt_ops;
@@ -33,18 +32,30 @@ int arm64_mem_crypt_ops_register(const struct arm64_mem_crypt_ops *ops)
int set_memory_encrypted(unsigned long addr, int numpages)
{
- if (likely(!crypt_ops) || WARN_ON(!PAGE_ALIGNED(addr)))
+ if (likely(!crypt_ops))
return 0;
+ if (WARN_ON(!IS_ALIGNED(addr, mem_decrypt_granule_size())))
+ return -EINVAL;
+
+ if (WARN_ON(!IS_ALIGNED(numpages << PAGE_SHIFT, mem_decrypt_granule_size())))
+ return -EINVAL;
+
return crypt_ops->encrypt(addr, numpages);
}
EXPORT_SYMBOL_GPL(set_memory_encrypted);
int set_memory_decrypted(unsigned long addr, int numpages)
{
- if (likely(!crypt_ops) || WARN_ON(!PAGE_ALIGNED(addr)))
+ if (likely(!crypt_ops))
return 0;
+ if (WARN_ON(!IS_ALIGNED(addr, mem_decrypt_granule_size())))
+ return -EINVAL;
+
+ if (WARN_ON(!IS_ALIGNED(numpages << PAGE_SHIFT, mem_decrypt_granule_size())))
+ return -EINVAL;
+
return crypt_ops->decrypt(addr, numpages);
}
EXPORT_SYMBOL_GPL(set_memory_decrypted);
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 291d7668cc8d..239d7e3bc16f 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -213,16 +213,17 @@ static gfp_t gfp_flags_quirk;
static struct page *its_alloc_pages_node(int node, gfp_t gfp,
unsigned int order)
{
+ unsigned int new_order;
struct page *page;
int ret = 0;
- page = alloc_pages_node(node, gfp | gfp_flags_quirk, order);
-
+ new_order = get_order(mem_decrypt_align((PAGE_SIZE << order)));
+ page = alloc_pages_node(node, gfp | gfp_flags_quirk, new_order);
if (!page)
return NULL;
ret = set_memory_decrypted((unsigned long)page_address(page),
- 1 << order);
+ 1 << new_order);
/*
* If set_memory_decrypted() fails then we don't know what state the
* page is in, so we can't free it. Instead we leak it.
@@ -241,13 +242,16 @@ static struct page *its_alloc_pages(gfp_t gfp, unsigned int order)
static void its_free_pages(void *addr, unsigned int order)
{
+ int new_order;
+
+ new_order = get_order(mem_decrypt_align((PAGE_SIZE << order)));
/*
* If the memory cannot be encrypted again then we must leak the pages.
* set_memory_encrypted() will already have WARNed.
*/
- if (set_memory_encrypted((unsigned long)addr, 1 << order))
+ if (set_memory_encrypted((unsigned long)addr, 1 << new_order))
return;
- free_pages((unsigned long)addr, order);
+ free_pages((unsigned long)addr, new_order);
}
static struct gen_pool *itt_pool;
@@ -268,11 +272,13 @@ static void *itt_alloc_pool(int node, int size)
if (addr)
break;
- page = its_alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
+ page = its_alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO,
+ get_order(mem_decrypt_granule_size()));
if (!page)
break;
- gen_pool_add(itt_pool, (unsigned long)page_address(page), PAGE_SIZE, node);
+ gen_pool_add(itt_pool, (unsigned long)page_address(page),
+ mem_decrypt_granule_size(), node);
} while (!addr);
return (void *)addr;
diff --git a/include/linux/mem_encrypt.h b/include/linux/mem_encrypt.h
index 07584c5e36fb..1e01c9ac697f 100644
--- a/include/linux/mem_encrypt.h
+++ b/include/linux/mem_encrypt.h
@@ -11,6 +11,8 @@
#define __MEM_ENCRYPT_H__
#ifndef __ASSEMBLY__
+#include <linux/align.h>
+#include <vdso/page.h>
#ifdef CONFIG_ARCH_HAS_MEM_ENCRYPT
@@ -54,6 +56,18 @@
#define dma_addr_canonical(x) (x)
#endif
+#ifndef mem_decrypt_granule_size
+static inline size_t mem_decrypt_granule_size(void)
+{
+ return PAGE_SIZE;
+}
+#endif
+
+static inline size_t mem_decrypt_align(size_t size)
+{
+ return ALIGN(size, mem_decrypt_granule_size());
+}
+
#endif /* __ASSEMBLY__ */
#endif /* __MEM_ENCRYPT_H__ */
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c
index c56004d314dc..2b7ff68be0c4 100644
--- a/kernel/dma/contiguous.c
+++ b/kernel/dma/contiguous.c
@@ -46,6 +46,7 @@
#include <linux/dma-map-ops.h>
#include <linux/cma.h>
#include <linux/nospec.h>
+#include <linux/dma-direct.h>
#ifdef CONFIG_CMA_SIZE_MBYTES
#define CMA_SIZE_MBYTES CONFIG_CMA_SIZE_MBYTES
@@ -374,6 +375,15 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
#ifdef CONFIG_DMA_NUMA_CMA
int nid = dev_to_node(dev);
#endif
+ /*
+ * for untrusted device, we require the dma buffers to be aligned to
+ * the mem_decrypt_align(PAGE_SIZE) so that we can set the memory
+ * attributes correctly.
+ */
+ if (force_dma_unencrypted(dev)) {
+ if (get_order(mem_decrypt_granule_size()) > CONFIG_CMA_ALIGNMENT)
+ return NULL;
+ }
/* CMA can be used only in the context which permits sleeping */
if (!gfpflags_allow_blocking(gfp))
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index c2a43e4ef902..34eccd047e9b 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -257,6 +257,9 @@ void *dma_direct_alloc(struct device *dev, size_t size,
return NULL;
}
+ if (force_dma_unencrypted(dev))
+ size = mem_decrypt_align(size);
+
/* we always manually zero the memory once we are done */
page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO, true);
if (!page)
@@ -350,6 +353,9 @@ void dma_direct_free(struct device *dev, size_t size,
if (swiotlb_find_pool(dev, dma_to_phys(dev, dma_addr)))
mark_mem_encrypted = false;
+ if (mark_mem_encrypted && force_dma_unencrypted(dev))
+ size = mem_decrypt_align(size);
+
if (is_vmalloc_addr(cpu_addr)) {
vunmap(cpu_addr);
} else {
@@ -384,6 +390,9 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
goto setup_page;
}
+ if (force_dma_unencrypted(dev))
+ size = mem_decrypt_align(size);
+
page = __dma_direct_alloc_pages(dev, size, gfp, false);
if (!page)
return NULL;
@@ -414,8 +423,11 @@ void dma_direct_free_pages(struct device *dev, size_t size,
if (swiotlb_find_pool(dev, page_to_phys(page)))
mark_mem_encrypted = false;
- if (mark_mem_encrypted && dma_set_encrypted(dev, vaddr, size))
- return;
+ if (mark_mem_encrypted && force_dma_unencrypted(dev)) {
+ size = mem_decrypt_align(size);
+ if (dma_set_encrypted(dev, vaddr, size))
+ return;
+ }
__dma_direct_free_pages(dev, page, size);
}
diff --git a/kernel/dma/pool.c b/kernel/dma/pool.c
index 2b2fbb709242..b5f10ba3e855 100644
--- a/kernel/dma/pool.c
+++ b/kernel/dma/pool.c
@@ -83,7 +83,9 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
struct page *page = NULL;
void *addr;
int ret = -ENOMEM;
+ unsigned int min_encrypt_order = get_order(mem_decrypt_granule_size());
+ pool_size = mem_decrypt_align(pool_size);
/* Cannot allocate larger than MAX_PAGE_ORDER */
order = min(get_order(pool_size), MAX_PAGE_ORDER);
@@ -94,7 +96,7 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
order, false);
if (!page)
page = alloc_pages(gfp | __GFP_NOWARN, order);
- } while (!page && order-- > 0);
+ } while (!page && order-- > min_encrypt_order);
if (!page)
goto out;
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 9fd73700ddcf..b5cf8cd65e77 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -261,7 +261,7 @@ void __init swiotlb_update_mem_attributes(void)
if (!mem->nslabs || mem->late_alloc)
return;
- bytes = PAGE_ALIGN(mem->nslabs << IO_TLB_SHIFT);
+ bytes = mem_decrypt_align(mem->nslabs << IO_TLB_SHIFT);
set_memory_decrypted((unsigned long)mem->vaddr, bytes >> PAGE_SHIFT);
}
@@ -318,8 +318,8 @@ static void __init *swiotlb_memblock_alloc(unsigned long nslabs,
unsigned int flags,
int (*remap)(void *tlb, unsigned long nslabs))
{
- size_t bytes = PAGE_ALIGN(nslabs << IO_TLB_SHIFT);
void *tlb;
+ size_t bytes = mem_decrypt_align(nslabs << IO_TLB_SHIFT);
/*
* By default allocate the bounce buffer memory from low memory, but
@@ -327,9 +327,9 @@ static void __init *swiotlb_memblock_alloc(unsigned long nslabs,
* memory encryption.
*/
if (flags & SWIOTLB_ANY)
- tlb = memblock_alloc(bytes, PAGE_SIZE);
+ tlb = memblock_alloc(bytes, mem_decrypt_granule_size());
else
- tlb = memblock_alloc_low(bytes, PAGE_SIZE);
+ tlb = memblock_alloc_low(bytes, mem_decrypt_granule_size());
if (!tlb) {
pr_warn("%s: Failed to allocate %zu bytes tlb structure\n",
@@ -338,7 +338,7 @@ static void __init *swiotlb_memblock_alloc(unsigned long nslabs,
}
if (remap && remap(tlb, nslabs) < 0) {
- memblock_free(tlb, PAGE_ALIGN(bytes));
+ memblock_free(tlb, bytes);
pr_warn("%s: Failed to remap %zu bytes\n", __func__, bytes);
return NULL;
}
@@ -460,7 +460,7 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
swiotlb_adjust_nareas(num_possible_cpus());
retry:
- order = get_order(nslabs << IO_TLB_SHIFT);
+ order = get_order(mem_decrypt_align(nslabs << IO_TLB_SHIFT));
nslabs = SLABS_PER_PAGE << order;
while ((SLABS_PER_PAGE << order) > IO_TLB_MIN_SLABS) {
@@ -469,6 +469,8 @@ int swiotlb_init_late(size_t size, gfp_t gfp_mask,
if (vstart)
break;
order--;
+ if (order < get_order(mem_decrypt_granule_size()))
+ break;
nslabs = SLABS_PER_PAGE << order;
retried = true;
}
@@ -536,7 +538,7 @@ void __init swiotlb_exit(void)
pr_info("tearing down default memory pool\n");
tbl_vaddr = (unsigned long)phys_to_virt(mem->start);
- tbl_size = PAGE_ALIGN(mem->end - mem->start);
+ tbl_size = mem_decrypt_align(mem->end - mem->start);
slots_size = PAGE_ALIGN(array_size(sizeof(*mem->slots), mem->nslabs));
set_memory_encrypted(tbl_vaddr, tbl_size >> PAGE_SHIFT);
@@ -572,11 +574,13 @@ void __init swiotlb_exit(void)
*/
static struct page *alloc_dma_pages(gfp_t gfp, size_t bytes, u64 phys_limit)
{
- unsigned int order = get_order(bytes);
+ unsigned int order;
struct page *page;
phys_addr_t paddr;
void *vaddr;
+ bytes = mem_decrypt_align(bytes);
+ order = get_order(bytes);
page = alloc_pages(gfp, order);
if (!page)
return NULL;
@@ -659,6 +663,7 @@ static void swiotlb_free_tlb(void *vaddr, size_t bytes)
dma_free_from_pool(NULL, vaddr, bytes))
return;
+ bytes = mem_decrypt_align(bytes);
/* Intentional leak if pages cannot be encrypted again. */
if (!set_memory_encrypted((unsigned long)vaddr, PFN_UP(bytes)))
__free_pages(virt_to_page(vaddr), get_order(bytes));
--
2.43.0
^ permalink raw reply related
* [PATCH v4 3/3] coco: guest: arm64: Query host IPA-change alignment via RHI
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 6:31 UTC (permalink / raw)
To: linux-kernel, iommu, linux-coco, linux-arm-kernel, kvmarm
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260427063108.909019-1-aneesh.kumar@kernel.org>
Add the Realm Host Interface support needed to query host configuration
from a Realm guest. Define the RHI hostconf SMCs, add rsi_host_call(), and
use them during Realm initialization to retrieve the host IPA-change
alignment size.
Expose that alignment through realm_get_hyp_pagesize() and
mem_decrypt_granule_size() so shared-buffer allocation and
encryption/decryption paths can honor the ipa change page-size requirement.
If the host reports an invalid alignment (when alginment value is not
multiple of 4K), do not enable Realm support.
This provides the host alignment information required by the shared buffer
alignment changes.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/mem_encrypt.h | 3 ++
arch/arm64/include/asm/rhi.h | 24 +++++++++++++
arch/arm64/include/asm/rsi.h | 2 ++
arch/arm64/include/asm/rsi_cmds.h | 10 ++++++
arch/arm64/include/asm/rsi_smc.h | 7 ++++
arch/arm64/kernel/Makefile | 2 +-
arch/arm64/kernel/rhi.c | 54 ++++++++++++++++++++++++++++
arch/arm64/kernel/rsi.c | 13 +++++++
arch/arm64/mm/mem_encrypt.c | 8 +++++
9 files changed, 122 insertions(+), 1 deletion(-)
create mode 100644 arch/arm64/include/asm/rhi.h
create mode 100644 arch/arm64/kernel/rhi.c
diff --git a/arch/arm64/include/asm/mem_encrypt.h b/arch/arm64/include/asm/mem_encrypt.h
index 314b2b52025f..5541911eb028 100644
--- a/arch/arm64/include/asm/mem_encrypt.h
+++ b/arch/arm64/include/asm/mem_encrypt.h
@@ -16,6 +16,9 @@ int arm64_mem_crypt_ops_register(const struct arm64_mem_crypt_ops *ops);
int set_memory_encrypted(unsigned long addr, int numpages);
int set_memory_decrypted(unsigned long addr, int numpages);
+#define mem_decrypt_granule_size mem_decrypt_granule_size
+size_t mem_decrypt_granule_size(void);
+
int realm_register_memory_enc_ops(void);
static inline bool force_dma_unencrypted(struct device *dev)
diff --git a/arch/arm64/include/asm/rhi.h b/arch/arm64/include/asm/rhi.h
new file mode 100644
index 000000000000..0895dd92ea1d
--- /dev/null
+++ b/arch/arm64/include/asm/rhi.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+
+#ifndef __ASM_RHI_H_
+#define __ASM_RHI_H_
+
+#include <linux/types.h>
+
+#define SMC_RHI_CALL(func) \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_STANDARD_HYP,\
+ (func))
+
+unsigned long rhi_get_ipa_change_alignment(void);
+#define RHI_HOSTCONF_VER_1_0 0x10000
+#define RHI_HOSTCONF_VERSION SMC_RHI_CALL(0x004E)
+
+#define __RHI_HOSTCONF_GET_IPA_CHANGE_ALIGNMENT BIT(0)
+#define RHI_HOSTCONF_FEATURES SMC_RHI_CALL(0x004F)
+#define RHI_HOSTCONF_GET_IPA_CHANGE_ALIGNMENT SMC_RHI_CALL(0x0050)
+#endif
diff --git a/arch/arm64/include/asm/rsi.h b/arch/arm64/include/asm/rsi.h
index 88b50d660e85..ae54fb3b1429 100644
--- a/arch/arm64/include/asm/rsi.h
+++ b/arch/arm64/include/asm/rsi.h
@@ -67,4 +67,6 @@ static inline int rsi_set_memory_range_shared(phys_addr_t start,
return rsi_set_memory_range(start, end, RSI_RIPAS_EMPTY,
RSI_CHANGE_DESTROYED);
}
+
+unsigned long realm_get_hyp_pagesize(void);
#endif /* __ASM_RSI_H_ */
diff --git a/arch/arm64/include/asm/rsi_cmds.h b/arch/arm64/include/asm/rsi_cmds.h
index 2c8763876dfb..a341ce0eeda1 100644
--- a/arch/arm64/include/asm/rsi_cmds.h
+++ b/arch/arm64/include/asm/rsi_cmds.h
@@ -159,4 +159,14 @@ static inline unsigned long rsi_attestation_token_continue(phys_addr_t granule,
return res.a0;
}
+static inline unsigned long rsi_host_call(struct rsi_host_call *rhi_call)
+{
+ phys_addr_t addr = virt_to_phys(rhi_call);
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RSI_HOST_CALL, addr, &res);
+
+ return res.a0;
+}
+
#endif /* __ASM_RSI_CMDS_H */
diff --git a/arch/arm64/include/asm/rsi_smc.h b/arch/arm64/include/asm/rsi_smc.h
index e19253f96c94..9ee8b5c7612e 100644
--- a/arch/arm64/include/asm/rsi_smc.h
+++ b/arch/arm64/include/asm/rsi_smc.h
@@ -182,6 +182,13 @@ struct realm_config {
*/
#define SMC_RSI_IPA_STATE_GET SMC_RSI_FID(0x198)
+struct rsi_host_call {
+ union {
+ u16 imm;
+ u64 padding0;
+ };
+ u64 gprs[31];
+} __aligned(0x100);
/*
* Make a Host call.
*
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index fe627100d199..3e72dd9584ed 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -34,7 +34,7 @@ obj-y := debug-monitors.o entry.o irq.o fpsimd.o \
cpufeature.o alternative.o cacheinfo.o \
smp.o smp_spin_table.o topology.o smccc-call.o \
syscall.o proton-pack.o idle.o patching.o pi/ \
- rsi.o jump_label.o
+ rsi.o jump_label.o rhi.o
obj-$(CONFIG_COMPAT) += sys32.o signal32.o \
sys_compat.o
diff --git a/arch/arm64/kernel/rhi.c b/arch/arm64/kernel/rhi.c
new file mode 100644
index 000000000000..7cd6c5102464
--- /dev/null
+++ b/arch/arm64/kernel/rhi.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+
+#include <linux/mm.h>
+#include <asm/rsi.h>
+#include <asm/rhi.h>
+
+/* we need an aligned rhicall for rsi_host_call. slab is not yet ready */
+static struct rsi_host_call hyp_pagesize_rhicall;
+unsigned long rhi_get_ipa_change_alignment(void)
+{
+ long ret;
+ unsigned long ipa_change_align;
+
+ hyp_pagesize_rhicall.imm = 0;
+ hyp_pagesize_rhicall.gprs[0] = RHI_HOSTCONF_VERSION;
+ ret = rsi_host_call(lm_alias(&hyp_pagesize_rhicall));
+ if (ret != RSI_SUCCESS)
+ goto err_out;
+
+ if (hyp_pagesize_rhicall.gprs[0] != RHI_HOSTCONF_VER_1_0)
+ goto err_out;
+
+ hyp_pagesize_rhicall.imm = 0;
+ hyp_pagesize_rhicall.gprs[0] = RHI_HOSTCONF_FEATURES;
+ ret = rsi_host_call(lm_alias(&hyp_pagesize_rhicall));
+ if (ret != RSI_SUCCESS)
+ goto err_out;
+
+ if (!(hyp_pagesize_rhicall.gprs[0] & __RHI_HOSTCONF_GET_IPA_CHANGE_ALIGNMENT))
+ goto err_out;
+
+ hyp_pagesize_rhicall.imm = 0;
+ hyp_pagesize_rhicall.gprs[0] = RHI_HOSTCONF_GET_IPA_CHANGE_ALIGNMENT;
+ ret = rsi_host_call(lm_alias(&hyp_pagesize_rhicall));
+ if (ret != RSI_SUCCESS)
+ goto err_out;
+
+ ipa_change_align = hyp_pagesize_rhicall.gprs[0];
+ /* This error needs special handling in the caller */
+ if (ipa_change_align & (SZ_4K - 1))
+ return 0;
+
+ return ipa_change_align;
+
+err_out:
+ /*
+ * For failure condition assume host is built with 4K page size
+ * and hence ipa change alignment can be guest PAGE_SIZE.
+ */
+ return PAGE_SIZE;
+}
diff --git a/arch/arm64/kernel/rsi.c b/arch/arm64/kernel/rsi.c
index 9e846ce4ef9c..ff735c04e236 100644
--- a/arch/arm64/kernel/rsi.c
+++ b/arch/arm64/kernel/rsi.c
@@ -14,8 +14,10 @@
#include <asm/mem_encrypt.h>
#include <asm/pgtable.h>
#include <asm/rsi.h>
+#include <asm/rhi.h>
static struct realm_config config;
+static unsigned long ipa_change_alignment = PAGE_SIZE;
unsigned long prot_ns_shared;
EXPORT_SYMBOL(prot_ns_shared);
@@ -139,6 +141,11 @@ static int realm_ioremap_hook(phys_addr_t phys, size_t size, pgprot_t *prot)
return 0;
}
+unsigned long realm_get_hyp_pagesize(void)
+{
+ return ipa_change_alignment;
+}
+
void __init arm64_rsi_init(void)
{
if (arm_smccc_1_1_get_conduit() != SMCCC_CONDUIT_SMC)
@@ -147,6 +154,12 @@ void __init arm64_rsi_init(void)
return;
if (WARN_ON(rsi_get_realm_config(&config)))
return;
+
+ ipa_change_alignment = rhi_get_ipa_change_alignment();
+ /* If we don't get a correct alignment response, don't enable realm */
+ if (!ipa_change_alignment)
+ return;
+
prot_ns_shared = __phys_to_pte_val(BIT(config.ipa_bits - 1));
if (arm64_ioremap_prot_hook_register(realm_ioremap_hook))
diff --git a/arch/arm64/mm/mem_encrypt.c b/arch/arm64/mm/mem_encrypt.c
index 38c62c9e4e74..f5d64bc29c20 100644
--- a/arch/arm64/mm/mem_encrypt.c
+++ b/arch/arm64/mm/mem_encrypt.c
@@ -59,3 +59,11 @@ int set_memory_decrypted(unsigned long addr, int numpages)
return crypt_ops->decrypt(addr, numpages);
}
EXPORT_SYMBOL_GPL(set_memory_decrypted);
+
+size_t mem_decrypt_granule_size(void)
+{
+ if (is_realm_world())
+ return max(PAGE_SIZE, realm_get_hyp_pagesize());
+ return PAGE_SIZE;
+}
+EXPORT_SYMBOL_GPL(mem_decrypt_granule_size);
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v4 00/14] coco/TSM: Host-side Arm CCA IDE setup via connect/disconnect callbacks
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 6:51 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Jonathan Cameron, Marc Zyngier,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
This patch series implements the TSM ->connect() and ->disconnect() callbacks
required for the Arm CCA IDE setup as per the RMM 2.0bet1 specification [1].
This patchset includes the host-side flow needed by connect/disconnect,
including:
- DA feature detection helpers
- host TSM callback wiring and IDE stream allocation support
- creation/registration of RMM pdev descriptors
- RMM pdev communication helpers
- pdev stop and teardown helpers for disconnect
- pdev instantiation from the connect path
- public key registration with RMM
To support public-key handling from the device certificate chain, the series
also includes the required X.509 parser updates.
The series builds upon the TSM framework patches posted at [2] and depends on
the KVM CCA patchset [3]. A git repository containing all the related changes is
available at [4].
Testing / Usage
To initiate the IDE setup:
echo tsm0 > /sys/bus/pci/devices/$DEVICE/tsm/connect
To disconnect:
echo tsm0 > /sys/bus/pci/devices/$DEVICE/tsm/disconnect
Changes from v3:
https://lore.kernel.org/all/20260312080129.3483585-1-aneesh.kumar@kernel.org
* updated the patches to follow the RMM 2.0bet1 specification
* reworked the host-side pdev lifecycle to better match the RMM 2.0bet1 flow,
including common pdev state, root-port pdev support, and non-coherent stream
setup and teardown
* split PF0 setup into identity collection and conditional public-key
installation, and gate DA enablement on RMI_FEATURE_REGISTER_2_DA
* added coordinated handling for RMI_DEV_COMM_EXIT_STREAM_WAIT, along with
stream connect/disconnect and stream key refresh/purge support during vdev
teardown
Changes from v2:
rfc-v2 https://lore.kernel.org/all/20251027095602.1154418-1-aneesh.kumar@kernel.org
* rebase to latest kernel and core TSM changes
* Address review feedback.
v1:
rfc-v1 https://lore.kernel.org/all/20250728135216.48084-1-aneesh.kumar@kernel.org
[1] https://developer.arm.com/documentation/den0137/2-0bet1/
[2] https://lore.kernel.org/all/20260303000207.1836586-1-dan.j.williams@intel.com
[3] https://lore.kernel.org/all/20260318155413.793430-1-steven.price@arm.com
[4] https://gitlab.arm.com/linux-arm/linux-cca.git cca/topics/cca-tdisp-upstream-rfc-v4
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Jonathan Cameron <jic23@kernel.org>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Steven Price <steven.price@arm.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Aneesh Kumar K.V (Arm) (11):
coco: host: arm64: Add host TSM callback and IDE stream allocation
support
coco: host: arm64: Create RMM pdev objects for PCI endpoints
coco: host: arm64: Add RMM device communication helpers
coco: host: arm64: Add helper to stop and tear down an RMM pdev
coco: host: arm64: Register device public key with RMM
coco: host: arm64: Initialize RMM pdev state for TDISP IDE connect
coco: host: arm64: Coordinate peer stream waits during pdev
communication
coco: host: arm64: Connect RMM pdev streams for IDE devices
coco: host: arm64: Refcount root-port pdevs used by IDE streams
PCI/TSM: Move CMA DOE mailbox discovery out of
pci_tsm_pf0_constructor()
coco: host: arm64: Add NCOH_SYS stream support for RC endpoints
Lukas Wunner (3):
X.509: Make certificate parser public
X.509: Parse Subject Alternative Name in certificates
X.509: Move certificate length retrieval into new helper
arch/arm64/include/asm/rmi_cmds.h | 85 +++
arch/arm64/include/asm/rmi_smc.h | 168 +++++
crypto/asymmetric_keys/x509_cert_parser.c | 9 +
crypto/asymmetric_keys/x509_loader.c | 38 +-
crypto/asymmetric_keys/x509_parser.h | 42 +-
drivers/crypto/ccp/sev-dev-tsm.c | 13 +
drivers/firmware/smccc/rmm.c | 12 +
drivers/firmware/smccc/rmm.h | 8 +
drivers/firmware/smccc/smccc.c | 1 +
drivers/pci/tsm/core.c | 14 +-
drivers/virt/coco/Kconfig | 2 +
drivers/virt/coco/Makefile | 1 +
drivers/virt/coco/arm-cca-host/Kconfig | 23 +
drivers/virt/coco/arm-cca-host/Makefile | 5 +
drivers/virt/coco/arm-cca-host/arm-cca.c | 494 ++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 867 ++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 217 ++++++
drivers/virt/coco/tdx-host/tdx-host.c | 13 +
include/keys/asymmetric-type.h | 2 +
include/keys/x509-parser.h | 57 ++
20 files changed, 2012 insertions(+), 59 deletions(-)
create mode 100644 drivers/virt/coco/arm-cca-host/Kconfig
create mode 100644 drivers/virt/coco/arm-cca-host/Makefile
create mode 100644 drivers/virt/coco/arm-cca-host/arm-cca.c
create mode 100644 drivers/virt/coco/arm-cca-host/rmi-da.c
create mode 100644 drivers/virt/coco/arm-cca-host/rmi-da.h
create mode 100644 include/keys/x509-parser.h
--
2.43.0
^ permalink raw reply
* [RFC PATCH v4 01/14] coco: host: arm64: Add host TSM callback and IDE stream allocation support
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 6:51 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Jonathan Cameron, Marc Zyngier,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
In-Reply-To: <20260427065121.916615-1-aneesh.kumar@kernel.org>
Register the TSM callback when the DA feature is supported by KVM.
This driver handles IDE stream setup for both the root port and PCIe
endpoints. Root port IDE stream enablement itself is managed by RMM.
In addition, the driver registers pci_tsm_ops with the TSM subsystem.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_smc.h | 2 +
drivers/firmware/smccc/rmm.c | 12 ++
drivers/firmware/smccc/rmm.h | 8 +
drivers/firmware/smccc/smccc.c | 1 +
drivers/virt/coco/Kconfig | 2 +
drivers/virt/coco/Makefile | 1 +
drivers/virt/coco/arm-cca-host/Kconfig | 19 ++
drivers/virt/coco/arm-cca-host/Makefile | 5 +
drivers/virt/coco/arm-cca-host/arm-cca.c | 225 +++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 46 +++++
10 files changed, 321 insertions(+)
create mode 100644 drivers/virt/coco/arm-cca-host/Kconfig
create mode 100644 drivers/virt/coco/arm-cca-host/Makefile
create mode 100644 drivers/virt/coco/arm-cca-host/arm-cca.c
create mode 100644 drivers/virt/coco/arm-cca-host/rmi-da.h
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index fa23818e1b4c..109d6cc6ef37 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -12,6 +12,8 @@
#include <linux/arm-smccc.h>
+#define RMI_DEV_NAME "arm-rmi-dev"
+
#define SMC_RMI_CALL(func) \
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
ARM_SMCCC_SMC_64, \
diff --git a/drivers/firmware/smccc/rmm.c b/drivers/firmware/smccc/rmm.c
index 2a6187df3285..7444cc3a588c 100644
--- a/drivers/firmware/smccc/rmm.c
+++ b/drivers/firmware/smccc/rmm.c
@@ -21,3 +21,15 @@ void __init register_rsi_device(struct platform_device *pdev)
__devm_auxiliary_device_create(&pdev->dev,
"arm_cca_guest", RSI_DEV_NAME, NULL, 0);
}
+
+void __init register_rmi_device(struct platform_device *pdev)
+{
+ struct arm_smccc_res res;
+ unsigned long host_version = RMI_ABI_VERSION(RMI_ABI_MAJOR_VERSION,
+ RMI_ABI_MINOR_VERSION);
+
+ arm_smccc_1_1_invoke(SMC_RMI_VERSION, host_version, &res);
+ if (res.a0 == RMI_SUCCESS)
+ __devm_auxiliary_device_create(&pdev->dev,
+ "arm_cca_host", RMI_DEV_NAME, NULL, 0);
+}
diff --git a/drivers/firmware/smccc/rmm.h b/drivers/firmware/smccc/rmm.h
index a47a650d4f51..37d0d95a099e 100644
--- a/drivers/firmware/smccc/rmm.h
+++ b/drivers/firmware/smccc/rmm.h
@@ -6,12 +6,20 @@
#ifdef CONFIG_ARM64
#include <asm/rsi_cmds.h>
+#include <asm/rmi_smc.h>
+
void __init register_rsi_device(struct platform_device *pdev);
+void __init register_rmi_device(struct platform_device *pdev);
#else
static void __init register_rsi_device(struct platform_device *pdev)
{
+}
+
+static void __init register_rmi_device(struct platform_device *pdev)
+{
+
}
#endif
#endif
diff --git a/drivers/firmware/smccc/smccc.c b/drivers/firmware/smccc/smccc.c
index fc9b44b7c687..2bf2d59e686d 100644
--- a/drivers/firmware/smccc/smccc.c
+++ b/drivers/firmware/smccc/smccc.c
@@ -97,6 +97,7 @@ static int __init smccc_devices_init(void)
* the required SMCCC function IDs at a supported revision.
*/
register_rsi_device(pdev);
+ register_rmi_device(pdev);
}
if (smccc_trng_available) {
diff --git a/drivers/virt/coco/Kconfig b/drivers/virt/coco/Kconfig
index f7691f64fbe3..1cbc2134f9ea 100644
--- a/drivers/virt/coco/Kconfig
+++ b/drivers/virt/coco/Kconfig
@@ -19,5 +19,7 @@ endif
source "drivers/virt/coco/tdx-host/Kconfig"
+source "drivers/virt/coco/arm-cca-host/Kconfig"
+
config TSM
bool
diff --git a/drivers/virt/coco/Makefile b/drivers/virt/coco/Makefile
index b323b0ae4f82..f2310c34daf9 100644
--- a/drivers/virt/coco/Makefile
+++ b/drivers/virt/coco/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_INTEL_TDX_HOST) += tdx-host/
obj-$(CONFIG_ARM_CCA_GUEST) += arm-cca-guest/
obj-$(CONFIG_TSM) += tsm-core.o
obj-$(CONFIG_TSM_GUEST) += guest/
+obj-$(CONFIG_ARM_CCA_HOST) += arm-cca-host/
diff --git a/drivers/virt/coco/arm-cca-host/Kconfig b/drivers/virt/coco/arm-cca-host/Kconfig
new file mode 100644
index 000000000000..efe40d61d5d8
--- /dev/null
+++ b/drivers/virt/coco/arm-cca-host/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# TSM (TEE Security Manager) host drivers
+#
+config ARM_CCA_HOST
+ tristate "Arm CCA Host driver"
+ depends on ARM64
+ depends on PCI
+ depends on KVM
+ select PCI_TSM
+ select AUXILIARY_BUS
+
+ help
+ ARM CCA RMM firmware is the trusted runtime that enforces memory
+ isolation and security for confidential computing on ARM. This driver
+ provides the interface for communicating with RMM to support secure
+ device assignment.
+
+ If you choose 'M' here, this module will be called arm-cca-host.
diff --git a/drivers/virt/coco/arm-cca-host/Makefile b/drivers/virt/coco/arm-cca-host/Makefile
new file mode 100644
index 000000000000..c236827f002c
--- /dev/null
+++ b/drivers/virt/coco/arm-cca-host/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+obj-$(CONFIG_ARM_CCA_HOST) += arm-cca-host.o
+
+arm-cca-host-y += arm-cca.o
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
new file mode 100644
index 000000000000..67f7e80106e8
--- /dev/null
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/pci-tsm.h>
+#include <linux/pci-ide.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/tsm.h>
+#include <linux/vmalloc.h>
+#include <linux/cleanup.h>
+
+#include "rmi-da.h"
+
+/* Total number of stream id supported at root port level */
+#define MAX_STREAM_ID 256
+
+static struct pci_tsm *cca_tsm_pci_probe(struct tsm_dev *tsm_dev, struct pci_dev *pdev)
+{
+ int ret;
+
+ if (!is_pci_tsm_pf0(pdev)) {
+ struct cca_host_fn_dsc *fn_dsc __free(kfree) =
+ kzalloc(sizeof(*fn_dsc), GFP_KERNEL);
+
+ if (!fn_dsc)
+ return NULL;
+
+ ret = pci_tsm_link_constructor(pdev, &fn_dsc->pci, tsm_dev);
+ if (ret)
+ return NULL;
+
+ return &no_free_ptr(fn_dsc)->pci;
+ }
+
+ if (!pdev->ide_cap)
+ return NULL;
+
+ struct cca_host_pf0_ep_dsc *pf0_ep_dsc __free(kfree) =
+ kzalloc(sizeof(*pf0_ep_dsc), GFP_KERNEL);
+ if (!pf0_ep_dsc)
+ return NULL;
+
+ ret = pci_tsm_pf0_constructor(pdev, &pf0_ep_dsc->pci, tsm_dev);
+ if (ret)
+ return NULL;
+
+ pci_dbg(pdev, "tsm enabled\n");
+ return &no_free_ptr(pf0_ep_dsc)->pci.base_tsm;
+}
+
+static void cca_tsm_pci_remove(struct pci_tsm *tsm)
+{
+ struct pci_dev *pdev = tsm->pdev;
+
+ if (is_pci_tsm_pf0(pdev)) {
+ struct cca_host_pf0_ep_dsc *pf0_ep_dsc = to_cca_pf0_ep_dsc(pdev);
+
+ pci_tsm_pf0_destructor(&pf0_ep_dsc->pci);
+ kfree(pf0_ep_dsc);
+ } else {
+ kfree(to_cca_fn_dsc(pdev));
+ }
+}
+
+/* For now global for simplicity. Protected by pci_tsm_rwsem */
+static DECLARE_BITMAP(cca_stream_ids, MAX_STREAM_ID);
+static int alloc_stream_id(struct pci_host_bridge *hb)
+{
+ int stream_id;
+
+redo_alloc:
+ stream_id = find_first_zero_bit(cca_stream_ids, MAX_STREAM_ID);
+ if (stream_id == MAX_STREAM_ID)
+ return stream_id;
+
+ if (ida_exists(&hb->ide_stream_ids_ida, stream_id)) {
+ /* mark the stream allocated in the global bitmap. */
+ set_bit(stream_id, cca_stream_ids);
+ goto redo_alloc;
+ }
+ return stream_id;
+}
+
+static inline bool cca_pdev_need_sel_ide_streams(struct pci_dev *pdev)
+{
+ return pci_pcie_type(pdev) == PCI_EXP_TYPE_ENDPOINT;
+}
+
+static int cca_tsm_connect(struct pci_dev *pdev)
+{
+ struct pci_dev *rp = pcie_find_root_port(pdev);
+ struct cca_host_pf0_ep_dsc *pf0_ep_dsc;
+ struct pci_ide *ide;
+ int ret, stream_id = 0;
+
+ /* Only function 0 supports connect in host */
+ if (WARN_ON(!is_pci_tsm_pf0(pdev)))
+ return -EIO;
+
+ pf0_ep_dsc = to_cca_pf0_ep_dsc(pdev);
+ if (cca_pdev_need_sel_ide_streams(pdev)) {
+ /* Allocate stream id */
+ stream_id = alloc_stream_id(pci_find_host_bridge(pdev->bus));
+ if (stream_id == MAX_STREAM_ID)
+ return -EBUSY;
+ set_bit(stream_id, cca_stream_ids);
+
+ ide = pci_ide_stream_alloc(pdev);
+ if (!ide) {
+ ret = -ENOMEM;
+ goto err_stream_alloc;
+ }
+
+ pf0_ep_dsc->sel_stream = ide;
+ ide->stream_id = stream_id;
+ ret = pci_ide_stream_register(ide);
+ if (ret)
+ goto err_stream;
+ /*
+ * 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);
+ pci_ide_stream_setup(rp, ide);
+
+ ret = tsm_ide_stream_register(ide);
+ if (ret)
+ goto err_tsm;
+
+ /*
+ * Once ide is setup, enable the stream at the endpoint
+ * Root port will be done by RMM
+ */
+ pci_ide_stream_enable(pdev, ide);
+ }
+ return 0;
+
+err_tsm:
+ if (cca_pdev_need_sel_ide_streams(pdev)) {
+ pci_ide_stream_teardown(rp, ide);
+ pci_ide_stream_teardown(pdev, ide);
+ pci_ide_stream_unregister(ide);
+ }
+err_stream:
+ if (cca_pdev_need_sel_ide_streams(pdev))
+ pci_ide_stream_free(ide);
+ pf0_ep_dsc->sel_stream = NULL;
+err_stream_alloc:
+ clear_bit(stream_id, cca_stream_ids);
+
+ return ret;
+}
+
+static void cca_tsm_disconnect(struct pci_dev *pdev)
+{
+ int stream_id;
+ struct pci_ide *ide;
+ struct cca_host_pf0_ep_dsc *pf0_ep_dsc;
+
+ pf0_ep_dsc = to_cca_pf0_ep_dsc(pdev);
+ if (!pf0_ep_dsc)
+ return;
+
+ if (cca_pdev_need_sel_ide_streams(pdev)) {
+ ide = pf0_ep_dsc->sel_stream;
+ stream_id = ide->stream_id;
+
+ pci_ide_stream_release(ide);
+ pf0_ep_dsc->sel_stream = NULL;
+ clear_bit(stream_id, cca_stream_ids);
+ }
+
+}
+
+static struct pci_tsm_ops cca_link_pci_ops = {
+ .probe = cca_tsm_pci_probe,
+ .remove = cca_tsm_pci_remove,
+ .connect = cca_tsm_connect,
+ .disconnect = cca_tsm_disconnect,
+};
+
+static void cca_link_tsm_remove(void *tsm_dev)
+{
+ tsm_unregister(tsm_dev);
+}
+
+static int cca_link_tsm_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct tsm_dev *tsm_dev;
+
+ if (!rmm_has_reg2_feature(RMI_FEATURE_REGISTER_2_DA))
+ return -ENODEV;
+
+ tsm_dev = tsm_register(&adev->dev, &cca_link_pci_ops);
+ if (IS_ERR(tsm_dev))
+ return PTR_ERR(tsm_dev);
+
+ return devm_add_action_or_reset(&adev->dev, cca_link_tsm_remove,
+ tsm_dev);
+}
+
+static const struct auxiliary_device_id cca_link_tsm_id_table[] = {
+ { .name = KBUILD_MODNAME "." RMI_DEV_NAME },
+ {}
+};
+MODULE_DEVICE_TABLE(auxiliary, cca_link_tsm_id_table);
+
+static struct auxiliary_driver cca_link_tsm_driver = {
+ .probe = cca_link_tsm_probe,
+ .id_table = cca_link_tsm_id_table,
+};
+module_auxiliary_driver(cca_link_tsm_driver);
+MODULE_IMPORT_NS("PCI_IDE");
+MODULE_AUTHOR("Aneesh Kumar <aneesh.kumar@kernel.org>");
+MODULE_DESCRIPTION("ARM CCA Host TSM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
new file mode 100644
index 000000000000..4abc7ad159e5
--- /dev/null
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+
+#ifndef _VIRT_COCO_RMM_DA_H_
+#define _VIRT_COCO_RMM_DA_H_
+
+#include <linux/pci.h>
+#include <linux/pci-ide.h>
+#include <linux/pci-tsm.h>
+#include <asm/rmi_cmds.h>
+#include <asm/rmi_smc.h>
+
+/**
+ * struct cca_host_pf0_ep_dsc - PF0 endpoint device security context.
+ * @pci: Physical Function 0 TDISP link context
+ * @sel_stream: Selective IDE Stream descriptor
+ */
+struct cca_host_pf0_ep_dsc {
+ struct pci_tsm_pf0 pci;
+ struct pci_ide *sel_stream;
+};
+
+struct cca_host_fn_dsc {
+ struct pci_tsm pci;
+};
+
+static inline struct cca_host_pf0_ep_dsc *to_cca_pf0_ep_dsc(struct pci_dev *pdev)
+{
+ struct pci_tsm *tsm = pdev->tsm;
+
+ if (!tsm || !is_pci_tsm_pf0(pdev))
+ return NULL;
+
+ return container_of(tsm, struct cca_host_pf0_ep_dsc, pci.base_tsm);
+}
+
+static inline struct cca_host_fn_dsc *to_cca_fn_dsc(struct pci_dev *pdev)
+{
+ struct pci_tsm *tsm = pdev->tsm;
+
+ return container_of(tsm, struct cca_host_fn_dsc, pci);
+}
+
+#endif
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v4 02/14] coco: host: arm64: Create RMM pdev objects for PCI endpoints
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 6:51 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Jonathan Cameron, Marc Zyngier,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
In-Reply-To: <20260427065121.916615-1-aneesh.kumar@kernel.org>
Add the RMI definitions needed for pdev management, including the pdev
state enum, parameter layout, and helpers for RMI_PDEV_CREATE and
RMI_PDEV_GET_STATE.
Introduce a host-side pdev descriptor and cca_pdev_create() to
allocate and delegate the backing granule, populate the pdev parameters
from the PCI endpoint, and issue RMI_PDEV_CREATE to the RMM.
The new helper stores the created RMM pdev handle in the PF0 endpoint
descriptor preparing the device for later IDE/TDISP setup.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 10 ++
arch/arm64/include/asm/rmi_smc.h | 49 ++++++++
drivers/virt/coco/arm-cca-host/Makefile | 2 +-
drivers/virt/coco/arm-cca-host/rmi-da.c | 151 ++++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 26 ++++
5 files changed, 237 insertions(+), 1 deletion(-)
create mode 100644 drivers/virt/coco/arm-cca-host/rmi-da.c
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 2901fc84d245..d23a0590c7ee 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -726,4 +726,14 @@ static inline int rmi_rtt_unmap_unprotected(unsigned long rd,
return res.a0;
}
+static inline unsigned long rmi_pdev_get_state(unsigned long pdev_phys, enum rmi_pdev_state *state)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_PDEV_GET_STATE, pdev_phys, &res);
+
+ *state = res.a1;
+ return res.a0;
+}
+
#endif /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 109d6cc6ef37..94bcaf3e7e68 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -429,4 +429,53 @@ struct rec_run {
struct rec_exit exit;
};
+enum rmi_pdev_state {
+ RMI_PDEV_NEW,
+ RMI_PDEV_NEEDS_KEY,
+ RMI_PDEV_HAS_KEY,
+ RMI_PDEV_READY,
+ RMI_PDEV_STOPPED,
+ RMI_PDEV_ERROR,
+};
+
+#define RMI_PDEV_FLAGS_SPDM BIT(0)
+#define RMI_PDEV_FLAGS_CATEGORY_MASK GENMASK(2, 1)
+#define RMI_PDEV_FLAGS_CATEGORY_SHIFT 1
+#define RMI_PDEV_FLAGS_P2P BIT(3)
+
+#define RMI_PDEV_FLAGS_CATEGORY_ROOT_PORT 0x0
+#define RMI_PDEV_FLAGS_CATEGORY_OFF_CHIP_EP 0x1
+#define RMI_PDEV_FLAGS_CATEGORY_ON_CHIP_EP 0x2
+#define RMI_PDEV_FLAGS_CATEGORY_CMEM 0x3
+
+#define RMI_HASH_SHA_256 0x0
+#define RMI_HASH_SHA_512 0x1
+#define RMI_HASH_SHA_384 0x2
+
+struct rmi_pdev_params {
+ union {
+ struct {
+ u64 flags;
+ u64 pdev_id;
+ //u64 rc_id;
+ u64 routing_id;
+ u64 id_index;
+ union {
+ u16 rid_base;
+ u8 padding1[8];
+ };
+ union {
+ u16 rid_top;
+ u8 padding2[8];
+ };
+ union {
+ u8 hash_algo;
+ u8 padding3[8];
+ };
+ u64 max_vdevs_order;
+ };
+ u8 padding5[0x1000];
+ };
+};
+
#endif /* __ASM_RMI_SMC_H */
diff --git a/drivers/virt/coco/arm-cca-host/Makefile b/drivers/virt/coco/arm-cca-host/Makefile
index c236827f002c..d48e8940af46 100644
--- a/drivers/virt/coco/arm-cca-host/Makefile
+++ b/drivers/virt/coco/arm-cca-host/Makefile
@@ -2,4 +2,4 @@
#
obj-$(CONFIG_ARM_CCA_HOST) += arm-cca-host.o
-arm-cca-host-y += arm-cca.o
+arm-cca-host-y += arm-cca.o rmi-da.o
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
new file mode 100644
index 000000000000..8fb5d286fd82
--- /dev/null
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+
+#include <linux/pci.h>
+#include <linux/pci-ecam.h>
+#include <asm/rmi_cmds.h>
+
+#include "rmi-da.h"
+
+static int pci_ide_segment(struct pci_dev *pdev)
+{
+ if (pdev->fm_enabled)
+ return pci_domain_nr(pdev->bus);
+ return 0;
+}
+
+static unsigned int pci_get_max_rid(struct pci_dev *pdev)
+{
+ int fn;
+ int max_rid;
+ int slot = PCI_SLOT(pdev->devfn);
+
+ for (fn = 0; fn < 8; fn++) {
+ struct pci_dev *fn_dev;
+
+ fn_dev = pci_get_slot(pdev->bus, PCI_DEVFN(slot, fn));
+ if (!fn_dev)
+ continue;
+
+ max_rid = PCI_DEVFN(slot, fn);
+ pci_dev_put(fn_dev);
+ }
+ return max_rid;
+}
+
+static int init_pdev_params(struct pci_dev *pdev, struct rmi_pdev_params *params)
+{
+ int rid;
+ unsigned long category;
+ struct pci_config_window *cfg = pdev->bus->sysdata;
+
+ /* check we are ECAM compliant */
+ if (!pdev->bus->ops->map_bus)
+ return -EINVAL;
+
+ switch (pci_pcie_type(pdev)) {
+ case PCI_EXP_TYPE_ENDPOINT: {
+ struct cca_host_pf0_ep_dsc *pf0_ep_dsc = to_cca_pf0_ep_dsc(pdev);
+
+ /* Endpoint needs DOE mailbox */
+ if (!pf0_ep_dsc->pci.doe_mb)
+ return -EINVAL;
+
+ params->flags = RMI_PDEV_FLAGS_SPDM;
+ category = RMI_PDEV_FLAGS_CATEGORY_OFF_CHIP_EP;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ params->flags |= (category << RMI_PDEV_FLAGS_CATEGORY_SHIFT);
+ /* assign the ep device with RMM */
+ rid = pci_dev_id(pdev);
+ params->pdev_id = cfg->res.start | rid;
+ // ecam window base FIXME!!
+ //params->pdev_id = rid;
+ //params->rc_id = cfg->res.start;
+ params->routing_id = pci_ide_segment(pdev);
+ /* slot number for certificate chain default to zero */
+ params->id_index = 0;
+ params->hash_algo = RMI_HASH_SHA_256;
+ /* no multi function device here. */
+ params->rid_base = rid;
+ params->rid_top = pci_get_max_rid(pdev) + 1;
+ // FIXME!! what is this?
+ params->max_vdevs_order = 10;
+ return 0;
+}
+
+static inline int rmi_pdev_create(unsigned long pdev_phys,
+ unsigned long pdev_params_phys, unsigned long *rmi_ret)
+{
+
+ struct rmi_sro_state *sro __free(sro) =
+ rmi_sro_init(SMC_RMI_PDEV_CREATE, pdev_phys, pdev_params_phys);
+ if (!sro)
+ return -ENOMEM;
+
+ *rmi_ret = rmi_sro_execute(sro);
+
+ return 0;
+}
+
+int cca_pdev_create(struct pci_dev *pci_dev)
+{
+ int ret;
+ void *rmm_pdev;
+ bool should_free = true;
+ phys_addr_t rmm_pdev_phys;
+ struct rmi_pdev_params *params;
+ struct cca_host_pdev_dsc *pdev_dsc = to_cca_pdev_dsc(pci_dev);
+
+ rmm_pdev = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!rmm_pdev)
+ return -ENOMEM;
+
+ rmm_pdev_phys = virt_to_phys(rmm_pdev);
+ if (rmi_delegate_page(rmm_pdev_phys)) {
+ ret = -EIO;
+ goto err_granule_delegate;
+ }
+
+ params = (struct rmi_pdev_params *)get_zeroed_page(GFP_KERNEL);
+ if (!params) {
+ ret = -ENOMEM;
+ goto err_param_alloc;
+ }
+
+ ret = init_pdev_params(pci_dev, params);
+ if (ret)
+ goto err_init_pdev_params;
+
+ {
+ unsigned long rmi_ret;
+
+ ret = rmi_pdev_create(rmm_pdev_phys, virt_to_phys(params),
+ &rmi_ret);
+ if (ret || rmi_ret) {
+ if (!ret)
+ ret = -EIO;
+ goto err_init_pdev_params;
+ }
+ }
+
+ pdev_dsc->rmm_pdev = rmm_pdev;
+ free_page((unsigned long)params);
+ return 0;
+
+err_init_pdev_params:
+ free_page((unsigned long)params);
+err_param_alloc:
+ if (rmi_undelegate_page(rmm_pdev_phys))
+ should_free = false;
+err_granule_delegate:
+ if (should_free)
+ free_page((unsigned long)rmm_pdev);
+ return ret;
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 4abc7ad159e5..de67f10ce20e 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -12,13 +12,26 @@
#include <asm/rmi_cmds.h>
#include <asm/rmi_smc.h>
+/**
+ * struct cca_host_pdev_dsc - Common RMM pdev context
+ * @rmm_pdev: Delegated page backing the RMM pdev object
+ * @object_lock: Serializes access to the RMM pdev object and PF0/TDI caches
+ */
+struct cca_host_pdev_dsc {
+ void *rmm_pdev;
+ /* lock kept here to simplify the generic lock/unlock paths. */
+ struct mutex object_lock;
+};
+
/**
* struct cca_host_pf0_ep_dsc - PF0 endpoint device security context.
* @pci: Physical Function 0 TDISP link context
+ * @pdev: pdev communication context
* @sel_stream: Selective IDE Stream descriptor
*/
struct cca_host_pf0_ep_dsc {
struct pci_tsm_pf0 pci;
+ struct cca_host_pdev_dsc pdev;
struct pci_ide *sel_stream;
};
@@ -43,4 +56,17 @@ static inline struct cca_host_fn_dsc *to_cca_fn_dsc(struct pci_dev *pdev)
return container_of(tsm, struct cca_host_fn_dsc, pci);
}
+static inline struct cca_host_pdev_dsc *to_cca_pdev_dsc(struct pci_dev *pdev)
+{
+ struct cca_host_pf0_ep_dsc *pf0_ep_dsc;
+
+ pf0_ep_dsc = to_cca_pf0_ep_dsc(pdev);
+ if (pf0_ep_dsc)
+ return &pf0_ep_dsc->pdev;
+
+ return NULL;
+}
+
+int cca_pdev_create(struct pci_dev *pdev);
+
#endif
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v4 03/14] coco: host: arm64: Add RMM device communication helpers
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 6:51 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Jonathan Cameron, Marc Zyngier,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
In-Reply-To: <20260427065121.916615-1-aneesh.kumar@kernel.org>
- add SMCCC IDs/wrappers for RMI_PDEV_COMMUNICATE/RMI_PDEV_ABORT
- describe the RMM device-communication ABI (struct rmi_dev_comm_*,
cache flags, protocol/object IDs, busy error code)
- track per-PF0 communication state (buffers, workqueue, cache metadata) and
serialize access behind object_lock
- plumb a DOE/SPDM worker plus shared helpers that submit the SMCCC call,
cache multi-part responses, and handle retries/abort
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 20 ++
arch/arm64/include/asm/rmi_smc.h | 60 +++++
drivers/virt/coco/arm-cca-host/arm-cca.c | 50 ++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 276 +++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 65 ++++++
5 files changed, 471 insertions(+)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index d23a0590c7ee..6664c439173f 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -736,4 +736,24 @@ static inline unsigned long rmi_pdev_get_state(unsigned long pdev_phys, enum rmi
return res.a0;
}
+static inline unsigned long rmi_pdev_communicate(unsigned long pdev_phys,
+ unsigned long pdev_comm_data_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_PDEV_COMMUNICATE,
+ pdev_phys, pdev_comm_data_phys, &res);
+
+ return res.a0;
+}
+
+static inline unsigned long rmi_pdev_abort(unsigned long pdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_PDEV_ABORT, pdev_phys, &res);
+
+ return res.a0;
+}
+
#endif /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 94bcaf3e7e68..9056a7639667 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -478,4 +478,64 @@ struct rmi_pdev_params {
};
};
+#define RMI_DEV_COMM_EXIT_CACHE_REQ BIT(0)
+#define RMI_DEV_COMM_EXIT_CACHE_RSP BIT(1)
+#define RMI_DEV_COMM_EXIT_SEND BIT(2)
+#define RMI_DEV_COMM_EXIT_WAIT BIT(3)
+#define RMI_DEV_COMM_EXIT_RSP_RESET BIT(4)
+#define RMI_DEV_COMM_EXIT_MULTI BIT(5)
+
+#define RMI_DEV_COMM_NONE 0
+#define RMI_DEV_COMM_RESPONSE 1
+#define RMI_DEV_COMM_ERROR 2
+
+#define RMI_PROTOCOL_SPDM 0
+#define RMI_PROTOCOL_SECURE_SPDM 1
+
+#define RMI_DEV_VCA 0
+#define RMI_DEV_CERTIFICATE 1
+#define RMI_DEV_MEASUREMENTS 2
+#define RMI_DEV_INTERFACE_REPORT 3
+
+struct rmi_dev_comm_enter {
+ union {
+ u8 status;
+ u64 padding0;
+ };
+ u64 req_addr;
+ u64 resp_addr;
+ u64 resp_len;
+};
+
+struct rmi_dev_comm_exit {
+ u64 flags;
+ u64 req_cache_offset;
+ u64 req_cache_len;
+ u64 rsp_cache_offset;
+ u64 rsp_cache_len;
+ union {
+ u8 cache_obj_id;
+ u64 padding0;
+ };
+
+ union {
+ u8 protocol;
+ u64 padding1;
+ };
+ u64 req_delay;
+ u64 req_len;
+ u64 rsp_timeout;
+};
+
+struct rmi_dev_comm_data {
+ union { /* 0x0 */
+ struct rmi_dev_comm_enter enter;
+ u8 padding0[0x800];
+ };
+ union { /* 0x800 */
+ struct rmi_dev_comm_exit exit;
+ u8 padding1[0x800];
+ };
+};
+
#endif /* __ASM_RMI_SMC_H */
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index 67f7e80106e8..3c854aab95cc 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -46,6 +46,7 @@ static struct pci_tsm *cca_tsm_pci_probe(struct tsm_dev *tsm_dev, struct pci_dev
ret = pci_tsm_pf0_constructor(pdev, &pf0_ep_dsc->pci, tsm_dev);
if (ret)
return NULL;
+ mutex_init(&pf0_ep_dsc->pdev.object_lock);
pci_dbg(pdev, "tsm enabled\n");
return &no_free_ptr(pf0_ep_dsc)->pci.base_tsm;
@@ -65,6 +66,55 @@ static void cca_tsm_pci_remove(struct pci_tsm *tsm)
}
}
+static __maybe_unused int init_dev_communication_buffers(struct pci_dev *pdev,
+ struct cca_host_comm_data *comm_data)
+{
+ int ret = -ENOMEM;
+
+ comm_data->io_params = (struct rmi_dev_comm_data *)get_zeroed_page(GFP_KERNEL);
+ if (!comm_data->io_params)
+ goto err_out;
+
+ comm_data->rsp_buff = (void *)__get_free_page(GFP_KERNEL);
+ if (!comm_data->rsp_buff)
+ goto err_res_buff;
+
+ comm_data->req_buff = (void *)__get_free_page(GFP_KERNEL);
+ if (!comm_data->req_buff)
+ goto err_req_buff;
+
+ comm_data->work_queue = alloc_ordered_workqueue("%s %s DEV_COMM", 0,
+ dev_bus_name(&pdev->dev),
+ pci_name(pdev));
+ if (!comm_data->work_queue)
+ goto err_work_queue;
+
+ comm_data->io_params->enter.status = RMI_DEV_COMM_NONE;
+ comm_data->io_params->enter.resp_addr = virt_to_phys(comm_data->rsp_buff);
+ comm_data->io_params->enter.req_addr = virt_to_phys(comm_data->req_buff);
+ comm_data->io_params->enter.resp_len = 0;
+
+ return 0;
+
+err_work_queue:
+ free_page((unsigned long)comm_data->req_buff);
+err_req_buff:
+ free_page((unsigned long)comm_data->rsp_buff);
+err_res_buff:
+ free_page((unsigned long)comm_data->io_params);
+err_out:
+ return ret;
+}
+
+static inline void free_dev_communication_buffers(struct cca_host_comm_data *comm_data)
+{
+ destroy_workqueue(comm_data->work_queue);
+
+ free_page((unsigned long)comm_data->req_buff);
+ free_page((unsigned long)comm_data->rsp_buff);
+ free_page((unsigned long)comm_data->io_params);
+}
+
/* For now global for simplicity. Protected by pci_tsm_rwsem */
static DECLARE_BITMAP(cca_stream_ids, MAX_STREAM_ID);
static int alloc_stream_id(struct pci_host_bridge *hb)
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 8fb5d286fd82..dc159d9f2c24 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -5,6 +5,8 @@
#include <linux/pci.h>
#include <linux/pci-ecam.h>
+#include <linux/pci-doe.h>
+#include <linux/delay.h>
#include <asm/rmi_cmds.h>
#include "rmi-da.h"
@@ -149,3 +151,277 @@ int cca_pdev_create(struct pci_dev *pci_dev)
free_page((unsigned long)rmm_pdev);
return ret;
}
+
+static int doe_send_req_resp(struct pci_tsm *tsm)
+{
+ int data_obj_type;
+ struct cca_host_comm_data *comm_data = to_cca_comm_data(tsm->pdev);
+ struct rmi_dev_comm_exit *io_exit = &comm_data->io_params->exit;
+ u8 protocol = io_exit->protocol;
+
+ if (protocol == RMI_PROTOCOL_SPDM)
+ data_obj_type = PCI_DOE_FEATURE_CMA;
+ else if (protocol == RMI_PROTOCOL_SECURE_SPDM)
+ data_obj_type = PCI_DOE_FEATURE_SSESSION;
+ else
+ return -EINVAL;
+
+ /* delay the send */
+ if (io_exit->req_delay)
+ fsleep(io_exit->req_delay);
+
+ return pci_tsm_doe_transfer(tsm->dsm_dev, data_obj_type,
+ comm_data->req_buff, io_exit->req_len,
+ comm_data->rsp_buff, PAGE_SIZE);
+}
+
+static inline bool pending_dev_communicate(struct rmi_dev_comm_exit *io_exit)
+{
+ bool pending = io_exit->flags & (RMI_DEV_COMM_EXIT_CACHE_REQ |
+ RMI_DEV_COMM_EXIT_CACHE_RSP |
+ RMI_DEV_COMM_EXIT_SEND |
+ RMI_DEV_COMM_EXIT_WAIT |
+ RMI_DEV_COMM_EXIT_MULTI);
+ return pending;
+}
+
+static inline gfp_t cache_obj_id_to_gfp_flags(u8 cache_obj_id)
+{
+ /* These two cache objects are system objects. */
+ if (cache_obj_id == RMI_DEV_VCA || cache_obj_id == RMI_DEV_CERTIFICATE)
+ return GFP_KERNEL;
+ /* rest are per TDI which is associated to a VM */
+ return GFP_KERNEL_ACCOUNT;
+}
+
+static int _do_dev_communicate(enum dev_comm_type type, struct pci_tsm *tsm)
+{
+ unsigned long rmi_ret;
+ gfp_t cache_alloc_flags;
+ int nbytes, cp_len;
+ struct cache_object **cache_objp, *cache_obj;
+ struct cca_host_pdev_dsc *pdev_dsc = to_cca_pdev_dsc(tsm->dsm_dev);
+ struct cca_host_comm_data *comm_data = to_cca_comm_data(tsm->pdev);
+ struct rmi_dev_comm_enter *io_enter = &comm_data->io_params->enter;
+ struct rmi_dev_comm_exit *io_exit = &comm_data->io_params->exit;
+
+redo_communicate:
+
+ if (type == PDEV_COMMUNICATE)
+ rmi_ret = rmi_pdev_communicate(virt_to_phys(pdev_dsc->rmm_pdev),
+ virt_to_phys(comm_data->io_params));
+ else
+ rmi_ret = RMI_ERROR_INPUT;
+ if (rmi_ret != RMI_SUCCESS) {
+ if (rmi_ret == RMI_BUSY)
+ return -EBUSY;
+ return -EIO;
+ }
+
+ if (io_exit->flags & RMI_DEV_COMM_EXIT_CACHE_REQ ||
+ io_exit->flags & RMI_DEV_COMM_EXIT_CACHE_RSP) {
+ struct cca_host_pf0_ep_dsc *pf0_ep_dsc = to_cca_pf0_ep_dsc(tsm->dsm_dev);
+
+ if (!pf0_ep_dsc) {
+ WARN(1,
+ "Device communication got cache request on wrong device\n");
+ return -EINVAL;
+ }
+
+ switch (io_exit->cache_obj_id) {
+ case RMI_DEV_VCA:
+ cache_objp = &pf0_ep_dsc->vca;
+ break;
+ case RMI_DEV_CERTIFICATE:
+ cache_objp = &pf0_ep_dsc->cert_chain.cache;
+ break;
+ default:
+ return -EINVAL;
+ }
+ cache_obj = *cache_objp;
+ cache_alloc_flags = cache_obj_id_to_gfp_flags(io_exit->cache_obj_id);
+ int cache_remaining;
+
+ if (io_exit->flags & RMI_DEV_COMM_EXIT_CACHE_REQ)
+ cp_len = io_exit->req_cache_len;
+ else
+ cp_len = io_exit->rsp_cache_len;
+
+ /* response and request len should be <= SZ_4k */
+ if (cp_len > CACHE_CHUNK_SIZE)
+ return -EINVAL;
+
+ /* new allocation */
+ if (!cache_obj) {
+ int obj_size = struct_size(cache_obj, buf,
+ CACHE_CHUNK_SIZE);
+
+ cache_obj = kvmalloc(obj_size, cache_alloc_flags);
+ if (!cache_obj)
+ return -ENOMEM;
+
+ cache_obj->size = CACHE_CHUNK_SIZE;
+ cache_obj->offset = 0;
+ *cache_objp = cache_obj;
+ }
+
+ cache_remaining = cache_obj->size - cache_obj->offset;
+ if (cp_len > cache_remaining) {
+ struct cache_object *new_obj;
+ int new_size = struct_size(cache_obj, buf,
+ cache_obj->size +
+ CACHE_CHUNK_SIZE);
+
+ if (cache_obj->size + CACHE_CHUNK_SIZE > MAX_CACHE_OBJ_SIZE)
+ return -EINVAL;
+
+ new_obj = kvrealloc(cache_obj, new_size, cache_alloc_flags);
+ if (!new_obj)
+ return -ENOMEM;
+ new_obj->size = cache_obj->size + CACHE_CHUNK_SIZE;
+ *cache_objp = new_obj;
+ }
+
+ /* cache object can change above. */
+ cache_obj = *cache_objp;
+ }
+
+
+ if (io_exit->flags & RMI_DEV_COMM_EXIT_CACHE_REQ) {
+ memcpy(cache_obj->buf + cache_obj->offset,
+ (comm_data->req_buff + io_exit->req_cache_offset), io_exit->req_cache_len);
+ cache_obj->offset += io_exit->req_cache_len;
+ }
+
+ if (io_exit->flags & RMI_DEV_COMM_EXIT_CACHE_RSP) {
+ memcpy(cache_obj->buf + cache_obj->offset,
+ (comm_data->rsp_buff + io_exit->rsp_cache_offset), io_exit->rsp_cache_len);
+ cache_obj->offset += io_exit->rsp_cache_len;
+ }
+
+ /*
+ * wait for last packet request from RMM.
+ * We should not find this because our device communication is synchronous
+ */
+ if (io_exit->flags & RMI_DEV_COMM_EXIT_WAIT)
+ return -EIO;
+
+ /* next packet to send */
+ if (io_exit->flags & RMI_DEV_COMM_EXIT_SEND) {
+ nbytes = doe_send_req_resp(tsm);
+ if (nbytes < 0) {
+ /* report error back to RMM */
+ io_enter->status = RMI_DEV_COMM_ERROR;
+ } else {
+ /* send response back to RMM */
+ io_enter->resp_len = nbytes;
+ io_enter->status = RMI_DEV_COMM_RESPONSE;
+ }
+ } else {
+ /* no data transmitted => no data received */
+ io_enter->resp_len = 0;
+ io_enter->status = RMI_DEV_COMM_NONE;
+ }
+
+ if (pending_dev_communicate(io_exit))
+ goto redo_communicate;
+
+ return 0;
+}
+
+static int do_dev_communicate(enum dev_comm_type type,
+ struct pci_tsm *tsm, unsigned long error_state)
+{
+ int ret, state = error_state;
+ struct rmi_dev_comm_enter *io_enter;
+ struct cca_host_pdev_dsc *pdev_dsc = to_cca_pdev_dsc(tsm->dsm_dev);
+
+ io_enter = &pdev_dsc->comm_data.io_params->enter;
+ io_enter->resp_len = 0;
+ io_enter->status = RMI_DEV_COMM_NONE;
+
+ ret = _do_dev_communicate(type, tsm);
+ if (ret) {
+ if (type == PDEV_COMMUNICATE)
+ rmi_pdev_abort(virt_to_phys(pdev_dsc->rmm_pdev));
+ } else {
+ /*
+ * Some device communication error will transition the
+ * device to error state. Report that.
+ */
+ if (type == PDEV_COMMUNICATE) {
+ if (rmi_pdev_get_state(virt_to_phys(pdev_dsc->rmm_pdev),
+ (enum rmi_pdev_state *)&state))
+ state = error_state;
+ }
+ }
+
+ if (state == error_state)
+ pci_err(tsm->pdev, "device communication error\n");
+
+ return state;
+}
+
+static int wait_for_dev_state(enum dev_comm_type type, struct pci_tsm *tsm,
+ unsigned long target_state, unsigned long error_state)
+{
+ int state;
+
+ do {
+ state = do_dev_communicate(type, tsm, error_state);
+
+ if (state == target_state || state == error_state)
+ return state;
+ } while (1);
+
+ /* can't reach */
+ return error_state;
+}
+
+static int wait_for_pdev_state(struct pci_tsm *tsm, enum rmi_pdev_state target_state)
+{
+ return wait_for_dev_state(PDEV_COMMUNICATE, tsm, target_state, RMI_PDEV_ERROR);
+}
+
+static void pdev_state_transition_workfn(struct work_struct *work)
+{
+ unsigned long state;
+ struct pci_tsm *tsm;
+ struct dev_comm_work *setup_work;
+ struct cca_host_pdev_dsc *pdev_dsc;
+
+ setup_work = container_of(work, struct dev_comm_work, work);
+ tsm = setup_work->tsm;
+ pdev_dsc = to_cca_pdev_dsc(tsm->dsm_dev);
+
+ guard(mutex)(&pdev_dsc->object_lock);
+ state = wait_for_pdev_state(tsm, setup_work->target_state);
+ WARN_ON(state != setup_work->target_state);
+}
+
+static int __maybe_unused submit_pdev_state_transition_work(struct pci_dev *pdev,
+ enum rmi_pdev_state target_state)
+{
+ enum rmi_pdev_state state;
+ struct dev_comm_work comm_work;
+ struct cca_host_pdev_dsc *pdev_dsc = to_cca_pdev_dsc(pdev);
+ struct cca_host_comm_data *comm_data = to_cca_comm_data(pdev);
+
+ INIT_WORK_ONSTACK(&comm_work.work, pdev_state_transition_workfn);
+ comm_work.tsm = pdev->tsm;
+ comm_work.target_state = target_state;
+
+ queue_work(comm_data->work_queue, &comm_work.work);
+
+ flush_work(&comm_work.work);
+ destroy_work_on_stack(&comm_work.work);
+
+ /* check if we reached target state */
+ if (rmi_pdev_get_state(virt_to_phys(pdev_dsc->rmm_pdev), &state))
+ return -EIO;
+
+ if (state != target_state)
+ /* no specific error for this */
+ return -1;
+ return 0;
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index de67f10ce20e..9f72ff8f28bf 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -9,15 +9,46 @@
#include <linux/pci.h>
#include <linux/pci-ide.h>
#include <linux/pci-tsm.h>
+#include <linux/sizes.h>
#include <asm/rmi_cmds.h>
#include <asm/rmi_smc.h>
+#define MAX_CACHE_OBJ_SIZE SZ_16M
+#define CACHE_CHUNK_SIZE SZ_4K
+struct cache_object {
+ int size;
+ int offset;
+ u8 buf[] __counted_by(size);
+};
+
+struct dev_comm_work {
+ struct pci_tsm *tsm;
+ int target_state;
+ struct work_struct work;
+};
+
+struct cca_host_comm_data {
+ void *rsp_buff;
+ void *req_buff;
+ struct rmi_dev_comm_data *io_params;
+ /*
+ * Only one device communication request can be active at
+ * a time. This limitation comes from using the DOE mailbox
+ * at the pdev level. Requests such as get_measurements may
+ * span multiple mailbox messages, which must not be
+ * interleaved with other SPDM requests.
+ */
+ struct workqueue_struct *work_queue;
+};
+
/**
* struct cca_host_pdev_dsc - Common RMM pdev context
+ * @comm_data: Shared device communication state for the DSM-owned pdev
* @rmm_pdev: Delegated page backing the RMM pdev object
* @object_lock: Serializes access to the RMM pdev object and PF0/TDI caches
*/
struct cca_host_pdev_dsc {
+ struct cca_host_comm_data comm_data;
void *rmm_pdev;
/* lock kept here to simplify the generic lock/unlock paths. */
struct mutex object_lock;
@@ -28,17 +59,33 @@ struct cca_host_pdev_dsc {
* @pci: Physical Function 0 TDISP link context
* @pdev: pdev communication context
* @sel_stream: Selective IDE Stream descriptor
+ * @cert_chain: cetrificate chain
+ * @vca: SPDM's Version-Capabilities-Algorithms cache object
*/
struct cca_host_pf0_ep_dsc {
struct pci_tsm_pf0 pci;
struct cca_host_pdev_dsc pdev;
struct pci_ide *sel_stream;
+
+ struct {
+ struct cache_object *cache;
+
+ void *public_key;
+ size_t public_key_size;
+
+ bool valid;
+ } cert_chain;
+ struct cache_object *vca;
};
struct cca_host_fn_dsc {
struct pci_tsm pci;
};
+enum dev_comm_type {
+ PDEV_COMMUNICATE = 0x1,
+};
+
static inline struct cca_host_pf0_ep_dsc *to_cca_pf0_ep_dsc(struct pci_dev *pdev)
{
struct pci_tsm *tsm = pdev->tsm;
@@ -67,6 +114,24 @@ static inline struct cca_host_pdev_dsc *to_cca_pdev_dsc(struct pci_dev *pdev)
return NULL;
}
+static inline struct cca_host_comm_data *to_cca_comm_data(struct pci_dev *pdev)
+{
+ struct cca_host_pdev_dsc *pdev_dsc;
+
+ pdev_dsc = to_cca_pdev_dsc(pdev);
+ if (pdev_dsc)
+ return &pdev_dsc->comm_data;
+
+ if (!pdev->tsm || !pdev->tsm->dsm_dev)
+ return NULL;
+
+ pdev_dsc = to_cca_pdev_dsc(pdev->tsm->dsm_dev);
+ if (pdev_dsc)
+ return &pdev_dsc->comm_data;
+
+ return NULL;
+}
+
int cca_pdev_create(struct pci_dev *pdev);
#endif
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v4 04/14] coco: host: arm64: Add helper to stop and tear down an RMM pdev
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 6:51 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Jonathan Cameron, Marc Zyngier,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
In-Reply-To: <20260427065121.916615-1-aneesh.kumar@kernel.org>
Add helper to stop and tear down an RMM pdev
- describe the RMI_PDEV_STOP/RMI_PDEV_DESTROY SMC IDs and provide
wrappers in rmi_cmds.h
- implement pdev_stop_and_destroy() so the host driver stops the pdev,
waits for it to reach RMI_PDEV_STOPPED and destroys it
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 9 +++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 47 ++++++++++++++++++++++++-
drivers/virt/coco/arm-cca-host/rmi-da.h | 1 +
3 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 6664c439173f..8024e9d89e55 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -756,4 +756,13 @@ static inline unsigned long rmi_pdev_abort(unsigned long pdev_phys)
return res.a0;
}
+static inline unsigned long rmi_pdev_stop(unsigned long pdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_PDEV_STOP, pdev_phys, &res);
+
+ return res.a0;
+}
+
#endif /* __ASM_RMI_CMDS_H */
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index dc159d9f2c24..8a43a1f1c036 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -399,7 +399,7 @@ static void pdev_state_transition_workfn(struct work_struct *work)
WARN_ON(state != setup_work->target_state);
}
-static int __maybe_unused submit_pdev_state_transition_work(struct pci_dev *pdev,
+static int submit_pdev_state_transition_work(struct pci_dev *pdev,
enum rmi_pdev_state target_state)
{
enum rmi_pdev_state state;
@@ -425,3 +425,48 @@ static int __maybe_unused submit_pdev_state_transition_work(struct pci_dev *pdev
return -1;
return 0;
}
+
+static inline int rmi_pdev_destroy(unsigned long pdev_phys,
+ unsigned long *rmi_ret)
+{
+ struct rmi_sro_state *sro __free(sro) =
+ rmi_sro_init(SMC_RMI_PDEV_DESTROY, pdev_phys);
+ if (!sro)
+ return -ENOMEM;
+
+ *rmi_ret = rmi_sro_execute(sro);
+
+ return 0;
+}
+
+void cca_pdev_stop_and_destroy(struct pci_dev *pdev)
+{
+ int ret;
+ unsigned long rmi_ret;
+ struct cca_host_pdev_dsc *pdev_dsc = to_cca_pdev_dsc(pdev);
+ struct cca_host_pf0_ep_dsc *pf0_ep_dsc = to_cca_pf0_ep_dsc(pdev);
+ phys_addr_t rmm_pdev_phys = virt_to_phys(pdev_dsc->rmm_pdev);
+
+ if (WARN_ON(rmi_pdev_stop(rmm_pdev_phys)))
+ return;
+
+ ret = submit_pdev_state_transition_work(pdev, RMI_PDEV_STOPPED);
+ if (ret)
+ return;
+
+ ret = rmi_pdev_destroy(rmm_pdev_phys, &rmi_ret);
+ if (WARN_ON(ret || rmi_ret))
+ return;
+
+ if (pf0_ep_dsc) {
+ kfree(pf0_ep_dsc->cert_chain.public_key);
+ kvfree(pf0_ep_dsc->cert_chain.cache);
+ kvfree(pf0_ep_dsc->vca);
+ pf0_ep_dsc->cert_chain.cache = NULL;
+ pf0_ep_dsc->vca = NULL;
+ }
+
+ if (!rmi_undelegate_page(rmm_pdev_phys))
+ free_page((unsigned long)pdev_dsc->rmm_pdev);
+ pdev_dsc->rmm_pdev = NULL;
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 9f72ff8f28bf..784eb1fff95d 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -133,5 +133,6 @@ static inline struct cca_host_comm_data *to_cca_comm_data(struct pci_dev *pdev)
}
int cca_pdev_create(struct pci_dev *pdev);
+void cca_pdev_stop_and_destroy(struct pci_dev *pdev);
#endif
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v4 05/14] X.509: Make certificate parser public
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 6:51 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Jonathan Cameron, Marc Zyngier,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun, Lukas Wunner, Ilpo Järvinen, Jonathan Cameron
In-Reply-To: <20260427065121.916615-1-aneesh.kumar@kernel.org>
From: Lukas Wunner <lukas@wunner.de>
The upcoming support for PCI device authentication with CMA-SPDM
(PCIe r6.1 sec 6.31) requires validating the Subject Alternative Name
in X.509 certificates.
High-level functions for X.509 parsing such as key_create_or_update()
throw away the internal, low-level struct x509_certificate after
extracting the struct public_key and public_key_signature from it.
The Subject Alternative Name is thus inaccessible when using those
functions.
Afford CMA-SPDM access to the Subject Alternative Name by making struct
x509_certificate public, together with the functions for parsing an
X.509 certificate into such a struct and freeing such a struct.
The private header file x509_parser.h previously included <linux/time.h>
for the definition of time64_t. That definition was since moved to
<linux/time64.h> by commit 361a3bf00582 ("time64: Add time64.h header
and define struct timespec64"), so adjust the #include directive as part
of the move to the new public header file <keys/x509-parser.h>.
No functional change intended.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
crypto/asymmetric_keys/x509_parser.h | 42 +--------------------
include/keys/x509-parser.h | 55 ++++++++++++++++++++++++++++
2 files changed, 56 insertions(+), 41 deletions(-)
create mode 100644 include/keys/x509-parser.h
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
index b7aeebdddb36..39f1521b773d 100644
--- a/crypto/asymmetric_keys/x509_parser.h
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -5,51 +5,11 @@
* Written by David Howells (dhowells@redhat.com)
*/
-#include <linux/cleanup.h>
-#include <linux/time.h>
-#include <crypto/public_key.h>
-#include <keys/asymmetric-type.h>
-#include <crypto/sha2.h>
-
-struct x509_certificate {
- struct x509_certificate *next;
- struct x509_certificate *signer; /* Certificate that signed this one */
- struct public_key *pub; /* Public key details */
- struct public_key_signature *sig; /* Signature parameters */
- u8 sha256[SHA256_DIGEST_SIZE]; /* Hash for blacklist purposes */
- char *issuer; /* Name of certificate issuer */
- char *subject; /* Name of certificate subject */
- struct asymmetric_key_id *id; /* Issuer + Serial number */
- struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */
- time64_t valid_from;
- time64_t valid_to;
- const void *tbs; /* Signed data */
- unsigned tbs_size; /* Size of signed data */
- unsigned raw_sig_size; /* Size of signature */
- const void *raw_sig; /* Signature data */
- const void *raw_serial; /* Raw serial number in ASN.1 */
- unsigned raw_serial_size;
- unsigned raw_issuer_size;
- const void *raw_issuer; /* Raw issuer name in ASN.1 */
- const void *raw_subject; /* Raw subject name in ASN.1 */
- unsigned raw_subject_size;
- unsigned raw_skid_size;
- const void *raw_skid; /* Raw subjectKeyId in ASN.1 */
- unsigned index;
- bool seen; /* Infinite recursion prevention */
- bool verified;
- bool self_signed; /* T if self-signed (check unsupported_sig too) */
- bool unsupported_sig; /* T if signature uses unsupported crypto */
- bool blacklisted;
-};
+#include <keys/x509-parser.h>
/*
* x509_cert_parser.c
*/
-extern void x509_free_certificate(struct x509_certificate *cert);
-DEFINE_FREE(x509_free_certificate, struct x509_certificate *,
- if (!IS_ERR(_T)) x509_free_certificate(_T))
-extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
extern int x509_decode_time(time64_t *_t, size_t hdrlen,
unsigned char tag,
const unsigned char *value, size_t vlen);
diff --git a/include/keys/x509-parser.h b/include/keys/x509-parser.h
new file mode 100644
index 000000000000..8b68e720693a
--- /dev/null
+++ b/include/keys/x509-parser.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* X.509 certificate parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifndef _KEYS_X509_PARSER_H
+#define _KEYS_X509_PARSER_H
+
+#include <linux/cleanup.h>
+#include <linux/time.h>
+#include <crypto/public_key.h>
+#include <keys/asymmetric-type.h>
+#include <crypto/sha2.h>
+
+struct x509_certificate {
+ struct x509_certificate *next;
+ struct x509_certificate *signer; /* Certificate that signed this one */
+ struct public_key *pub; /* Public key details */
+ struct public_key_signature *sig; /* Signature parameters */
+ u8 sha256[SHA256_DIGEST_SIZE]; /* Hash for blacklist purposes */
+ char *issuer; /* Name of certificate issuer */
+ char *subject; /* Name of certificate subject */
+ struct asymmetric_key_id *id; /* Issuer + Serial number */
+ struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */
+ time64_t valid_from;
+ time64_t valid_to;
+ const void *tbs; /* Signed data */
+ unsigned tbs_size; /* Size of signed data */
+ unsigned raw_sig_size; /* Size of signature */
+ const void *raw_sig; /* Signature data */
+ const void *raw_serial; /* Raw serial number in ASN.1 */
+ unsigned raw_serial_size;
+ unsigned raw_issuer_size;
+ const void *raw_issuer; /* Raw issuer name in ASN.1 */
+ const void *raw_subject; /* Raw subject name in ASN.1 */
+ unsigned raw_subject_size;
+ unsigned raw_skid_size;
+ const void *raw_skid; /* Raw subjectKeyId in ASN.1 */
+ unsigned index;
+ bool seen; /* Infinite recursion prevention */
+ bool verified;
+ bool self_signed; /* T if self-signed (check unsupported_sig too) */
+ bool unsupported_sig; /* T if signature uses unsupported crypto */
+ bool blacklisted;
+};
+
+struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
+void x509_free_certificate(struct x509_certificate *cert);
+
+DEFINE_FREE(x509_free_certificate, struct x509_certificate *,
+ if (!IS_ERR(_T)) x509_free_certificate(_T))
+
+#endif /* _KEYS_X509_PARSER_H */
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v4 06/14] X.509: Parse Subject Alternative Name in certificates
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 6:51 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Jonathan Cameron, Marc Zyngier,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun, Lukas Wunner, Wilfred Mallawa, Ilpo Järvinen,
Jonathan Cameron
In-Reply-To: <20260427065121.916615-1-aneesh.kumar@kernel.org>
From: Lukas Wunner <lukas@wunner.de>
The upcoming support for PCI device authentication with CMA-SPDM
(PCIe r6.1 sec 6.31) requires validating the Subject Alternative Name
in X.509 certificates.
Store a pointer to the Subject Alternative Name upon parsing for
consumption by CMA-SPDM.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Reviewed-by: Wilfred Mallawa <wilfred.mallawa@wdc.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Acked-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
crypto/asymmetric_keys/x509_cert_parser.c | 9 +++++++++
include/keys/x509-parser.h | 2 ++
2 files changed, 11 insertions(+)
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 37e4fb9da106..d81b7de4236c 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -596,6 +596,15 @@ int x509_process_extension(void *context, size_t hdrlen,
return 0;
}
+ if (ctx->last_oid == OID_subjectAltName) {
+ if (ctx->cert->raw_san)
+ return -EBADMSG;
+
+ ctx->cert->raw_san = v;
+ ctx->cert->raw_san_size = vlen;
+ return 0;
+ }
+
if (ctx->last_oid == OID_keyUsage) {
/*
* Get hold of the keyUsage bit string
diff --git a/include/keys/x509-parser.h b/include/keys/x509-parser.h
index 8b68e720693a..4e6a05a8c7a6 100644
--- a/include/keys/x509-parser.h
+++ b/include/keys/x509-parser.h
@@ -38,6 +38,8 @@ struct x509_certificate {
unsigned raw_subject_size;
unsigned raw_skid_size;
const void *raw_skid; /* Raw subjectKeyId in ASN.1 */
+ const void *raw_san; /* Raw subjectAltName in ASN.1 */
+ unsigned raw_san_size;
unsigned index;
bool seen; /* Infinite recursion prevention */
bool verified;
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v4 07/14] X.509: Move certificate length retrieval into new helper
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 6:51 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Jonathan Cameron, Marc Zyngier,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun, Lukas Wunner, Jonathan Cameron
In-Reply-To: <20260427065121.916615-1-aneesh.kumar@kernel.org>
From: Lukas Wunner <lukas@wunner.de>
The upcoming in-kernel SPDM library (Security Protocol and Data Model,
https://www.dmtf.org/dsp/DSP0274) needs to retrieve the length from
ASN.1 DER-encoded X.509 certificates.
Such code already exists in x509_load_certificate_list(), so move it
into a new helper for reuse by SPDM.
Export the helper so that SPDM can be tristate. (Some upcoming users of
the SPDM libray may be modular, such as SCSI and ATA.)
No functional change intended.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
crypto/asymmetric_keys/x509_loader.c | 38 +++++++++++++++++++---------
include/keys/asymmetric-type.h | 2 ++
2 files changed, 28 insertions(+), 12 deletions(-)
diff --git a/crypto/asymmetric_keys/x509_loader.c b/crypto/asymmetric_keys/x509_loader.c
index a41741326998..25ff027fad1d 100644
--- a/crypto/asymmetric_keys/x509_loader.c
+++ b/crypto/asymmetric_keys/x509_loader.c
@@ -4,28 +4,42 @@
#include <linux/key.h>
#include <keys/asymmetric-type.h>
+ssize_t x509_get_certificate_length(const u8 *p, unsigned long buflen)
+{
+ ssize_t plen;
+
+ /* Each cert begins with an ASN.1 SEQUENCE tag and must be more
+ * than 256 bytes in size.
+ */
+ if (buflen < 4)
+ return -EINVAL;
+
+ if (p[0] != 0x30 &&
+ p[1] != 0x82)
+ return -EINVAL;
+
+ plen = (p[2] << 8) | p[3];
+ plen += 4;
+ if (plen > buflen)
+ return -EINVAL;
+
+ return plen;
+}
+EXPORT_SYMBOL_GPL(x509_get_certificate_length);
+
int x509_load_certificate_list(const u8 cert_list[],
const unsigned long list_size,
const struct key *keyring)
{
key_ref_t key;
const u8 *p, *end;
- size_t plen;
+ ssize_t plen;
p = cert_list;
end = p + list_size;
while (p < end) {
- /* Each cert begins with an ASN.1 SEQUENCE tag and must be more
- * than 256 bytes in size.
- */
- if (end - p < 4)
- goto dodgy_cert;
- if (p[0] != 0x30 &&
- p[1] != 0x82)
- goto dodgy_cert;
- plen = (p[2] << 8) | p[3];
- plen += 4;
- if (plen > end - p)
+ plen = x509_get_certificate_length(p, end - p);
+ if (plen < 0)
goto dodgy_cert;
key = key_create_or_update(make_key_ref(keyring, 1),
diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h
index 1b91c8f98688..301efa952e26 100644
--- a/include/keys/asymmetric-type.h
+++ b/include/keys/asymmetric-type.h
@@ -84,6 +84,8 @@ extern struct key *find_asymmetric_key(struct key *keyring,
const struct asymmetric_key_id *id_2,
bool partial);
+ssize_t x509_get_certificate_length(const u8 *p, unsigned long buflen);
+
int x509_load_certificate_list(const u8 cert_list[], const unsigned long list_size,
const struct key *keyring);
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v4 08/14] coco: host: arm64: Register device public key with RMM
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 6:51 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Jonathan Cameron, Marc Zyngier,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
In-Reply-To: <20260427065121.916615-1-aneesh.kumar@kernel.org>
- Introduce the SMC_RMI_PDEV_SET_PUBKEY helper and the associated struct
rmi_public_key_params so the host can hand the device’s public key to
the RMM.
- Parse the certificate chain cached during SPDM session setup, extract the
final certificate’s public key, and recognise RSA-3072, ECDSA-P256, and
ECDSA-P384 keys before calling into the RMM.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 9 ++
arch/arm64/include/asm/rmi_smc.h | 17 +++
drivers/virt/coco/arm-cca-host/Kconfig | 4 +
drivers/virt/coco/arm-cca-host/rmi-da.c | 155 ++++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 2 +
5 files changed, 187 insertions(+)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 8024e9d89e55..00e0a08e17a6 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -765,4 +765,13 @@ static inline unsigned long rmi_pdev_stop(unsigned long pdev_phys)
return res.a0;
}
+static inline unsigned long rmi_pdev_set_pubkey(unsigned long pdev_phys, unsigned long key_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_PDEV_SET_PUBKEY, pdev_phys, key_phys, &res);
+
+ return res.a0;
+}
+
#endif /* __ASM_RMI_CMDS_H */
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 9056a7639667..7a5d57a8be7a 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -538,4 +538,21 @@ struct rmi_dev_comm_data {
};
};
+#define RMI_SIG_RSASSA_3072 0
+#define RMI_SIG_ECDSA_P256 1
+#define RMI_SIG_ECDSA_P384 2
+
+struct rmi_public_key_params {
+ union {
+ struct {
+ u8 public_key[1024];
+ u8 metadata[1024];
+ u64 public_key_len;
+ u64 metadata_len;
+ u8 rmi_signature_algorithm;
+ };
+ u8 padding[0x1000];
+ };
+};
+
#endif /* __ASM_RMI_SMC_H */
diff --git a/drivers/virt/coco/arm-cca-host/Kconfig b/drivers/virt/coco/arm-cca-host/Kconfig
index efe40d61d5d8..c5076e2b4eb5 100644
--- a/drivers/virt/coco/arm-cca-host/Kconfig
+++ b/drivers/virt/coco/arm-cca-host/Kconfig
@@ -8,7 +8,11 @@ config ARM_CCA_HOST
depends on PCI
depends on KVM
select PCI_TSM
+ select KEYS
+ select X509_CERTIFICATE_PARSER
select AUXILIARY_BUS
+ select CRYPTO_ECDSA
+ select CRYPTO_RSA
help
ARM CCA RMM firmware is the trusted runtime that enforces memory
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 8a43a1f1c036..996979dba709 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -8,6 +8,9 @@
#include <linux/pci-doe.h>
#include <linux/delay.h>
#include <asm/rmi_cmds.h>
+#include <crypto/internal/rsa.h>
+#include <keys/asymmetric-type.h>
+#include <keys/x509-parser.h>
#include "rmi-da.h"
@@ -383,6 +386,158 @@ static int wait_for_pdev_state(struct pci_tsm *tsm, enum rmi_pdev_state target_s
return wait_for_dev_state(PDEV_COMMUNICATE, tsm, target_state, RMI_PDEV_ERROR);
}
+static int __maybe_unused parse_certificate_chain(struct pci_tsm *tsm)
+{
+ struct cca_host_pf0_ep_dsc *pf0_ep_dsc;
+ unsigned int chain_size;
+ unsigned int offset = 0;
+ u8 *chain_data;
+
+ pf0_ep_dsc = to_cca_pf0_ep_dsc(tsm->pdev);
+
+ /* If device communication didn't results in certificate caching. */
+ if (!pf0_ep_dsc->cert_chain.cache || !pf0_ep_dsc->cert_chain.cache->offset)
+ return -EINVAL;
+
+ chain_size = pf0_ep_dsc->cert_chain.cache->offset;
+ chain_data = pf0_ep_dsc->cert_chain.cache->buf;
+
+ while (offset < chain_size) {
+ ssize_t cert_len =
+ x509_get_certificate_length(chain_data + offset,
+ chain_size - offset);
+ if (cert_len < 0)
+ return cert_len;
+
+ struct x509_certificate *cert __free(x509_free_certificate) =
+ x509_cert_parse(chain_data + offset, cert_len);
+
+ if (IS_ERR(cert)) {
+ pci_warn(tsm->pdev, "parsing of certificate chain not successful\n");
+ return PTR_ERR(cert);
+ }
+
+ /* The key in the last cert in the chain is used */
+ if (offset + cert_len == chain_size) {
+ void *public_key __free(kfree) =
+ kzalloc(cert->pub->keylen, GFP_KERNEL);
+
+ if (!public_key)
+ return -ENOMEM;
+
+ if (!strcmp("ecdsa-nist-p256", cert->pub->pkey_algo)) {
+ pf0_ep_dsc->rmi_signature_algorithm = RMI_SIG_ECDSA_P256;
+ } else if (!strcmp("ecdsa-nist-p384", cert->pub->pkey_algo)) {
+ pf0_ep_dsc->rmi_signature_algorithm = RMI_SIG_ECDSA_P384;
+ } else if (!strcmp("rsa", cert->pub->pkey_algo)) {
+ struct rsa_key rsa_key = {0};
+ size_t skip = 0;
+ int ret;
+
+ ret = rsa_parse_pub_key(&rsa_key, cert->pub->key,
+ cert->pub->keylen);
+ if (ret)
+ return ret;
+
+ while (skip < rsa_key.n_sz && !rsa_key.n[skip])
+ skip++;
+
+ /* check we have 3072 bits len */
+ if ((rsa_key.n_sz - skip) != (3072 >> 3))
+ return -EINVAL;
+
+ pf0_ep_dsc->rmi_signature_algorithm = RMI_SIG_RSASSA_3072;
+ } else {
+ return -EINVAL;
+ }
+
+ memcpy(public_key, cert->pub->key, cert->pub->keylen);
+ pf0_ep_dsc->cert_chain.public_key = no_free_ptr(public_key);
+ pf0_ep_dsc->cert_chain.public_key_size = cert->pub->keylen;
+ pf0_ep_dsc->cert_chain.valid = true;
+ return 0;
+ }
+
+ offset += cert_len;
+ }
+
+ /* something wrong with chain size and parsing. */
+ return -EINVAL;
+}
+
+static inline void key_param_free(struct rmi_public_key_params *param)
+{
+ return free_page((unsigned long)param);
+}
+
+static inline int copy_key_part(u8 *buf, const u8 *key_buf, size_t sz)
+{
+ int skip;
+
+ /* skip leading zero in asn.1 */
+ for (skip = 0; skip < sz; skip++)
+ if (key_buf[skip])
+ break;
+
+ memcpy(buf, key_buf + skip, sz - skip);
+ return sz - skip;
+}
+
+DEFINE_FREE(key_param_free, struct rmi_public_key_params *, if (_T) key_param_free(_T))
+static int __maybe_unused pdev_set_public_key(struct pci_tsm *tsm)
+{
+ struct cca_host_pf0_ep_dsc *pf0_ep_dsc;
+
+ pf0_ep_dsc = to_cca_pf0_ep_dsc(tsm->pdev);
+ /* Check that all the necessary information was captured from communication */
+ if (!pf0_ep_dsc->cert_chain.valid)
+ return -EINVAL;
+
+ struct rmi_public_key_params *key_params __free(key_param_free) =
+ (struct rmi_public_key_params *)get_zeroed_page(GFP_KERNEL);
+ if (!key_params)
+ return -ENOMEM;
+
+ key_params->rmi_signature_algorithm = pf0_ep_dsc->rmi_signature_algorithm;
+
+ switch (key_params->rmi_signature_algorithm) {
+ case RMI_SIG_ECDSA_P384:
+ case RMI_SIG_ECDSA_P256:
+ {
+ key_params->public_key_len = pf0_ep_dsc->cert_chain.public_key_size;
+ memcpy(key_params->public_key,
+ pf0_ep_dsc->cert_chain.public_key,
+ pf0_ep_dsc->cert_chain.public_key_size);
+ key_params->metadata_len = 0;
+ break;
+ }
+ case RMI_SIG_RSASSA_3072:
+ {
+ int ret;
+ struct rsa_key rsa_key = {0};
+
+ ret = rsa_parse_pub_key(&rsa_key,
+ pf0_ep_dsc->cert_chain.public_key,
+ pf0_ep_dsc->cert_chain.public_key_size);
+ if (ret)
+ return ret;
+
+ key_params->public_key_len = copy_key_part(key_params->public_key,
+ rsa_key.n, rsa_key.n_sz);
+ key_params->metadata_len = copy_key_part(key_params->metadata,
+ rsa_key.e, rsa_key.e_sz);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ if (rmi_pdev_set_pubkey(virt_to_phys(pf0_ep_dsc->pdev.rmm_pdev),
+ virt_to_phys(key_params)))
+ return -ENXIO;
+ return 0;
+}
+
static void pdev_state_transition_workfn(struct work_struct *work)
{
unsigned long state;
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 784eb1fff95d..7d38e548b659 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -59,6 +59,7 @@ struct cca_host_pdev_dsc {
* @pci: Physical Function 0 TDISP link context
* @pdev: pdev communication context
* @sel_stream: Selective IDE Stream descriptor
+ * @rmi_signature_algorithm: Signature algorithm used for public key
* @cert_chain: cetrificate chain
* @vca: SPDM's Version-Capabilities-Algorithms cache object
*/
@@ -67,6 +68,7 @@ struct cca_host_pf0_ep_dsc {
struct cca_host_pdev_dsc pdev;
struct pci_ide *sel_stream;
+ uint8_t rmi_signature_algorithm;
struct {
struct cache_object *cache;
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v4 09/14] coco: host: arm64: Initialize RMM pdev state for TDISP IDE connect
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 6:51 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Jonathan Cameron, Marc Zyngier,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
In-Reply-To: <20260427065121.916615-1-aneesh.kumar@kernel.org>
Update connect() to:
- allocate device-communication buffers,
- create the RMM pdev object,
- perform initial device communication to collect identity, and
- set the device public key when the pdev enters NEEDS_KEY.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/virt/coco/arm-cca-host/arm-cca.c | 43 +++++++++--
drivers/virt/coco/arm-cca-host/rmi-da.c | 92 +++++++++++++++++++++++-
drivers/virt/coco/arm-cca-host/rmi-da.h | 3 +
3 files changed, 128 insertions(+), 10 deletions(-)
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index 3c854aab95cc..f0aa4e46e96c 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -66,7 +66,7 @@ static void cca_tsm_pci_remove(struct pci_tsm *tsm)
}
}
-static __maybe_unused int init_dev_communication_buffers(struct pci_dev *pdev,
+static int init_dev_communication_buffers(struct pci_dev *pdev,
struct cca_host_comm_data *comm_data)
{
int ret = -ENOMEM;
@@ -184,15 +184,40 @@ static int cca_tsm_connect(struct pci_dev *pdev)
ret = tsm_ide_stream_register(ide);
if (ret)
goto err_tsm;
+ }
- /*
- * Once ide is setup, enable the stream at the endpoint
- * Root port will be done by RMM
- */
- pci_ide_stream_enable(pdev, ide);
+ ret = init_dev_communication_buffers(pdev, &pf0_ep_dsc->pdev.comm_data);
+ if (ret)
+ goto err_comm_buff;
+ ret = cca_pdev_create(pdev);
+ if (ret)
+ goto err_pdev_create;
+
+ ret = cca_pdev_collect_identity(pdev);
+ if (ret)
+ goto pdev_destroy;
+
+ if (cca_pdev_needs_key(pdev)) {
+ ret = cca_pdev_set_public_key(pdev);
+ if (ret)
+ goto pdev_destroy;
}
+ /*
+ * Once ide is setup, enable the stream at the endpoint
+ * Root port will be done by RMM
+ */
+ if (cca_pdev_need_sel_ide_streams(pdev))
+ pci_ide_stream_enable(pdev, ide);
+
return 0;
+pdev_destroy:
+ cca_pdev_stop_and_destroy(pdev);
+err_pdev_create:
+ free_dev_communication_buffers(&pf0_ep_dsc->pdev.comm_data);
+err_comm_buff:
+ if (cca_pdev_need_sel_ide_streams(pdev))
+ tsm_ide_stream_unregister(ide);
err_tsm:
if (cca_pdev_need_sel_ide_streams(pdev)) {
pci_ide_stream_teardown(rp, ide);
@@ -222,12 +247,16 @@ static void cca_tsm_disconnect(struct pci_dev *pdev)
if (cca_pdev_need_sel_ide_streams(pdev)) {
ide = pf0_ep_dsc->sel_stream;
stream_id = ide->stream_id;
+ }
+
+ cca_pdev_stop_and_destroy(pdev);
+ free_dev_communication_buffers(&pf0_ep_dsc->pdev.comm_data);
+ if (cca_pdev_need_sel_ide_streams(pdev)) {
pci_ide_stream_release(ide);
pf0_ep_dsc->sel_stream = NULL;
clear_bit(stream_id, cca_stream_ids);
}
-
}
static struct pci_tsm_ops cca_link_pci_ops = {
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 996979dba709..cb654d1b2eb3 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -386,7 +386,7 @@ static int wait_for_pdev_state(struct pci_tsm *tsm, enum rmi_pdev_state target_s
return wait_for_dev_state(PDEV_COMMUNICATE, tsm, target_state, RMI_PDEV_ERROR);
}
-static int __maybe_unused parse_certificate_chain(struct pci_tsm *tsm)
+static int parse_certificate_chain(struct pci_tsm *tsm)
{
struct cca_host_pf0_ep_dsc *pf0_ep_dsc;
unsigned int chain_size;
@@ -484,7 +484,7 @@ static inline int copy_key_part(u8 *buf, const u8 *key_buf, size_t sz)
}
DEFINE_FREE(key_param_free, struct rmi_public_key_params *, if (_T) key_param_free(_T))
-static int __maybe_unused pdev_set_public_key(struct pci_tsm *tsm)
+static int pdev_set_public_key(struct pci_tsm *tsm)
{
struct cca_host_pf0_ep_dsc *pf0_ep_dsc;
@@ -581,8 +581,94 @@ static int submit_pdev_state_transition_work(struct pci_dev *pdev,
return 0;
}
+static void pdev_collect_identity_workfn(struct work_struct *work)
+{
+ struct pci_tsm *tsm;
+ struct dev_comm_work *setup_work;
+ struct cca_host_pdev_dsc *pdev_dsc;
+
+ setup_work = container_of(work, struct dev_comm_work, work);
+ tsm = setup_work->tsm;
+ pdev_dsc = to_cca_pdev_dsc(tsm->dsm_dev);
+
+ guard(mutex)(&pdev_dsc->object_lock);
+
+ do_dev_communicate(PDEV_COMMUNICATE, tsm, RMI_PDEV_ERROR);
+
+ /*
+ * Don't worry about communication error. The caller will look at
+ * device state to find more about error
+ */
+}
+
+int cca_pdev_collect_identity(struct pci_dev *pdev)
+{
+ enum rmi_pdev_state state;
+ struct dev_comm_work comm_work;
+ struct cca_host_pdev_dsc *pdev_dsc = to_cca_pdev_dsc(pdev);
+ struct cca_host_comm_data *comm_data = to_cca_comm_data(pdev);
+
+ /*
+ * Device identity is collected by doing a device communication
+ * after a pdev_create
+ */
+ INIT_WORK_ONSTACK(&comm_work.work, pdev_collect_identity_workfn);
+ comm_work.tsm = pdev->tsm;
+
+ queue_work(comm_data->work_queue, &comm_work.work);
+
+ flush_work(&comm_work.work);
+ destroy_work_on_stack(&comm_work.work);
+
+ /* check for device communication error*/
+ if (rmi_pdev_get_state(virt_to_phys(pdev_dsc->rmm_pdev), &state))
+ return -EIO;
+
+ if (state == RMI_PDEV_ERROR)
+ return -EPROTO;
+
+ return 0;
+}
+
+bool cca_pdev_needs_key(struct pci_dev *pdev)
+{
+ enum rmi_pdev_state state;
+ struct cca_host_pdev_dsc *pdev_dsc = to_cca_pdev_dsc(pdev);
+
+ /*
+ * Consider pdev_get_state failure as need key transition
+ * and that will result in device communication failure, which
+ * will handle this error.
+ */
+ if (rmi_pdev_get_state(virt_to_phys(pdev_dsc->rmm_pdev), &state))
+ return true;
+
+ if (state == RMI_PDEV_NEEDS_KEY)
+ return true;
+ return false;
+}
+
+int cca_pdev_set_public_key(struct pci_dev *pdev)
+{
+ int ret;
+
+ /*
+ * we now have certificate chain in dsm->cert_chain. Parse that and set
+ * the pubkey.
+ */
+ ret = parse_certificate_chain(pdev->tsm);
+ if (ret)
+ return ret;
+
+ ret = pdev_set_public_key(pdev->tsm);
+ if (ret)
+ return ret;
+
+ return submit_pdev_state_transition_work(pdev, RMI_PDEV_READY);
+}
+
static inline int rmi_pdev_destroy(unsigned long pdev_phys,
- unsigned long *rmi_ret)
+ unsigned long *rmi_ret)
{
struct rmi_sro_state *sro __free(sro) =
rmi_sro_init(SMC_RMI_PDEV_DESTROY, pdev_phys);
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 7d38e548b659..240b2993ae53 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -135,6 +135,9 @@ static inline struct cca_host_comm_data *to_cca_comm_data(struct pci_dev *pdev)
}
int cca_pdev_create(struct pci_dev *pdev);
+int cca_pdev_collect_identity(struct pci_dev *pdev);
+bool cca_pdev_needs_key(struct pci_dev *pdev);
+int cca_pdev_set_public_key(struct pci_dev *pdev);
void cca_pdev_stop_and_destroy(struct pci_dev *pdev);
#endif
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v4 10/14] coco: host: arm64: Coordinate peer stream waits during pdev communication
From: Aneesh Kumar K.V (Arm) @ 2026-04-27 6:51 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Alexey Kardashevskiy, Catalin Marinas,
Dan Williams, Jason Gunthorpe, Jonathan Cameron, Marc Zyngier,
Samuel Ortiz, Steven Price, Suzuki K Poulose, Will Deacon,
Xu Yilun
In-Reply-To: <20260427065121.916615-1-aneesh.kumar@kernel.org>
RMM stream operations can return RMI_DEV_COMM_EXIT_STREAM_WAIT while
one side waits for the peer stream to reach the matching point in the
protocol.
Teach arm-cca host device communication to detect STREAM_WAIT and add
a helper that runs pdev communication for both sides in parallel until
each side has made enough progress, then issue rmi_pdev_stream_complete().
This provides the synchronization needed for stream connect,
disconnect, key refresh, and key purge operations.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_smc.h | 1 +
drivers/virt/coco/arm-cca-host/rmi-da.c | 116 +++++++++++++++++++++++-
drivers/virt/coco/arm-cca-host/rmi-da.h | 13 +++
3 files changed, 125 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 7a5d57a8be7a..e9437d56996a 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -484,6 +484,7 @@ struct rmi_pdev_params {
#define RMI_DEV_COMM_EXIT_WAIT BIT(3)
#define RMI_DEV_COMM_EXIT_RSP_RESET BIT(4)
#define RMI_DEV_COMM_EXIT_MULTI BIT(5)
+#define RMI_DEV_COMM_EXIT_STREAM_WAIT BIT(6)
#define RMI_DEV_COMM_NONE 0
#define RMI_DEV_COMM_RESPONSE 1
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index cb654d1b2eb3..28f450e2db27 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -197,7 +197,7 @@ static inline gfp_t cache_obj_id_to_gfp_flags(u8 cache_obj_id)
return GFP_KERNEL_ACCOUNT;
}
-static int _do_dev_communicate(enum dev_comm_type type, struct pci_tsm *tsm)
+static int _do_dev_communicate(enum dev_comm_type type, struct pci_tsm *tsm, int *stream_wait)
{
unsigned long rmi_ret;
gfp_t cache_alloc_flags;
@@ -329,11 +329,17 @@ static int _do_dev_communicate(enum dev_comm_type type, struct pci_tsm *tsm)
if (pending_dev_communicate(io_exit))
goto redo_communicate;
+ if (io_exit->flags & RMI_DEV_COMM_EXIT_STREAM_WAIT) {
+ if (stream_wait)
+ *stream_wait = 1;
+ else
+ WARN(1, "Unexpected Stream wait status\n");
+ }
return 0;
}
static int do_dev_communicate(enum dev_comm_type type,
- struct pci_tsm *tsm, unsigned long error_state)
+ struct pci_tsm *tsm, unsigned long error_state, int *stream_wait)
{
int ret, state = error_state;
struct rmi_dev_comm_enter *io_enter;
@@ -342,8 +348,10 @@ static int do_dev_communicate(enum dev_comm_type type,
io_enter = &pdev_dsc->comm_data.io_params->enter;
io_enter->resp_len = 0;
io_enter->status = RMI_DEV_COMM_NONE;
+ if (stream_wait)
+ *stream_wait = 0;
- ret = _do_dev_communicate(type, tsm);
+ ret = _do_dev_communicate(type, tsm, stream_wait);
if (ret) {
if (type == PDEV_COMMUNICATE)
rmi_pdev_abort(virt_to_phys(pdev_dsc->rmm_pdev));
@@ -371,7 +379,7 @@ static int wait_for_dev_state(enum dev_comm_type type, struct pci_tsm *tsm,
int state;
do {
- state = do_dev_communicate(type, tsm, error_state);
+ state = do_dev_communicate(type, tsm, error_state, NULL);
if (state == target_state || state == error_state)
return state;
@@ -593,7 +601,7 @@ static void pdev_collect_identity_workfn(struct work_struct *work)
guard(mutex)(&pdev_dsc->object_lock);
- do_dev_communicate(PDEV_COMMUNICATE, tsm, RMI_PDEV_ERROR);
+ do_dev_communicate(PDEV_COMMUNICATE, tsm, RMI_PDEV_ERROR, NULL);
/*
* Don't worry about communication error. The caller will look at
@@ -711,3 +719,101 @@ void cca_pdev_stop_and_destroy(struct pci_dev *pdev)
free_page((unsigned long)pdev_dsc->rmm_pdev);
pdev_dsc->rmm_pdev = NULL;
}
+
+static void stream_connect_workfn(struct work_struct *work)
+{
+ int state;
+ int peer_wait = 0;
+ struct pci_tsm *tsm;
+ int my_index, peer_index, target;
+ struct stream_connect_work *stream_work;
+ struct cca_host_pdev_dsc *pdev_dsc;
+
+ stream_work = container_of(work, struct stream_connect_work, work);
+ tsm = stream_work->tsm;
+ pdev_dsc = to_cca_pdev_dsc(tsm->dsm_dev);
+
+ my_index = stream_work->my_index;
+ peer_index = my_index ^ 0x1;
+
+redo_communicate:
+ mutex_lock(&pdev_dsc->object_lock);
+
+ state = do_dev_communicate(PDEV_COMMUNICATE, tsm, RMI_PDEV_ERROR, &peer_wait);
+ if (state != RMI_PDEV_ERROR && peer_wait) {
+
+ if (!stream_work->has_peer) {
+ WARN(1, "Unexpected STREAM_WAIT without peer stream\n");
+ mutex_unlock(&pdev_dsc->object_lock);
+ return;
+ }
+ /*
+ * Record a fresh target val for this side, then wait until
+ * peer reaches at least the same target.
+ */
+ target = atomic_inc_return(&stream_work->sync->val[my_index]);
+
+ wake_up_all(&stream_work->sync->wq);
+
+ mutex_unlock(&pdev_dsc->object_lock);
+
+ /* Wait for peer to make matching progress */
+ wait_event(stream_work->sync->wq,
+ atomic_read(&stream_work->sync->val[peer_index]) >= target);
+ goto redo_communicate;
+ }
+
+ /* Signal peer if it is waiting on me */
+ atomic_inc_return(&stream_work->sync->val[my_index]);
+ wake_up_all(&stream_work->sync->wq);
+
+ mutex_unlock(&pdev_dsc->object_lock);
+}
+
+static int __maybe_unused submit_stream_work(struct pci_dev *pdev1, struct pci_dev *pdev2,
+ unsigned long stream_handle)
+{
+ phys_addr_t rmm_pdev1_phys, rmm_pdev2_phys = 0;
+ struct cca_host_comm_data *comm_data_pdev1, *comm_data_pdev2;
+ struct cca_host_pdev_dsc *pdev_dsc1, *pdev_dsc2 = NULL;
+ struct stream_sync sync;
+ struct stream_connect_work stream_work_pdev1, stream_work_pdev2;
+
+ comm_data_pdev1 = to_cca_comm_data(pdev1);
+ init_waitqueue_head(&sync.wq);
+ atomic_set(&sync.val[0], 0);
+ atomic_set(&sync.val[1], 0);
+
+ pdev_dsc1 = to_cca_pdev_dsc(pdev1);
+ INIT_WORK_ONSTACK(&stream_work_pdev1.work, stream_connect_workfn);
+ stream_work_pdev1.tsm = pdev1->tsm;
+ stream_work_pdev1.sync = &sync;
+ stream_work_pdev1.my_index = 0;
+ stream_work_pdev1.has_peer = !!pdev2;
+ queue_work(comm_data_pdev1->work_queue, &stream_work_pdev1.work);
+
+ if (pdev2) {
+ comm_data_pdev2 = to_cca_comm_data(pdev2);
+ pdev_dsc2 = to_cca_pdev_dsc(pdev2);
+ INIT_WORK_ONSTACK(&stream_work_pdev2.work, stream_connect_workfn);
+ stream_work_pdev2.tsm = pdev2->tsm;
+ stream_work_pdev2.sync = &sync;
+ stream_work_pdev2.my_index = 1;
+ stream_work_pdev2.has_peer = true;
+ queue_work(comm_data_pdev2->work_queue, &stream_work_pdev2.work);
+ }
+
+ flush_work(&stream_work_pdev1.work);
+ if (pdev2) {
+ flush_work(&stream_work_pdev2.work);
+ destroy_work_on_stack(&stream_work_pdev2.work);
+ }
+
+ destroy_work_on_stack(&stream_work_pdev1.work);
+
+ rmm_pdev1_phys = virt_to_phys(pdev_dsc1->rmm_pdev);
+ if (pdev2)
+ rmm_pdev2_phys = virt_to_phys(pdev_dsc2->rmm_pdev);
+
+ return 0;
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 240b2993ae53..5b0f43493485 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -27,6 +27,19 @@ struct dev_comm_work {
struct work_struct work;
};
+struct stream_sync {
+ wait_queue_head_t wq;
+ atomic_t val[2];
+};
+
+struct stream_connect_work {
+ struct pci_tsm *tsm;
+ struct work_struct work;
+ struct stream_sync *sync;
+ u8 my_index;
+ bool has_peer;
+};
+
struct cca_host_comm_data {
void *rsp_buff;
void *req_buff;
--
2.43.0
^ 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