* Re: [PATCH v4 11/24] x86/virt/seamldr: Introduce skeleton for TDX Module updates
From: Chao Gao @ 2026-03-12 14:09 UTC (permalink / raw)
To: Edgecombe, Rick P
Cc: kvm@vger.kernel.org, linux-coco@lists.linux.dev,
linux-kernel@vger.kernel.org, x86@kernel.org, Huang, Kai,
dave.hansen@linux.intel.com, tony.lindgren@linux.intel.com,
binbin.wu@linux.intel.com, seanjc@google.com, Weiny, Ira,
Chatre, Reinette, Verma, Vishal L, nik.borisov@suse.com,
mingo@redhat.com, kas@kernel.org, Annapurve, Vishal,
sagis@google.com, Duan, Zhenzhong, tglx@kernel.org,
paulmck@kernel.org, hpa@zytor.com, bp@alien8.de,
yilun.xu@linux.intel.com, Williams, Dan J
In-Reply-To: <daa8080e1195f586af331dcf31ee5239c4ecbd15.camel@intel.com>
On Thu, Mar 12, 2026 at 10:00:20AM +0800, Edgecombe, Rick P wrote:
>On Thu, 2026-02-12 at 06:35 -0800, Chao Gao wrote:
>> TDX Module updates require careful synchronization with other TDX
>> operations on the host. During updates, only update-related SEAMCALLs are
>> permitted; all other SEAMCALLs must be blocked.
>>
>> However, SEAMCALLs can be invoked from different contexts (normal and IRQ
>> context) and run in parallel across CPUs. And, all TD vCPUs must remain
>> out of guest mode during updates.
>>
>
>Above it says only update-related SEAMCALLs are permitted. Does that not already
>exclude SEAMCALLs that might allow entering the TD?
Those SEAMCALLs would return errors, and TDs would be killed if those errors
aren't handled properly.
One may argue that we can handle errors and retry after updates. But this just
provides a new form of synchronization, which is not as clean as the
well-defined synchronization provided by the kernel.
>
>> No single lock primitive can satisfy
>> all these synchronization requirements, so stop_machine() is used as the
>> only well-understood mechanism that can meet them all.
>>
>> The TDX Module update process consists of several steps as described in
>> Intel® Trust Domain Extensions (Intel® TDX) Module Base Architecture
>> Specification, Revision 348549-007, Chapter 4.5 "TD-Preserving TDX Module
>> Update"
>>
>> - shut down the old module
>> - install the new module
>> - global and per-CPU initialization
>> - restore state information
>>
>> Some steps must execute on a single CPU, others must run serially across
>> all CPUs, and some can run concurrently on all CPUs. There are also
>> ordering requirements between steps, so all CPUs must work in a step-locked
>> manner.
>
>Does the fact that they can run on other CPUs add any synchronization
>requirements? If not I'd leave it off.
I'm not sure I understand the concern.
Lockstep synchronization is needed specifically because we have both multiple
CPUs and multiple steps.
If updates only required a single CPU, stop_machine() would be sufficient.
>
>>
>> In summary, TDX Module updates create two requirements:
>
>The stop_machine() part seems more like a solution then a requirement.
>
>>
>> 1. The entire update process must use stop_machine() to synchronize with
>> other TDX workloads
>> 2. Update steps must be performed in a step-locked manner
>>
>> To prepare for implementing concrete TDX Module update steps, establish
>> the framework by mimicking multi_cpu_stop(), which is a good example of
>> performing a multi-step task in step-locked manner.
>>
>
>Offline Chao pointed that Paul suggested this after considering refactoring out
>the common code. I think it might still be worth mentioning why you can't use
>multi_cpu_stop() directly. I guess there are some differences. what are they.
To be clear, Paul didn't actually suggest this approach. His feedback indicated
he wasn't concerned about duplicating some of multi_cpu_stop()'s code, i.e., no
need to refactor out some common code.
https://lore.kernel.org/all/a7affba9-0cea-4493-b868-392158b59d83@paulmck-laptop/#t
We can't use multi_cpu_stop() directly because it only provides lockstep
execution for its own infrastructure, not for the function it runs. If we
passed a function that performs steps A, B, and C to multi_cpu_stop(), there's
no guarantee that all CPUs complete step A before any CPU begins step B.
>
>> Specifically, use a
>> global state machine to control each CPU's work and require all CPUs to
>> acknowledge completion before proceeding to the next step.
>
>Maybe add a bit more about the reasoning for requiring the other steps to ack.
>Tie it back to the lockstep part.
>
Ok. How about:
Specifically, add a global state machine where each state represents a step in
the above update flow. The state advances only after all CPUs acknowledge
completing their work in the current state. This acknowledgment mechanism is
what ensures lockstep execution.
<snip>
>> +static int do_seamldr_install_module(void *params)
>> +{
>> + enum tdp_state newstate, curstate = TDP_START;
>> + int ret = 0;
>> +
>> + do {
>> + /* Chill out and re-read tdp_data */
>> + cpu_relax();
>> + newstate = READ_ONCE(tdp_data.state);
>> +
>> + if (newstate != curstate) {
>> + curstate = newstate;
>> + switch (curstate) {
>
>Maybe a little comment here like "todo add the steps".
Sure.
^ permalink raw reply
* Re: [PATCH net-next v3 1/2] dma-mapping: introduce DMA_ATTR_CC_DECRYPTED for pre-decrypted memory
From: Jiri Pirko @ 2026-03-12 13:27 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Leon Romanovsky, dri-devel, linaro-mm-sig, iommu, linux-media,
sumit.semwal, benjamin.gaignard, Brian.Starkey, jstultz,
tjmercier, christian.koenig, m.szyprowski, robin.murphy,
sean.anderson, ptesarik, catalin.marinas, aneesh.kumar,
suzuki.poulose, steven.price, thomas.lendacky, john.allen,
ashish.kalra, suravee.suthikulpanit, linux-coco
In-Reply-To: <20260312120606.GC1469476@ziepe.ca>
Thu, Mar 12, 2026 at 01:06:06PM +0100, jgg@ziepe.ca wrote:
>On Thu, Mar 12, 2026 at 10:03:37AM +0100, Jiri Pirko wrote:
>> >Alot of device MMIO is decrypted by nature and can't be encrypted, so
>> >you'd have to use both flags. eg in VFIO we'd want to do this.
>>
>> Why both flags? Why MMIO flag is not enough? You still want to hit
>> "if (attrs & DMA_ATTR_MMIO) {" path, don't you?
>
>Because we will eventually have both decrypted and encrypted MMIO.
>
>> I mean, CC_DECRYPTED says the memory to be mapped was explicitly
>> decrypted before the call. MMIO was not explicitly decrypted, it is
>> decrypted by definition. For me that does not fit the CC_DECRYPTED
>> semantics.
>
>I would say CC_DECRYPTED means that pgprot_decrypted must be used to
>form a PTE, and !CC_DECRYPTED means that pgprot_encrypted() was used
>
>This flag should someday flow down into the vIOMMU driver and set the
>corresponding C bit the IOPTE (for AMD) exactly as the pgprot does.
>
>Less about set_memory_encrypted as that is only for DRAM.
Okay, that makes sense. Thanks!
^ permalink raw reply
* Re: [PATCH net-next v3 1/2] dma-mapping: introduce DMA_ATTR_CC_DECRYPTED for pre-decrypted memory
From: Jason Gunthorpe @ 2026-03-12 12:06 UTC (permalink / raw)
To: Jiri Pirko
Cc: Leon Romanovsky, dri-devel, linaro-mm-sig, iommu, linux-media,
sumit.semwal, benjamin.gaignard, Brian.Starkey, jstultz,
tjmercier, christian.koenig, m.szyprowski, robin.murphy,
sean.anderson, ptesarik, catalin.marinas, aneesh.kumar,
suzuki.poulose, steven.price, thomas.lendacky, john.allen,
ashish.kalra, suravee.suthikulpanit, linux-coco
In-Reply-To: <pmfxzgtazwa7fwz4mx53htdg4jjifwyq3gyzwklc45affy3xrp@57hxan4pqnkx>
On Thu, Mar 12, 2026 at 10:03:37AM +0100, Jiri Pirko wrote:
> >Alot of device MMIO is decrypted by nature and can't be encrypted, so
> >you'd have to use both flags. eg in VFIO we'd want to do this.
>
> Why both flags? Why MMIO flag is not enough? You still want to hit
> "if (attrs & DMA_ATTR_MMIO) {" path, don't you?
Because we will eventually have both decrypted and encrypted MMIO.
> I mean, CC_DECRYPTED says the memory to be mapped was explicitly
> decrypted before the call. MMIO was not explicitly decrypted, it is
> decrypted by definition. For me that does not fit the CC_DECRYPTED
> semantics.
I would say CC_DECRYPTED means that pgprot_decrypted must be used to
form a PTE, and !CC_DECRYPTED means that pgprot_encrypted() was used
This flag should someday flow down into the vIOMMU driver and set the
corresponding C bit the IOPTE (for AMD) exactly as the pgprot does.
Less about set_memory_encrypted as that is only for DRAM.
Jason
^ permalink raw reply
* Re: [PATCH v12 06/46] arm64: RMI: Define the user ABI
From: Steven Price @ 2026-03-12 10:45 UTC (permalink / raw)
To: Marc Zyngier, Suzuki K Poulose
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Zenghui Yu, linux-arm-kernel, linux-kernel,
Joey Gouly, Alexandru Elisei, Christoffer Dall, Fuad Tabba,
linux-coco, Ganapatrao Kulkarni, Gavin Shan, Shanker Donthineni,
Alper Gun, Aneesh Kumar K . V, Emi Kisanuki, Vishal Annapurve
In-Reply-To: <86342575ev.wl-maz@kernel.org>
On 12/03/2026 09:39, Marc Zyngier wrote:
> On Thu, 12 Mar 2026 09:28:16 +0000,
> Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
>>
>> On 11/03/2026 19:10, Marc Zyngier wrote:
>>> On Mon, 02 Mar 2026 15:23:44 +0000,
>>> Steven Price <steven.price@arm.com> wrote:
>>>>
>>>>>> + struct kvm_arm_rmi_populate {
>>>>>> + __u64 base;
>>>>>> + __u64 size;
>>>>>> + __u64 source_uaddr;
>>>>>> + __u32 flags;
>>>>>> + __u32 reserved;
>>>>>> + };
>>>>>> +
>>>>>> +Populate a region of protected address space by copying the data from the user
>>>>>> +space pointer provided. This is only valid before any VCPUs have been run.
>>>>>> +The ioctl might not populate the entire region and user space may have to
>>>>>> +repeatedly call it (with updated pointers) to populate the entire region.
>>>>>
>>>>> size as a __u64 is odd, as the return value from the ioctl is a signed
>>>>> int. This implies that you can't really report how many bytes you have
>>>>> copied. Some form of consistency wouldn't hurt.
>>>>
>>>> Good spot. In practice this works because >2GB in one operation is
>>>> highly unlikely to be processed in one go. But I guess I'll change this
>>>> to have an output size argument. I guess I could make the kernel update
>>>> all of base,size,source_uaddr which would simplify user space.
>>>
>>> In a conversation with Suzuki, I suggested that splice(2) could be a
>>> nicer way to express this, and allow asynchronous use with io-uring.
>>>
>>> After all, having a guestmem backend for CCA is not exactly
>>> outlandish, and having a splice implementation realistic enough.
>>>
>>> Thoughts?
>>
>> One issue that I realised, is about the "flags" for the populate.
>> Data can be loaded as measured vs unmeasured in CCA (and in TDX
>> and SNP). I don't see a way to convey this with splice.
>
> Surely that should me a property of the memory region, and not the way
> it is copied, right?
I'm not sure what the current thoughts on the matter are, but there was
an intention in the past that some data could be measured and some not
within a region which is logically the same. E.g. the DT might be
deliberately unmeasured so that the same measurement would cover a
selection of different configurations (with the guest obviously
validating that it was sane before continuing). Forcing the memory to be
split into multiple memslots just so a measurement flag can be set
correctly seems like a poor uAPI.
While splice(2) certainly looks similar to what we want, it does
(according to the man page at least) have the requirement that "one of
the file descriptors must refer to a pipe" which seems less than ideal
for the DT case where it's likely to be generated in memory by the VMM.
Or is my man page out of date and the pipe restriction isn't there in
the kernel?
Thanks,
Steve
^ permalink raw reply
* Re: [PATCH v4 24/24] [NOT-FOR-REVIEW] x86/virt/seamldr: Save and restore current VMCS
From: Huang, Kai @ 2026-03-12 9:59 UTC (permalink / raw)
To: Gao, Chao
Cc: tony.lindgren@linux.intel.com, linux-coco@lists.linux.dev,
kvm@vger.kernel.org, dave.hansen@linux.intel.com, bp@alien8.de,
kas@kernel.org, mingo@redhat.com, Chatre, Reinette, Weiny, Ira,
seanjc@google.com, Verma, Vishal L, nik.borisov@suse.com,
binbin.wu@linux.intel.com, hpa@zytor.com, Annapurve, Vishal,
sagis@google.com, Duan, Zhenzhong, Edgecombe, Rick P,
linux-kernel@vger.kernel.org, paulmck@kernel.org, tglx@kernel.org,
yilun.xu@linux.intel.com, x86@kernel.org, Williams, Dan J
In-Reply-To: <abJ90EMUnEAy763n@intel.com>
>
> But I agree that following the X86_BUG_TDX_PW_MCE is better in consistency
> and extensibility. So, here is the refined patch:
>
>
> From 46e89a50803d6568eb60bd8ec866ac3fd9f6e6da Mon Sep 17 00:00:00 2001
> From: Chao Gao <chao.gao@intel.com>
> Date: Tue, 10 Mar 2026 18:49:41 -0700
> Subject: [PATCH] coco/tdx-host: Don't expose P-SEAMLDR features on CPUs with
> erratum
> MIME-Version: 1.0
> Content-Type: text/plain; charset=UTF-8
> Content-Transfer-Encoding: 8bit
>
> Some TDX-capable CPUs have an erratum, as documented in Intel® Trust
> Domain CPU Architectural Extensions (May 2021 edition) Chapter 2.3:
>
> SEAMRET from the P-SEAMLDR clears the current VMCS structure pointed
> to by the current-VMCS pointer. A VMM that invokes the P-SEAMLDR using
> SEAMCALL must reload the current-VMCS, if required, using the VMPTRLD
> instruction.
>
> Clearing the current VMCS behind KVM's back will break KVM.
>
> This erratum is not present when IA32_VMX_BASIC[60] is set. Add a CPU
> bug bit for this erratum and refuse to expose P-SEAMLDR features (e.g.,
> TDX module updates) on affected CPUs. Also, emit a message to clarify
> why P-SEAMLDR features are disabled for affected CPUs.
The user can actually see this new bug flag in /proc/cpuinfo, so the error
message may not be mandatory. It's fine to me anyway, so will leave to
others.
>
> == Alternatives ==
> Two workarounds were considered but both were rejected:
>
> 1. Save/restore the current VMCS around P-SEAMLDR calls. This produces ugly
> assembly code [1] and doesn't play well with #MCE or #NMI if they
> need to use the current VMCS.
>
> 2. Move KVM's VMCS tracking logic to the TDX core code, which would break
> the boundary between KVM and the TDX core code [2].
>
> Signed-off-by: Chao Gao <chao.gao@intel.com>
> Link: https://lore.kernel.org/kvm/fedb3192-e68c-423c-93b2-a4dc2f964148@intel.com/ # [1]
> Link: https://lore.kernel.org/kvm/aYIXFmT-676oN6j0@google.com/ # [2]
LGTM:
Reviewed-by: Kai Huang <kai.huang@intel.com>
One nit below:
[...]
> +#define X86_BUG_SEAMRET_INVD_VMCS X86_BUG( 1*32+11) /* "seamret_invd_vmcs" SEAMRET may clear the current VMCS */
"may clear" -> "clears" ?
^ permalink raw reply
* Re: [PATCH v12 06/46] arm64: RMI: Define the user ABI
From: Marc Zyngier @ 2026-03-12 9:39 UTC (permalink / raw)
To: Suzuki K Poulose
Cc: Steven Price, kvm, kvmarm, Catalin Marinas, Will Deacon,
James Morse, Oliver Upton, Zenghui Yu, linux-arm-kernel,
linux-kernel, Joey Gouly, Alexandru Elisei, Christoffer Dall,
Fuad Tabba, linux-coco, Ganapatrao Kulkarni, Gavin Shan,
Shanker Donthineni, Alper Gun, Aneesh Kumar K . V, Emi Kisanuki,
Vishal Annapurve
In-Reply-To: <dd6f4715-e8be-41d1-af60-00f068e9d730@arm.com>
On Thu, 12 Mar 2026 09:28:16 +0000,
Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
>
> On 11/03/2026 19:10, Marc Zyngier wrote:
> > On Mon, 02 Mar 2026 15:23:44 +0000,
> > Steven Price <steven.price@arm.com> wrote:
> >>
> >>>> + struct kvm_arm_rmi_populate {
> >>>> + __u64 base;
> >>>> + __u64 size;
> >>>> + __u64 source_uaddr;
> >>>> + __u32 flags;
> >>>> + __u32 reserved;
> >>>> + };
> >>>> +
> >>>> +Populate a region of protected address space by copying the data from the user
> >>>> +space pointer provided. This is only valid before any VCPUs have been run.
> >>>> +The ioctl might not populate the entire region and user space may have to
> >>>> +repeatedly call it (with updated pointers) to populate the entire region.
> >>>
> >>> size as a __u64 is odd, as the return value from the ioctl is a signed
> >>> int. This implies that you can't really report how many bytes you have
> >>> copied. Some form of consistency wouldn't hurt.
> >>
> >> Good spot. In practice this works because >2GB in one operation is
> >> highly unlikely to be processed in one go. But I guess I'll change this
> >> to have an output size argument. I guess I could make the kernel update
> >> all of base,size,source_uaddr which would simplify user space.
> >
> > In a conversation with Suzuki, I suggested that splice(2) could be a
> > nicer way to express this, and allow asynchronous use with io-uring.
> >
> > After all, having a guestmem backend for CCA is not exactly
> > outlandish, and having a splice implementation realistic enough.
> >
> > Thoughts?
>
> One issue that I realised, is about the "flags" for the populate.
> Data can be loaded as measured vs unmeasured in CCA (and in TDX
> and SNP). I don't see a way to convey this with splice.
Surely that should me a property of the memory region, and not the way
it is copied, right?
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply
* Re: [PATCH v12 06/46] arm64: RMI: Define the user ABI
From: Suzuki K Poulose @ 2026-03-12 9:28 UTC (permalink / raw)
To: Marc Zyngier, Steven Price
Cc: kvm, kvmarm, Catalin Marinas, Will Deacon, James Morse,
Oliver Upton, Zenghui Yu, linux-arm-kernel, linux-kernel,
Joey Gouly, Alexandru Elisei, Christoffer Dall, Fuad Tabba,
linux-coco, Ganapatrao Kulkarni, Gavin Shan, Shanker Donthineni,
Alper Gun, Aneesh Kumar K . V, Emi Kisanuki, Vishal Annapurve
In-Reply-To: <87v7f2chde.wl-maz@kernel.org>
On 11/03/2026 19:10, Marc Zyngier wrote:
> On Mon, 02 Mar 2026 15:23:44 +0000,
> Steven Price <steven.price@arm.com> wrote:
>>
>>>> + struct kvm_arm_rmi_populate {
>>>> + __u64 base;
>>>> + __u64 size;
>>>> + __u64 source_uaddr;
>>>> + __u32 flags;
>>>> + __u32 reserved;
>>>> + };
>>>> +
>>>> +Populate a region of protected address space by copying the data from the user
>>>> +space pointer provided. This is only valid before any VCPUs have been run.
>>>> +The ioctl might not populate the entire region and user space may have to
>>>> +repeatedly call it (with updated pointers) to populate the entire region.
>>>
>>> size as a __u64 is odd, as the return value from the ioctl is a signed
>>> int. This implies that you can't really report how many bytes you have
>>> copied. Some form of consistency wouldn't hurt.
>>
>> Good spot. In practice this works because >2GB in one operation is
>> highly unlikely to be processed in one go. But I guess I'll change this
>> to have an output size argument. I guess I could make the kernel update
>> all of base,size,source_uaddr which would simplify user space.
>
> In a conversation with Suzuki, I suggested that splice(2) could be a
> nicer way to express this, and allow asynchronous use with io-uring.
>
> After all, having a guestmem backend for CCA is not exactly
> outlandish, and having a splice implementation realistic enough.
>
> Thoughts?
One issue that I realised, is about the "flags" for the populate.
Data can be loaded as measured vs unmeasured in CCA (and in TDX
and SNP). I don't see a way to convey this with splice.
Suzuki
>
> M.
>
^ permalink raw reply
* Re: [PATCH net-next v3 1/2] dma-mapping: introduce DMA_ATTR_CC_DECRYPTED for pre-decrypted memory
From: Jiri Pirko @ 2026-03-12 9:03 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: Leon Romanovsky, dri-devel, linaro-mm-sig, iommu, linux-media,
sumit.semwal, benjamin.gaignard, Brian.Starkey, jstultz,
tjmercier, christian.koenig, m.szyprowski, robin.murphy,
sean.anderson, ptesarik, catalin.marinas, aneesh.kumar,
suzuki.poulose, steven.price, thomas.lendacky, john.allen,
ashish.kalra, suravee.suthikulpanit, linux-coco
In-Reply-To: <20260312003408.GA1469476@ziepe.ca>
Thu, Mar 12, 2026 at 01:34:08AM +0100, jgg@ziepe.ca wrote:
>On Mon, Mar 09, 2026 at 06:51:21PM +0100, Jiri Pirko wrote:
>> Mon, Mar 09, 2026 at 04:18:57PM +0100, jgg@ziepe.ca wrote:
>> >On Mon, Mar 09, 2026 at 04:02:33PM +0200, Leon Romanovsky wrote:
>> >> On Mon, Mar 09, 2026 at 10:15:30AM -0300, Jason Gunthorpe wrote:
>> >> > On Sun, Mar 08, 2026 at 12:19:48PM +0200, Leon Romanovsky wrote:
>> >> >
>> >> > > > +/*
>> >> > > > + * DMA_ATTR_CC_DECRYPTED: Indicates memory that has been explicitly decrypted
>> >> > > > + * (shared) for confidential computing guests. The caller must have
>> >> > > > + * called set_memory_decrypted(). A struct page is required.
>> >> > > > + */
>> >> > > > +#define DMA_ATTR_CC_DECRYPTED (1UL << 12)
>> >> > >
>> >> > > While adding the new attribute is fine, I would expect additional checks in
>> >> > > dma_map_phys() to ensure the attribute cannot be misused. For example,
>> >> > > WARN_ON(attrs & (DMA_ATTR_CC_DECRYPTED | DMA_ATTR_MMIO)), along with a check
>> >> > > that we are taking the direct path only.
>> >> >
>> >> > DECRYPYED and MMIO is something that needs to work, VFIO (inside a
>> >> > TVM) should be using that combination.
>> >>
>> >> So this sentence "A struct page is required" from the comment above is
>> >> not accurate.
>> >
>> >It would be clearer to say "Unless DMA_ATTR_MMIO is provided a struct
>> >page is required"
>> >
>> >We need to audit if that works properly, IIRC it does, but I don't
>> >remember.. Jiri?
>>
>> How can you do set_memory_decrypted if you don't have page/folio ?
>
>Alot of device MMIO is decrypted by nature and can't be encrypted, so
>you'd have to use both flags. eg in VFIO we'd want to do this.
Why both flags? Why MMIO flag is not enough? You still want to hit
"if (attrs & DMA_ATTR_MMIO) {" path, don't you?
I mean, CC_DECRYPTED says the memory to be mapped was explicitly
decrypted before the call. MMIO was not explicitly decrypted, it is
decrypted by definition. For me that does not fit the CC_DECRYPTED
semantics.
What am I missing?
^ permalink raw reply
* Re: [PATCH v4 24/24] [NOT-FOR-REVIEW] x86/virt/seamldr: Save and restore current VMCS
From: Chao Gao @ 2026-03-12 8:48 UTC (permalink / raw)
To: Huang, Kai
Cc: kvm@vger.kernel.org, linux-coco@lists.linux.dev,
linux-kernel@vger.kernel.org, x86@kernel.org,
dave.hansen@linux.intel.com, tony.lindgren@linux.intel.com,
binbin.wu@linux.intel.com, seanjc@google.com, kas@kernel.org,
Chatre, Reinette, Verma, Vishal L, nik.borisov@suse.com,
mingo@redhat.com, Weiny, Ira, hpa@zytor.com, Annapurve, Vishal,
sagis@google.com, Duan, Zhenzhong, Edgecombe, Rick P,
paulmck@kernel.org, tglx@kernel.org, yilun.xu@linux.intel.com,
Williams, Dan J, bp@alien8.de
In-Reply-To: <d664ac9445b1c7cc864dead103086341c374b094.camel@intel.com>
On Thu, Mar 12, 2026 at 06:06:22AM +0800, Huang, Kai wrote:
>
>> static const struct x86_cpu_id tdx_host_ids[] = {
>> X86_MATCH_FEATURE(X86_FEATURE_TDX_HOST_PLATFORM, NULL),
>> @@ -175,6 +177,7 @@ static int seamldr_init(struct device *dev)
>> {
>> const struct tdx_sys_info *tdx_sysinfo = tdx_get_sysinfo();
>> struct fw_upload *tdx_fwl;
>> + u64 basic_msr;
>>
>> if (WARN_ON_ONCE(!tdx_sysinfo))
>> return -EIO;
>> @@ -182,6 +185,15 @@ static int seamldr_init(struct device *dev)
>> if (!tdx_supports_runtime_update(tdx_sysinfo))
>> return 0;
>>
>> + /*
>> + * Some TDX-capable CPUs have an erratum where the current VMCS may
>> + * be cleared after calling into P-SEAMLDR. Ensure no such erratum
>> + * exists before exposing any P-SEAMLDR functions.
>> + */
>> + rdmsrq(MSR_IA32_VMX_BASIC, basic_msr);
>> + if (!(basic_msr & VMX_BASIC_PRESERVE_CURRENT_VMCS))
>> + return 0;
>> +
>
>IIUC this silently disables runtime update and user won't be able to have
>any clue to tell what went wrong (while the user can see the module supports
>this feature and apparently the kernel should support it)?
I'll add some logging.
>
>Since we already have a X86_BUG_TDX_PW_MCE which is detected during kernel
>boot in tdx_init(), shouldn't we just follow so that the user can at least
>see the CPU has this erratum?
>
>Another advantage is, if in the future some other kernel code needs to know
>this erratum, it can just consult this flag.
Thanks!
I didn't do that because I wasn't sure if adding a bug bit was justified
without another use case (i.e., this is a one-off check).
But I agree that following the X86_BUG_TDX_PW_MCE is better in consistency
and extensibility. So, here is the refined patch:
From 46e89a50803d6568eb60bd8ec866ac3fd9f6e6da Mon Sep 17 00:00:00 2001
From: Chao Gao <chao.gao@intel.com>
Date: Tue, 10 Mar 2026 18:49:41 -0700
Subject: [PATCH] coco/tdx-host: Don't expose P-SEAMLDR features on CPUs with
erratum
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Some TDX-capable CPUs have an erratum, as documented in Intel® Trust
Domain CPU Architectural Extensions (May 2021 edition) Chapter 2.3:
SEAMRET from the P-SEAMLDR clears the current VMCS structure pointed
to by the current-VMCS pointer. A VMM that invokes the P-SEAMLDR using
SEAMCALL must reload the current-VMCS, if required, using the VMPTRLD
instruction.
Clearing the current VMCS behind KVM's back will break KVM.
This erratum is not present when IA32_VMX_BASIC[60] is set. Add a CPU
bug bit for this erratum and refuse to expose P-SEAMLDR features (e.g.,
TDX module updates) on affected CPUs. Also, emit a message to clarify
why P-SEAMLDR features are disabled for affected CPUs.
== Alternatives ==
Two workarounds were considered but both were rejected:
1. Save/restore the current VMCS around P-SEAMLDR calls. This produces ugly
assembly code [1] and doesn't play well with #MCE or #NMI if they
need to use the current VMCS.
2. Move KVM's VMCS tracking logic to the TDX core code, which would break
the boundary between KVM and the TDX core code [2].
Signed-off-by: Chao Gao <chao.gao@intel.com>
Link: https://lore.kernel.org/kvm/fedb3192-e68c-423c-93b2-a4dc2f964148@intel.com/ # [1]
Link: https://lore.kernel.org/kvm/aYIXFmT-676oN6j0@google.com/ # [2]
---
arch/x86/include/asm/cpufeatures.h | 1 +
arch/x86/include/asm/vmx.h | 1 +
arch/x86/virt/vmx/tdx/tdx.c | 12 ++++++++++++
drivers/virt/coco/tdx-host/tdx-host.c | 5 +++++
4 files changed, 19 insertions(+)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index c3b53beb1300..dab518122946 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -570,4 +570,5 @@
#define X86_BUG_ITS_NATIVE_ONLY X86_BUG( 1*32+ 8) /* "its_native_only" CPU is affected by ITS, VMX is not affected */
#define X86_BUG_TSA X86_BUG( 1*32+ 9) /* "tsa" CPU is affected by Transient Scheduler Attacks */
#define X86_BUG_VMSCAPE X86_BUG( 1*32+10) /* "vmscape" CPU is affected by VMSCAPE attacks from guests */
+#define X86_BUG_SEAMRET_INVD_VMCS X86_BUG( 1*32+11) /* "seamret_invd_vmcs" SEAMRET may clear the current VMCS */
#endif /* _ASM_X86_CPUFEATURES_H */
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index c85c50019523..a467b681e62d 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -135,6 +135,7 @@
#define VMX_BASIC_INOUT BIT_ULL(54)
#define VMX_BASIC_TRUE_CTLS BIT_ULL(55)
#define VMX_BASIC_NO_HW_ERROR_CODE_CC BIT_ULL(56)
+#define VMX_BASIC_NO_SEAMRET_INVD_VMCS BIT_ULL(60)
static inline u32 vmx_basic_vmcs_revision_id(u64 vmx_basic)
{
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 2caedc985fbd..06c8f957a6db 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -39,6 +39,7 @@
#include <asm/cpu_device_id.h>
#include <asm/processor.h>
#include <asm/mce.h>
+#include <asm/vmx.h>
#include "seamcall_internal.h"
#include "tdx.h"
@@ -1453,6 +1454,8 @@ static struct notifier_block tdx_memory_nb = {
static void __init check_tdx_erratum(void)
{
+ u64 basic_msr;
+
/*
* These CPUs have an erratum. A partial write from non-TD
* software (e.g. via MOVNTI variants or UC/WC mapping) to TDX
@@ -1464,6 +1467,15 @@ static void __init check_tdx_erratum(void)
case INTEL_EMERALDRAPIDS_X:
setup_force_cpu_bug(X86_BUG_TDX_PW_MCE);
}
+
+ /*
+ * Some TDX-capable CPUs have an erratum where the current VMCS may
+ * be cleared after calling into P-SEAMLDR. Ensure no such erratum
+ * exists before exposing any P-SEAMLDR functions.
+ */
+ rdmsrq(MSR_IA32_VMX_BASIC, basic_msr);
+ if (!(basic_msr & VMX_BASIC_NO_SEAMRET_INVD_VMCS))
+ setup_force_cpu_bug(X86_BUG_SEAMRET_INVD_VMCS);
}
void __init tdx_init(void)
diff --git a/drivers/virt/coco/tdx-host/tdx-host.c b/drivers/virt/coco/tdx-host/tdx-host.c
index 891cc6a083e0..7e9496e215f6 100644
--- a/drivers/virt/coco/tdx-host/tdx-host.c
+++ b/drivers/virt/coco/tdx-host/tdx-host.c
@@ -182,6 +182,11 @@ static int seamldr_init(struct device *dev)
if (!tdx_supports_runtime_update(tdx_sysinfo))
return 0;
+ if (boot_cpu_has_bug(X86_BUG_SEAMRET_INVD_VMCS)) {
+ pr_info("Cannot talk with P-SEAMLDR due to seamret_invd_vmcs bug\n");
+ return 0;
+ }
+
tdx_fwl = firmware_upload_register(THIS_MODULE, dev, "tdx_module",
&tdx_fw_ops, NULL);
if (IS_ERR(tdx_fwl))
--
2.47.3
>
>And btw,
>
>Which code base was this patch generated? If I read correctly, in this
>series seamldr_init() is a void function but doesn't return anything.
This patch is based on a new version. Yilun suggested changing seamldr_init()
to return an error:
https://lore.kernel.org/kvm/aaEP8CbLCc69U45Z@yilunxu-OptiPlex-7050/
^ permalink raw reply related
* [RFC PATCH v3 12/12] KVM: arm64: CCA: enable DA in realm create parameters
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:07 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
In-Reply-To: <20260312080743.3487326-1-aneesh.kumar@kernel.org>
Now that we have all the required steps for DA in-place, enable
DA while creating realm.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_smc.h | 1 +
arch/arm64/kvm/rmi.c | 3 +++
2 files changed, 4 insertions(+)
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index 384bde2d423e..5598b19ffc1d 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -118,6 +118,7 @@ enum rmi_ripas {
#define RMI_REALM_PARAM_FLAG_LPA2 BIT(0)
#define RMI_REALM_PARAM_FLAG_SVE BIT(1)
#define RMI_REALM_PARAM_FLAG_PMU BIT(2)
+#define RMI_REALM_PARAM_FLAG_DA BIT(3)
/*
* Note many of these fields are smaller than u64 but all fields have u64
diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
index 5de49a47d782..328eef406419 100644
--- a/arch/arm64/kvm/rmi.c
+++ b/arch/arm64/kvm/rmi.c
@@ -703,6 +703,9 @@ static int realm_create_rd(struct kvm *kvm)
if (r)
goto out_undelegate_tables;
+ /* For now default enable DA */
+ if (rmi_has_feature(RMI_FEATURE_REGISTER_0_DA))
+ params->flags |= RMI_REALM_PARAM_FLAG_DA;
params_phys = virt_to_phys(params);
if (rmi_realm_create(rd_phys, params_phys)) {
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v3 11/12] coco: host: arm64: Transition vdevs to TDISP RUN state
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:07 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
In-Reply-To: <20260312080743.3487326-1-aneesh.kumar@kernel.org>
- define SMC_RMI_VDEV_START and the __RHI_DA_VDEV_SET_TDI_STATE
- let the host guest TSM request handler (__RHI_DA_SET_TDI_STATE) accept
RHI_DA_TDI_CONFIG_RUN and call into rmi_vdev_start()
- The RHI_DA_TDI_CONFIG_UNLOCKED and RHI_DA_TDI_CONFIG_LOCKED transition
will be handled by the VMM
- wait for the firmware to report vdev state as RMI_VDEV_STARTED before
returning
With this in place, a guest can move a vdev from LOCKED into the TDISP
RUN state once attestation completes.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rhi.h | 1 +
arch/arm64/include/asm/rmi_cmds.h | 10 ++++++++++
arch/arm64/include/asm/rmi_smc.h | 1 +
arch/arm64/include/uapi/asm/rmi-da.h | 5 +++++
drivers/virt/coco/arm-cca-host/arm-cca.c | 15 +++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 22 ++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 1 +
7 files changed, 55 insertions(+)
diff --git a/arch/arm64/include/asm/rhi.h b/arch/arm64/include/asm/rhi.h
index ba9e11152c1b..68780918e28b 100644
--- a/arch/arm64/include/asm/rhi.h
+++ b/arch/arm64/include/asm/rhi.h
@@ -86,5 +86,6 @@ enum rhi_tdi_state {
#define __RHI_DA_VDEV_GET_MEASUREMENTS 0x4
#define __REC_EXIT_DA_VDEV_REQUEST 0x5
#define __REC_EXIT_DA_VDEV_MAP 0x6
+#define __RHI_DA_VDEV_SET_TDI_STATE 0x7
#endif
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 0c06a4f45346..688414f695f7 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -686,6 +686,16 @@ rmi_vdev_get_device_measurements(unsigned long rd, unsigned long pdev_phys,
return res.a0;
}
+static inline unsigned long rmi_vdev_start(unsigned long rd, unsigned long pdev_phys,
+ unsigned long vdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_START, rd, pdev_phys, vdev_phys, &res);
+
+ return res.a0;
+}
+
static inline unsigned long rmi_vdev_complete(unsigned long rec_phys, unsigned long vdev_phys)
{
struct arm_smccc_res res;
diff --git a/arch/arm64/include/asm/rmi_smc.h b/arch/arm64/include/asm/rmi_smc.h
index f4b8f1c9ba0b..384bde2d423e 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -67,6 +67,7 @@
#define SMC_RMI_VDEV_GET_INTERFACE_REPORT SMC_RMI_CALL(0x01D0)
#define SMC_RMI_VDEV_GET_DEV_MEASUREMENTS SMC_RMI_CALL(0x01D1)
#define SMC_RMI_VDEV_LOCK SMC_RMI_CALL(0x01D2)
+#define SMC_RMI_VDEV_START SMC_RMI_CALL(0x01D3)
#define RMI_ABI_MAJOR_VERSION 1
#define RMI_ABI_MINOR_VERSION 0
diff --git a/arch/arm64/include/uapi/asm/rmi-da.h b/arch/arm64/include/uapi/asm/rmi-da.h
index 20d3eab8ce64..dc2855cb05a8 100644
--- a/arch/arm64/include/uapi/asm/rmi-da.h
+++ b/arch/arm64/include/uapi/asm/rmi-da.h
@@ -35,4 +35,9 @@ struct arm64_vdev_device_memmap_guest_req {
__aligned_u64 pa_base;
};
+struct arm64_vdev_set_tdi_state_guest_req {
+ __u32 req_type;
+ __u32 tdi_state;
+};
+
#endif
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index 405542ffd9d1..9883bf9e0470 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -402,6 +402,21 @@ static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi, enum pci_tsm_req_scope sco
req_obj.gpa_top,
req_obj.pa_base);
}
+ case __RHI_DA_VDEV_SET_TDI_STATE:
+ {
+ struct arm64_vdev_set_tdi_state_guest_req req_obj;
+
+ if (req_len != sizeof(req_obj))
+ return -EINVAL;
+
+ if (copy_from_user((void *)&req_obj, req.user, req_len))
+ return -EFAULT;
+
+ if (req_obj.tdi_state != RHI_DA_TDI_CONFIG_RUN)
+ return -EINVAL;
+
+ return cca_vdev_device_start(pdev);
+ }
default:
return -EINVAL;
}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index d76095a3e6c3..877a649dea13 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -1145,3 +1145,25 @@ int cca_vdev_device_map_validate(struct pci_dev *pdev, unsigned long vcpu_fd,
return realm_dev_mem_map(kvm, rec_phys, rmm_pdev_phys,
rmm_vdev_phys, gpa_base, gpa_top, pa_base);
}
+
+int cca_vdev_device_start(struct pci_dev *pdev)
+{
+ phys_addr_t rmm_pdev_phys;
+ phys_addr_t rmm_vdev_phys;
+ struct cca_host_pf0_dsc *pf0_dsc;
+ struct cca_host_tdi *host_tdi;
+ struct realm *realm;
+ phys_addr_t rd_phys;
+
+ host_tdi = to_cca_host_tdi(pdev);
+ rmm_vdev_phys = virt_to_phys(host_tdi->rmm_vdev);
+ realm = &host_tdi->tdi.kvm->arch.realm;
+ rd_phys = virt_to_phys(realm->rd);
+
+ pf0_dsc = to_cca_pf0_dsc(pdev->tsm->dsm_dev);
+ rmm_pdev_phys = virt_to_phys(pf0_dsc->rmm_pdev);
+
+ if (rmi_vdev_start(rd_phys, rmm_pdev_phys, rmm_vdev_phys))
+ return -ENXIO;
+ return submit_vdev_state_transition_work(pdev, RMI_VDEV_STARTED);
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 60b10bce3140..51ef49cb482b 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -157,4 +157,5 @@ int cca_vdev_device_request(struct pci_dev *pdev, unsigned long rec_id);
int cca_vdev_device_map_validate(struct pci_dev *pdev, unsigned long vcpu_fd,
unsigned long gpa_base, unsigned long gpa_top,
unsigned long pa_base);
+int cca_vdev_device_start(struct pci_dev *pdev);
#endif
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v3 10/12] KVM: arm64: Unmap device mappings when a private granule is destroyed
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:07 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
In-Reply-To: <20260312080743.3487326-1-aneesh.kumar@kernel.org>
Ensure tearing down a private granule also tears down any RMM device
mapping by reading the RTT entry, invoking the new RMI_VDEV_MEM_UNMAP,
and remembering the entry’s RIPAS so we only free RAM pages.
Drive the device-unmap path when RIPAS transitions to EMPTY. Also roll
back partially built device maps when errors occur.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 15 +++++++
arch/arm64/include/asm/rmi_smc.h | 2 +
arch/arm64/kvm/rmi.c | 65 +++++++++++++++++++++++++++----
3 files changed, 74 insertions(+), 8 deletions(-)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 53bffaace64c..0c06a4f45346 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -721,4 +721,19 @@ static inline int rmi_vdev_mem_map(unsigned long rd, unsigned long vdev_phys,
return res.a0;
}
+static inline int rmi_vdev_mem_unmap(unsigned long rd, unsigned long ipa, unsigned long level,
+ unsigned long *out_pa, unsigned long *out_ipa)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_MEM_UNMAP, rd, ipa, level, &res);
+
+ if (out_pa)
+ *out_pa = res.a1;
+ if (out_ipa)
+ *out_ipa = res.a2;
+
+ 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 41ee49c341c0..f4b8f1c9ba0b 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -49,6 +49,7 @@
#define SMC_RMI_RTT_SET_RIPAS SMC_RMI_CALL(0x0169)
#define SMC_RMI_VDEV_MEM_MAP SMC_RMI_CALL(0x0172)
+#define SMC_RMI_VDEV_MEM_UNMAP SMC_RMI_CALL(0x0173)
#define SMC_RMI_PDEV_ABORT SMC_RMI_CALL(0x0174)
#define SMC_RMI_PDEV_COMMUNICATE SMC_RMI_CALL(0x0175)
#define SMC_RMI_PDEV_CREATE SMC_RMI_CALL(0x0176)
@@ -92,6 +93,7 @@ enum rmi_ripas {
RMI_EMPTY = 0,
RMI_RAM = 1,
RMI_DESTROYED = 2,
+ RMI_DEV = 3,
};
#define RMI_NO_MEASURE_CONTENT 0
diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
index bb338712ef34..5de49a47d782 100644
--- a/arch/arm64/kvm/rmi.c
+++ b/arch/arm64/kvm/rmi.c
@@ -454,15 +454,26 @@ void kvm_realm_destroy_rtts(struct kvm *kvm, u32 ia_bits)
static int realm_destroy_private_granule(struct realm *realm,
unsigned long ipa,
unsigned long *next_addr,
- phys_addr_t *out_rtt)
+ phys_addr_t *out_rtt,
+ int *ripas)
{
unsigned long rd = virt_to_phys(realm->rd);
unsigned long rtt_addr;
+ struct rtt_entry rtt_entry;
phys_addr_t rtt;
int ret;
+ // mmu_lock avoids parallel rte modification?
+ ret = rmi_rtt_read_entry(rd, ipa, RMM_RTT_MAX_LEVEL, &rtt_entry);
+ if (ret != RMI_SUCCESS)
+ return -ENXIO;
+
retry:
- ret = rmi_data_destroy(rd, ipa, &rtt_addr, next_addr);
+ if (rtt_entry.ripas == RMI_DEV)
+ ret = rmi_vdev_mem_unmap(rd, ipa, RMM_RTT_MAX_LEVEL, &rtt_addr, next_addr);
+ else
+ ret = rmi_data_destroy(rd, ipa, &rtt_addr, next_addr);
+
if (RMI_RETURN_STATUS(ret) == RMI_ERROR_RTT) {
if (*next_addr > ipa)
return 0; /* UNASSIGNED */
@@ -490,6 +501,7 @@ static int realm_destroy_private_granule(struct realm *realm,
return -ENXIO;
*out_rtt = rtt_addr;
+ *ripas = rtt_entry.ripas;
return 0;
}
@@ -501,16 +513,16 @@ static int realm_unmap_private_page(struct realm *realm,
unsigned long end = ALIGN(ipa + 1, PAGE_SIZE);
unsigned long addr;
phys_addr_t out_rtt = PHYS_ADDR_MAX;
- int ret;
+ int ret, ripas;
for (addr = ipa; addr < end; addr = *next_addr) {
ret = realm_destroy_private_granule(realm, addr, next_addr,
- &out_rtt);
+ &out_rtt, &ripas);
if (ret)
return ret;
}
- if (out_rtt != PHYS_ADDR_MAX) {
+ if (out_rtt != PHYS_ADDR_MAX && ripas != RMI_DEV) {
out_rtt = ALIGN_DOWN(out_rtt, PAGE_SIZE);
free_page((unsigned long)phys_to_virt(out_rtt));
}
@@ -1226,10 +1238,27 @@ static int realm_set_ipa_state(struct kvm_vcpu *vcpu,
unsigned long *top_ipa)
{
struct kvm *kvm = vcpu->kvm;
- int ret = ripas_change(kvm, vcpu, start, end, RIPAS_SET, top_ipa);
+ int ret;
- if (ripas == RMI_EMPTY && *top_ipa != start)
- realm_unmap_private_range(kvm, start, *top_ipa, false);
+ /*
+ * We use the RIPAS value to decide between a data_destroy or a
+ * dev_mem_unmap. Hence call realm_unmap_private_range() before
+ * ripas_change().
+ *
+ * Technically, for private RAM, we don't need to call
+ * realm_unmap_private_range(), because any RIPAS change via RSI would
+ * trigger a memory fault exit. That would, in turn, invalidate the
+ * guest's memfd range, which then triggers realm_unmap_private_range()
+ * automatically.
+ *
+ * However, this doesn’t apply to RIPAS_DEV, because we currently
+ * lack a user-space API to call realm_dev_mem_unmap() in response to a
+ * memory fault exit. Therefore, the unmap must happen explicitly before
+ * the RIPAS change.
+ */
+ if (ripas == RMI_EMPTY)
+ realm_unmap_private_range(kvm, start, end, false);
+ ret = ripas_change(kvm, vcpu, start, end, RIPAS_SET, top_ipa);
return ret;
}
@@ -1587,7 +1616,9 @@ int realm_dev_mem_map(struct kvm *kvm, unsigned long rec_phys,
int ret;
unsigned long top_ipa;
unsigned long base_ipa = start_ipa;
+ struct realm *realm = &kvm->arch.realm;
struct kvm_s2_mmu *mmu = &kvm->arch.mmu;
+ phys_addr_t rd_phys = virt_to_phys(realm->rd);
struct kvm_mmu_memory_cache cache = { .gfp_zero = __GFP_ZERO };
do {
@@ -1614,6 +1645,24 @@ int realm_dev_mem_map(struct kvm *kvm, unsigned long rec_phys,
for (start_ipa = ALIGN(base_ipa, RMM_L2_BLOCK_SIZE);
((start_ipa + RMM_L2_BLOCK_SIZE) < end_ipa); start_ipa += RMM_L2_BLOCK_SIZE)
fold_rtt(&kvm->arch.realm, start_ipa, RMM_RTT_BLOCK_LEVEL);
+ } else {
+ /* unmap the partial mapping. */
+ while (start_ipa > base_ipa) {
+ unsigned long out_pa;
+ unsigned long out_ipa;
+
+ /* start_ipa is highest mapped ipa */
+ start_pa -= RMM_PAGE_SIZE;
+ start_ipa -= RMM_PAGE_SIZE;
+
+ WARN_ON(rmi_vdev_mem_unmap(rd_phys, start_ipa,
+ RMM_RTT_MAX_LEVEL, &out_pa, &out_ipa));
+
+ WARN_ON(start_pa != out_pa);
+ WARN_ON(start_ipa + RMM_PAGE_SIZE != out_ipa);
+ WARN_ON(rmi_granule_undelegate(out_pa));
+
+ }
}
return ret;
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v3 09/12] coco: host: KVM: arm64: Handle vdev map/validation exits
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:07 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
In-Reply-To: <20260312080743.3487326-1-aneesh.kumar@kernel.org>
- define the RMM SMCCC IDs (and wrappers) for VDEV_VALIDATE_MAPPING
and VDEV_MEM_MAP, add the matching RHI request IDs, and extend the REC
exit payload to carry GPA/HPA details for mapping exits
- update KVM to recognize RMI_EXIT_VDEV_MAP and surface it to
userspace via KVM_EXIT_ARM64_TIO
- use the new realm_dev_mem_map() to map device memory.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/kvm_rmi.h | 4 +
arch/arm64/include/asm/rhi.h | 1 +
arch/arm64/include/asm/rmi_cmds.h | 26 +++++
arch/arm64/include/asm/rmi_smc.h | 4 +
arch/arm64/include/uapi/asm/rmi-da.h | 8 ++
arch/arm64/kvm/rmi-exit.c | 38 ++++++++
arch/arm64/kvm/rmi.c | 115 +++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/arm-cca.c | 28 ++++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 37 ++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 3 +
10 files changed, 264 insertions(+)
diff --git a/arch/arm64/include/asm/kvm_rmi.h b/arch/arm64/include/asm/kvm_rmi.h
index a967061af6ed..0a38a489fd53 100644
--- a/arch/arm64/include/asm/kvm_rmi.h
+++ b/arch/arm64/include/asm/kvm_rmi.h
@@ -134,4 +134,8 @@ static inline bool kvm_realm_is_private_address(struct realm *realm,
return !(addr & BIT(realm->ia_bits - 1));
}
+int realm_dev_mem_map(struct kvm *kvm, unsigned long rec_phys,
+ unsigned long pdev_phys, unsigned long vdev_phys,
+ unsigned long start_ipa, unsigned long end_ipa,
+ unsigned long start_pa);
#endif /* __ASM_KVM_RMI_H */
diff --git a/arch/arm64/include/asm/rhi.h b/arch/arm64/include/asm/rhi.h
index 888b3a1c3953..ba9e11152c1b 100644
--- a/arch/arm64/include/asm/rhi.h
+++ b/arch/arm64/include/asm/rhi.h
@@ -85,5 +85,6 @@ enum rhi_tdi_state {
#define __RHI_DA_VDEV_GET_INTERFACE_REPORT 0x3
#define __RHI_DA_VDEV_GET_MEASUREMENTS 0x4
#define __REC_EXIT_DA_VDEV_REQUEST 0x5
+#define __REC_EXIT_DA_VDEV_MAP 0x6
#endif
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index f29c2de5d3b9..53bffaace64c 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -695,4 +695,30 @@ static inline unsigned long rmi_vdev_complete(unsigned long rec_phys, unsigned l
return res.a0;
}
+static inline int rmi_vdev_validate_mapping(unsigned long rd, unsigned long rec_phys,
+ unsigned long pdev_phys, unsigned long vdev_phys,
+ unsigned long base, unsigned long top,
+ unsigned long *out_top)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_VALIDATE_MAPPING, rd,
+ rec_phys, pdev_phys, vdev_phys, base, top, &res);
+
+ if (out_top)
+ *out_top = res.a1;
+
+ return res.a0;
+}
+
+static inline int rmi_vdev_mem_map(unsigned long rd, unsigned long vdev_phys,
+ unsigned long ipa, unsigned long level, unsigned long pa)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_MEM_MAP, rd, vdev_phys, ipa, level, pa, &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 6b685585e750..41ee49c341c0 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -39,6 +39,7 @@
#define SMC_RMI_RTT_READ_ENTRY SMC_RMI_CALL(0x0161)
#define SMC_RMI_RTT_UNMAP_UNPROTECTED SMC_RMI_CALL(0x0162)
+#define SMC_RMI_VDEV_VALIDATE_MAPPING SMC_RMI_CALL(0x0163)
#define SMC_RMI_PSCI_COMPLETE SMC_RMI_CALL(0x0164)
#define SMC_RMI_FEATURES SMC_RMI_CALL(0x0165)
@@ -47,6 +48,7 @@
#define SMC_RMI_RTT_INIT_RIPAS SMC_RMI_CALL(0x0168)
#define SMC_RMI_RTT_SET_RIPAS SMC_RMI_CALL(0x0169)
+#define SMC_RMI_VDEV_MEM_MAP SMC_RMI_CALL(0x0172)
#define SMC_RMI_PDEV_ABORT SMC_RMI_CALL(0x0174)
#define SMC_RMI_PDEV_COMMUNICATE SMC_RMI_CALL(0x0175)
#define SMC_RMI_PDEV_CREATE SMC_RMI_CALL(0x0176)
@@ -187,6 +189,7 @@ struct rec_params {
#define REC_ENTER_FLAG_TRAP_WFI BIT(2)
#define REC_ENTER_FLAG_TRAP_WFE BIT(3)
#define REC_ENTER_FLAG_RIPAS_RESPONSE BIT(4)
+#define REC_ENTER_FLAG_DEV_MEM_RESPONSE BIT(6)
#define REC_RUN_GPRS 31
#define REC_MAX_GIC_NUM_LRS 16
@@ -227,6 +230,7 @@ struct rec_enter {
#define RMI_EXIT_HOST_CALL 0x05
#define RMI_EXIT_SERROR 0x06
#define RMI_EXIT_VDEV_REQUEST 0x08
+#define RMI_EXIT_VDEV_MAP 0x09
struct rec_exit {
union { /* 0x000 */
diff --git a/arch/arm64/include/uapi/asm/rmi-da.h b/arch/arm64/include/uapi/asm/rmi-da.h
index ac6e2fd2807d..20d3eab8ce64 100644
--- a/arch/arm64/include/uapi/asm/rmi-da.h
+++ b/arch/arm64/include/uapi/asm/rmi-da.h
@@ -27,4 +27,12 @@ struct arm64_vdev_device_idmap_guest_req {
__s32 vcpu_fd;
};
+struct arm64_vdev_device_memmap_guest_req {
+ __u32 req_type;
+ __s32 vcpu_fd;
+ __aligned_u64 gpa_base;
+ __aligned_u64 gpa_top;
+ __aligned_u64 pa_base;
+};
+
#endif
diff --git a/arch/arm64/kvm/rmi-exit.c b/arch/arm64/kvm/rmi-exit.c
index 3bba5e6afe88..c1605b03a32d 100644
--- a/arch/arm64/kvm/rmi-exit.c
+++ b/arch/arm64/kvm/rmi-exit.c
@@ -144,6 +144,42 @@ static int rec_exit_vdev_request(struct kvm_vcpu *vcpu)
kvm_prepare_vdev_request_exit(vcpu, rec->run->exit.vdev_id_1);
return 0;
}
+
+static inline void kvm_prepare_vdev_validate_mapping_exit(struct kvm_vcpu *vcpu,
+ gpa_t gpa_base, gpa_t gpa_top,
+ hpa_t pa_base, unsigned long vdev_id)
+{
+ vcpu->run->exit_reason = KVM_EXIT_ARM64_TIO;
+ vcpu->run->cca_exit.nr = RMI_EXIT_VDEV_MAP;
+ vcpu->run->cca_exit.vdev_id = vdev_id;
+ vcpu->run->cca_exit.flags = 0;
+ vcpu->run->cca_exit.gpa_base = gpa_base;
+ vcpu->run->cca_exit.gpa_top = gpa_top;
+ vcpu->run->cca_exit.pa_base = pa_base;
+}
+
+static int rec_exit_vdev_validate_mapping(struct kvm_vcpu *vcpu)
+{
+ struct kvm *kvm = vcpu->kvm;
+ struct realm *realm = &kvm->arch.realm;
+ struct realm_rec *rec = &vcpu->arch.rec;
+ unsigned long base = rec->run->exit.dev_mem_base;
+ unsigned long top = rec->run->exit.dev_mem_top;
+
+ if (!kvm_realm_is_private_address(realm, base) ||
+ !kvm_realm_is_private_address(realm, top - 1)) {
+
+ /* Set RMI_REJECT bit */
+ rec->run->enter.flags = REC_ENTER_FLAG_DEV_MEM_RESPONSE;
+ vcpu_err(vcpu, "Invalid DEV_MEM_VALIDATE for %#lx - %#lx\n", base, top);
+ return -EINVAL;
+ }
+
+ kvm_prepare_vdev_validate_mapping_exit(vcpu, base, top, rec->run->exit.dev_mem_pa,
+ rec->run->exit.vdev_id_1);
+ return 0;
+}
+
static void update_arch_timer_irq_lines(struct kvm_vcpu *vcpu)
{
struct realm_rec *rec = &vcpu->arch.rec;
@@ -215,6 +251,8 @@ int handle_rec_exit(struct kvm_vcpu *vcpu, int rec_run_ret)
return rec_exit_host_call(vcpu);
case RMI_EXIT_VDEV_REQUEST:
return rec_exit_vdev_request(vcpu);
+ case RMI_EXIT_VDEV_MAP:
+ return rec_exit_vdev_validate_mapping(vcpu);
}
kvm_pr_unimpl("Unsupported exit reason: %u\n",
diff --git a/arch/arm64/kvm/rmi.c b/arch/arm64/kvm/rmi.c
index 08f3d2362dfd..bb338712ef34 100644
--- a/arch/arm64/kvm/rmi.c
+++ b/arch/arm64/kvm/rmi.c
@@ -1505,6 +1505,121 @@ static void kvm_complete_ripas_change(struct kvm_vcpu *vcpu)
rec->run->exit.ripas_base = base;
}
+/*
+ * Even though we can map larger block, since we need to delegate each granule.
+ * We map granule size and fold
+ */
+static int __realm_dev_mem_map(struct kvm *kvm,
+ struct kvm_mmu_memory_cache *cache, unsigned long rec_phys,
+ unsigned long pdev_phys, unsigned long vdev_phys,
+ unsigned long start_ipa, unsigned long end_ipa,
+ phys_addr_t phys, unsigned long *top_ipa)
+{
+ int ret = 0;
+ unsigned long rmi_ret;
+ unsigned long ipa, next_ipa;
+ struct realm *realm = &kvm->arch.realm;
+ phys_addr_t rd_phys = virt_to_phys(realm->rd);
+
+ for (ipa = start_ipa ; ipa < end_ipa; ipa += RMM_PAGE_SIZE) {
+
+ if (rmi_granule_delegate(phys)) {
+ ret = -EINVAL;
+ goto err_delegate;
+ }
+
+ rmi_ret = rmi_vdev_mem_map(rd_phys, vdev_phys,
+ ipa, RMM_RTT_MAX_LEVEL, phys);
+ if (RMI_RETURN_STATUS(rmi_ret) == RMI_ERROR_RTT) {
+ /* Create missing RTTs and retry */
+ int level = RMI_RETURN_INDEX(rmi_ret);
+
+ ret = realm_create_rtt_levels(realm, ipa, level,
+ RMM_RTT_MAX_LEVEL,
+ cache);
+ if (ret)
+ goto err_vdev_mem_map;
+
+ if (rmi_vdev_mem_map(rd_phys, vdev_phys,
+ ipa, RMM_RTT_MAX_LEVEL, phys))
+ ret = -ENXIO;
+ }
+ if (ret)
+ goto err_vdev_mem_map;
+
+ phys += RMM_PAGE_SIZE;
+ }
+
+ /*
+ * Return the highest mapped IPA within the range
+ * (processed by vdev_mem_map)
+ */
+ *top_ipa = end_ipa;
+
+ while (start_ipa < end_ipa) {
+ /* now validate the device memory mapping */
+ if (rmi_vdev_validate_mapping(rd_phys, rec_phys, pdev_phys,
+ vdev_phys, start_ipa, end_ipa, &next_ipa)) {
+ /*
+ * We can't find the RTT error here, because
+ * things are already setup by dev_mem_map before
+ * Caller will do the unmap and undelegate
+ */
+ return -ENXIO;
+ }
+ start_ipa = next_ipa;
+ }
+
+ return 0;
+
+ err_vdev_mem_map:
+ WARN_ON(rmi_granule_undelegate(phys));
+ err_delegate:
+ *top_ipa = ipa - RMM_PAGE_SIZE;
+ return ret;
+}
+
+int realm_dev_mem_map(struct kvm *kvm, unsigned long rec_phys,
+ unsigned long pdev_phys, unsigned long vdev_phys,
+ unsigned long start_ipa, unsigned long end_ipa,
+ unsigned long start_pa)
+{
+ int ret;
+ unsigned long top_ipa;
+ unsigned long base_ipa = start_ipa;
+ struct kvm_s2_mmu *mmu = &kvm->arch.mmu;
+ struct kvm_mmu_memory_cache cache = { .gfp_zero = __GFP_ZERO };
+
+ do {
+ ret = kvm_mmu_topup_memory_cache(&cache,
+ kvm_mmu_cache_min_pages(mmu));
+ if (ret)
+ break;
+
+ write_lock(&kvm->mmu_lock);
+ ret = __realm_dev_mem_map(kvm, &cache, rec_phys, pdev_phys,
+ vdev_phys, start_ipa, end_ipa, start_pa, &top_ipa);
+ write_unlock(&kvm->mmu_lock);
+
+ /* update base before we break out of loop*/
+ start_pa += top_ipa - start_ipa;
+ start_ipa = top_ipa;
+ if (ret && ret != -ENOMEM)
+ break;
+ } while (start_ipa < end_ipa);
+
+ kvm_mmu_free_memory_cache(&cache);
+ if (!ret) {
+ /* fold rtts if we can */
+ for (start_ipa = ALIGN(base_ipa, RMM_L2_BLOCK_SIZE);
+ ((start_ipa + RMM_L2_BLOCK_SIZE) < end_ipa); start_ipa += RMM_L2_BLOCK_SIZE)
+ fold_rtt(&kvm->arch.realm, start_ipa, RMM_RTT_BLOCK_LEVEL);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(realm_dev_mem_map);
+
/*
* kvm_rec_pre_enter - Complete operations before entering a REC
*
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index 8aa362f44090..405542ffd9d1 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -378,6 +378,34 @@ static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi, enum pci_tsm_req_scope sco
return -EINVAL;
}
}
+ case PCI_TSM_REQ_STATE_CHANGE:
+ {
+ u32 req_type;
+
+ if (get_user(req_type, (u32 __user *)req.user))
+ return -EFAULT;
+
+ switch (req_type) {
+
+ case __REC_EXIT_DA_VDEV_MAP:
+ {
+ struct arm64_vdev_device_memmap_guest_req req_obj;
+
+ if (req_len != sizeof(req_obj))
+ return -EINVAL;
+
+ if (copy_from_user((void *)&req_obj, req.user, req_len))
+ return -EFAULT;
+
+ return cca_vdev_device_map_validate(pdev, req_obj.vcpu_fd,
+ req_obj.gpa_base,
+ req_obj.gpa_top,
+ req_obj.pa_base);
+ }
+ default:
+ return -EINVAL;
+ }
+ }
default:
return -EINVAL;
}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 3c19dfe89c0a..d76095a3e6c3 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -1108,3 +1108,40 @@ int cca_vdev_device_request(struct pci_dev *pdev, unsigned long vcpu_fd)
return -ENXIO;
return 0;
}
+
+int cca_vdev_device_map_validate(struct pci_dev *pdev, unsigned long vcpu_fd,
+ unsigned long gpa_base, unsigned long gpa_top,
+ unsigned long pa_base)
+{
+ struct kvm *kvm;
+ struct realm *realm;
+ phys_addr_t rec_phys;
+ struct kvm_vcpu *vcpu;
+ phys_addr_t rmm_pdev_phys;
+ phys_addr_t rmm_vdev_phys;
+ struct cca_host_tdi *host_tdi;
+ struct cca_host_pf0_dsc *pf0_dsc;
+ struct file *vcpu_filp __free(fput) = fget(vcpu_fd);
+
+ if (!file_is_vcpu(vcpu_filp))
+ return -EINVAL;
+
+ vcpu = vcpu_filp->private_data;
+ if (!vcpu)
+ return -EINVAL;
+
+ host_tdi = to_cca_host_tdi(pdev);
+ pf0_dsc = to_cca_pf0_dsc(pdev->tsm->dsm_dev);
+ kvm = host_tdi->tdi.kvm;
+ realm = &kvm->arch.realm;
+ rec_phys = virt_to_phys(vcpu->arch.rec.rec_page);
+ rmm_vdev_phys = virt_to_phys(host_tdi->rmm_vdev);
+ rmm_pdev_phys = virt_to_phys(pf0_dsc->rmm_pdev);
+
+ /* make sure this is the same vm */
+ if (vcpu->kvm != kvm)
+ return -EINVAL;
+
+ return realm_dev_mem_map(kvm, rec_phys, rmm_pdev_phys,
+ rmm_vdev_phys, gpa_base, gpa_top, pa_base);
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 2547afa1256f..60b10bce3140 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -154,4 +154,7 @@ int cca_vdev_read_cached_object(struct pci_dev *pdev, int type, unsigned long of
int cca_vdev_get_interface_report(struct pci_dev *pdev);
int cca_vdev_get_device_measurements(struct pci_dev *pdev, unsigned long flags, u8 *nonce);
int cca_vdev_device_request(struct pci_dev *pdev, unsigned long rec_id);
+int cca_vdev_device_map_validate(struct pci_dev *pdev, unsigned long vcpu_fd,
+ unsigned long gpa_base, unsigned long gpa_top,
+ unsigned long pa_base);
#endif
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v3 08/12] coco: host: KVM: arm64: Handle vdev request exits and completion
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:07 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
In-Reply-To: <20260312080743.3487326-1-aneesh.kumar@kernel.org>
- add the RMI/RHI definitions for RMI_VDEV_COMPLETE, the new exit
reason, and the extended REC exit payload
- update KVM to recognize RMI_EXIT_VDEV_REQUEST and surface it to
userspace via KVM_EXIT_ARM64_TIO
- Add CCA TSM guest request handler for __REC_EXIT_DA_VDEV_REQUEST which
takes a vCPU fd and verify it belongs to the same VM before calling
rmi_vdev_complete()
This lets Realm firmware hand control back to the VMM when it needs host
assistance for vdev operations, and gives userspace a way to finish the
request.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
Documentation/virt/kvm/api.rst | 22 +++++++++++++++++
arch/arm64/include/asm/rhi.h | 1 +
arch/arm64/include/asm/rmi_cmds.h | 10 ++++++++
arch/arm64/include/asm/rmi_smc.h | 17 ++++++++++++--
arch/arm64/include/uapi/asm/rmi-da.h | 5 ++++
arch/arm64/kvm/rmi-exit.c | 17 ++++++++++++++
drivers/virt/coco/arm-cca-host/arm-cca.c | 12 ++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 30 ++++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 2 ++
include/linux/kvm_host.h | 1 +
include/uapi/linux/kvm.h | 10 ++++++++
virt/kvm/kvm_main.c | 6 +++++
12 files changed, 131 insertions(+), 2 deletions(-)
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index bd2f0dd0aeda..041009307ee8 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -7429,6 +7429,28 @@ the ``KVM_EXIT_ARM_SEA_FLAG_GPA_VALID`` flag is set. Otherwise, the value of
``gpa`` is unknown.
::
+ /* KVM_EXIT_ARM64_TIO*/
+ struct {
+ __u64 flags;
+ __u64 nr;
+ __u64 vdev_id;
+ __u64 gpa_base;
+ __u64 gpa_top;
+ __u64 pa_base;
+ } cca_exit;
+
+Used on arm64 systems. When the VM capability ``KVM_CAP_ARM_RMI`` is enabled,
+KVM generates a VM exit whenever the guest needs host assistance to map a vdev
+ID to a vdev object, or to validate a device-memory GPA-to-PA mapping. The
+``nr`` field records the exit reason; currently the following values are
+defined:
+
+* ``RMI_EXIT_VDEV_REQUEST``: the RMM is requiring host to provide the vdev
+ object details matching a specific virtual device id.
+* ``RMI_EXIT_VDEV_MAP``: the guest wants the host to validate or install a
+ device-memory mapping.
+
+The ``flags`` field must be zero.
/* Fix the size of the union. */
char padding[256];
diff --git a/arch/arm64/include/asm/rhi.h b/arch/arm64/include/asm/rhi.h
index a18ad7bbc028..888b3a1c3953 100644
--- a/arch/arm64/include/asm/rhi.h
+++ b/arch/arm64/include/asm/rhi.h
@@ -84,5 +84,6 @@ enum rhi_tdi_state {
#define __RHI_DA_OBJECT_READ 0x2
#define __RHI_DA_VDEV_GET_INTERFACE_REPORT 0x3
#define __RHI_DA_VDEV_GET_MEASUREMENTS 0x4
+#define __REC_EXIT_DA_VDEV_REQUEST 0x5
#endif
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index aad245675c7d..f29c2de5d3b9 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -685,4 +685,14 @@ rmi_vdev_get_device_measurements(unsigned long rd, unsigned long pdev_phys,
return res.a0;
}
+
+static inline unsigned long rmi_vdev_complete(unsigned long rec_phys, unsigned long vdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_COMPLETE, rec_phys, vdev_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 36c3db8b821d..6b685585e750 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -60,6 +60,7 @@
#define SMC_RMI_VDEV_DESTROY SMC_RMI_CALL(0x0188)
#define SMC_RMI_VDEV_GET_STATE SMC_RMI_CALL(0x0189)
#define SMC_RMI_VDEV_UNLOCK SMC_RMI_CALL(0x018A)
+#define SMC_RMI_VDEV_COMPLETE SMC_RMI_CALL(0x018e)
#define SMC_RMI_VDEV_GET_INTERFACE_REPORT SMC_RMI_CALL(0x01D0)
#define SMC_RMI_VDEV_GET_DEV_MEASUREMENTS SMC_RMI_CALL(0x01D1)
#define SMC_RMI_VDEV_LOCK SMC_RMI_CALL(0x01D2)
@@ -225,6 +226,7 @@ struct rec_enter {
#define RMI_EXIT_RIPAS_CHANGE 0x04
#define RMI_EXIT_HOST_CALL 0x05
#define RMI_EXIT_SERROR 0x06
+#define RMI_EXIT_VDEV_REQUEST 0x08
struct rec_exit {
union { /* 0x000 */
@@ -266,12 +268,23 @@ struct rec_exit {
u64 ripas_base;
u64 ripas_top;
u8 ripas_value;
- u8 padding8[7];
+ u8 padding8[15];
+ u64 s2ap_base;
+ u64 s2ap_top;
+ u64 vdev_id_1;
+ u64 vdev_id_2;
+ u64 dev_mem_base;
+ u64 dev_mem_top;
+ u64 dev_mem_pa;
};
u8 padding5[0x100];
};
union { /* 0x600 */
- u16 imm;
+ struct {
+ u16 imm;
+ u8 padding[6];
+ u64 plane;
+ };
u8 padding6[0x100];
};
union { /* 0x700 */
diff --git a/arch/arm64/include/uapi/asm/rmi-da.h b/arch/arm64/include/uapi/asm/rmi-da.h
index 1c21a5e78eb5..ac6e2fd2807d 100644
--- a/arch/arm64/include/uapi/asm/rmi-da.h
+++ b/arch/arm64/include/uapi/asm/rmi-da.h
@@ -22,4 +22,9 @@ struct arm64_vdev_device_measurement_guest_req {
__aligned_u64 nonce;
};
+struct arm64_vdev_device_idmap_guest_req {
+ __u32 req_type;
+ __s32 vcpu_fd;
+};
+
#endif
diff --git a/arch/arm64/kvm/rmi-exit.c b/arch/arm64/kvm/rmi-exit.c
index 7eff6967530c..3bba5e6afe88 100644
--- a/arch/arm64/kvm/rmi-exit.c
+++ b/arch/arm64/kvm/rmi-exit.c
@@ -129,6 +129,21 @@ static int rec_exit_host_call(struct kvm_vcpu *vcpu)
return kvm_smccc_call_handler(vcpu);
}
+static inline void kvm_prepare_vdev_request_exit(struct kvm_vcpu *vcpu, unsigned long vdev_id)
+{
+ vcpu->run->exit_reason = KVM_EXIT_ARM64_TIO;
+ vcpu->run->cca_exit.nr = RMI_EXIT_VDEV_REQUEST;
+ vcpu->run->cca_exit.vdev_id = vdev_id;
+ vcpu->run->cca_exit.flags = 0;
+}
+
+static int rec_exit_vdev_request(struct kvm_vcpu *vcpu)
+{
+ struct realm_rec *rec = &vcpu->arch.rec;
+
+ kvm_prepare_vdev_request_exit(vcpu, rec->run->exit.vdev_id_1);
+ return 0;
+}
static void update_arch_timer_irq_lines(struct kvm_vcpu *vcpu)
{
struct realm_rec *rec = &vcpu->arch.rec;
@@ -198,6 +213,8 @@ int handle_rec_exit(struct kvm_vcpu *vcpu, int rec_run_ret)
return rec_exit_ripas_change(vcpu);
case RMI_EXIT_HOST_CALL:
return rec_exit_host_call(vcpu);
+ case RMI_EXIT_VDEV_REQUEST:
+ return rec_exit_vdev_request(vcpu);
}
kvm_pr_unimpl("Unsupported exit reason: %u\n",
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index ba2751eb06f7..8aa362f44090 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -362,6 +362,18 @@ static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi, enum pci_tsm_req_scope sco
(u8 *)req_obj.nonce);
return ret;
}
+ case __REC_EXIT_DA_VDEV_REQUEST:
+ {
+ struct arm64_vdev_device_idmap_guest_req req_obj;
+
+ if (req_len != sizeof(req_obj))
+ return -EINVAL;
+
+ if (copy_from_user((void *)&req_obj, req.user, req_len))
+ return -EFAULT;
+
+ return cca_vdev_device_request(pdev, req_obj.vcpu_fd);
+ }
default:
return -EINVAL;
}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 58a20877c6b6..3c19dfe89c0a 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -1078,3 +1078,33 @@ int cca_vdev_get_device_measurements(struct pci_dev *pdev, unsigned long flags,
/* get and update the interface report cache. */
return vdev_update_device_measurements_cache(pdev);
}
+
+int cca_vdev_device_request(struct pci_dev *pdev, unsigned long vcpu_fd)
+{
+ struct kvm *kvm;
+ struct kvm_vcpu *vcpu;
+ unsigned long rec_phys;
+ struct cca_host_tdi *host_tdi = NULL;
+ struct file *vcpu_filp __free(fput) = fget(vcpu_fd);
+
+ if (!file_is_vcpu(vcpu_filp))
+ return -EINVAL;
+
+ vcpu = vcpu_filp->private_data;
+ if (!vcpu)
+ return -EINVAL;
+
+ rec_phys = virt_to_phys(vcpu->arch.rec.rec_page);
+ host_tdi = to_cca_host_tdi(pdev);
+ if (!host_tdi)
+ return -EINVAL;
+
+ kvm = host_tdi->tdi.kvm;
+ /* make sure this is the same vm */
+ if (vcpu->kvm != kvm)
+ return -EINVAL;
+
+ if (rmi_vdev_complete(rec_phys, virt_to_phys(host_tdi->rmm_vdev)))
+ return -ENXIO;
+ 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 6304cee85874..2547afa1256f 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -93,6 +93,7 @@ struct cca_host_tdi {
struct pci_tdi tdi;
struct realm *realm;
void *rmm_vdev;
+ unsigned long vdev_id;
/* protected by cca_host_pf0_dsc.object_lock */
struct cache_object *interface_report;
struct cache_object *measurements;
@@ -152,4 +153,5 @@ int cca_vdev_read_cached_object(struct pci_dev *pdev, int type, unsigned long of
unsigned long max_len, void __user *user_buf);
int cca_vdev_get_interface_report(struct pci_dev *pdev);
int cca_vdev_get_device_measurements(struct pci_dev *pdev, unsigned long flags, u8 *nonce);
+int cca_vdev_device_request(struct pci_dev *pdev, unsigned long rec_id);
#endif
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 34759a262b28..26a9619c364c 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -1066,6 +1066,7 @@ void kvm_get_kvm(struct kvm *kvm);
bool kvm_get_kvm_safe(struct kvm *kvm);
void kvm_put_kvm(struct kvm *kvm);
bool file_is_kvm(struct file *file);
+bool file_is_vcpu(struct file *file);
void kvm_put_kvm_no_destroy(struct kvm *kvm);
static inline struct kvm_memslots *__kvm_memslots(struct kvm *kvm, int as_id)
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 49d5ce0b7a26..c2e12a1bb23b 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -188,6 +188,7 @@ struct kvm_exit_snp_req_certs {
#define KVM_EXIT_ARM_SEA 41
#define KVM_EXIT_ARM_LDST64B 42
#define KVM_EXIT_SNP_REQ_CERTS 43
+#define KVM_EXIT_ARM64_TIO 44
/* For KVM_EXIT_INTERNAL_ERROR */
/* Emulate instruction failed. */
@@ -492,6 +493,15 @@ struct kvm_run {
} arm_sea;
/* KVM_EXIT_SNP_REQ_CERTS */
struct kvm_exit_snp_req_certs snp_req_certs;
+ /* KVM_EXIT_ARM64_TIO*/
+ struct {
+ __u64 flags;
+ __u64 nr;
+ __u64 vdev_id;
+ __u64 gpa_base;
+ __u64 gpa_top;
+ __u64 pa_base;
+ } cca_exit;
/* Fix the size of the union. */
char padding[256];
};
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index f076c5a7a290..229c2b14bc83 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -4110,6 +4110,12 @@ static struct file_operations kvm_vcpu_fops = {
KVM_COMPAT(kvm_vcpu_compat_ioctl),
};
+bool file_is_vcpu(struct file *file)
+{
+ return file && file->f_op == &kvm_vcpu_fops;
+}
+EXPORT_SYMBOL_GPL(file_is_vcpu);
+
/*
* Allocates an inode for the vcpu.
*/
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v3 07/12] coco: host: arm64: Fetch device measurements via RMI
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:07 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
In-Reply-To: <20260312080743.3487326-1-aneesh.kumar@kernel.org>
- define __RHI_DA_VDEV_GET_MEASUREMENTS for guest requests and
expose the RMI SMC ID/wrapper for RMI_VDEV_GET_DEV_MEASUREMENTS
- teach the CCA host driver to handle the new guest request by fetching
the device measurements from RMM using rmi_vdev_get_device_measurements()
and refreshing the cached buffer
- add a helper that submits a DOE work to pull the latest device
measurements into the cache
This lets guests request up-to-date device measurements via RHI
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rhi.h | 1 +
arch/arm64/include/asm/rmi_cmds.h | 12 +++++
arch/arm64/include/asm/rmi_smc.h | 15 +++++-
arch/arm64/include/uapi/asm/rmi-da.h | 6 +++
drivers/virt/coco/arm-cca-host/arm-cca.c | 16 ++++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 69 ++++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 1 +
7 files changed, 119 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/rhi.h b/arch/arm64/include/asm/rhi.h
index edb23614cdeb..a18ad7bbc028 100644
--- a/arch/arm64/include/asm/rhi.h
+++ b/arch/arm64/include/asm/rhi.h
@@ -83,5 +83,6 @@ enum rhi_tdi_state {
#define __RHI_DA_OBJECT_SIZE 0x1
#define __RHI_DA_OBJECT_READ 0x2
#define __RHI_DA_VDEV_GET_INTERFACE_REPORT 0x3
+#define __RHI_DA_VDEV_GET_MEASUREMENTS 0x4
#endif
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index ea9d4ec21e0e..aad245675c7d 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -673,4 +673,16 @@ static inline unsigned long rmi_vdev_get_interface_report(unsigned long rd,
return res.a0;
}
+static inline unsigned long
+rmi_vdev_get_device_measurements(unsigned long rd, unsigned long pdev_phys,
+ unsigned long vdev_phys,
+ unsigned long param_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_GET_DEV_MEASUREMENTS,
+ rd, pdev_phys, vdev_phys, param_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 b3239f51de22..36c3db8b821d 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -61,7 +61,7 @@
#define SMC_RMI_VDEV_GET_STATE SMC_RMI_CALL(0x0189)
#define SMC_RMI_VDEV_UNLOCK SMC_RMI_CALL(0x018A)
#define SMC_RMI_VDEV_GET_INTERFACE_REPORT SMC_RMI_CALL(0x01D0)
-
+#define SMC_RMI_VDEV_GET_DEV_MEASUREMENTS SMC_RMI_CALL(0x01D1)
#define SMC_RMI_VDEV_LOCK SMC_RMI_CALL(0x01D2)
#define RMI_ABI_MAJOR_VERSION 1
@@ -482,4 +482,17 @@ struct rmi_vdev_params {
};
};
+#define RMI_VDEV_MEASURE_HASH 0x0
+#define RMI_VDEV_MEASURE_RAW 0x1
+struct rmi_vdev_measurement_params {
+ union {
+ u64 flags;
+ u8 padding0[256];
+ };
+ union {
+ u8 nonce[32];
+ u8 padding1[256];
+ };
+};
+
#endif /* __ASM_RMI_SMC_H */
diff --git a/arch/arm64/include/uapi/asm/rmi-da.h b/arch/arm64/include/uapi/asm/rmi-da.h
index 8743d9a2e5f7..1c21a5e78eb5 100644
--- a/arch/arm64/include/uapi/asm/rmi-da.h
+++ b/arch/arm64/include/uapi/asm/rmi-da.h
@@ -16,4 +16,10 @@ struct arm64_vdev_object_read_guest_req {
__aligned_u64 offset;
};
+struct arm64_vdev_device_measurement_guest_req {
+ __u32 req_type;
+ __aligned_u64 flags;
+ __aligned_u64 nonce;
+};
+
#endif
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index de3c239345a8..ba2751eb06f7 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -346,6 +346,22 @@ static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi, enum pci_tsm_req_scope sco
{
return cca_vdev_get_interface_report(pdev);
}
+ case __RHI_DA_VDEV_GET_MEASUREMENTS:
+ {
+ int ret;
+ struct arm64_vdev_device_measurement_guest_req req_obj;
+
+ if (req_len != sizeof(req_obj))
+ return -EINVAL;
+
+ if (copy_from_user((void *)&req_obj, req.user, req_len))
+ return -EFAULT;
+
+ ret = cca_vdev_get_device_measurements(pdev,
+ req_obj.flags,
+ (u8 *)req_obj.nonce);
+ return ret;
+ }
default:
return -EINVAL;
}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 48a18905bb55..58a20877c6b6 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -1009,3 +1009,72 @@ int cca_vdev_get_interface_report(struct pci_dev *pdev)
/* get and update the interface report cache. */
return vdev_update_interface_report_cache(pdev);
}
+
+static int vdev_update_device_measurements_cache(struct pci_dev *pdev)
+{
+ struct dev_comm_work comm_work;
+ struct cca_host_tdi *host_tdi = to_cca_host_tdi(pdev);
+ struct cca_host_comm_data *comm_data = to_cca_comm_data(pdev);
+
+ INIT_WORK_ONSTACK(&comm_work.work, vdev_fetch_object_workfn);
+ init_completion(&comm_work.complete);
+ comm_work.tsm = pdev->tsm;
+ if (host_tdi->measurements) {
+ comm_work.cache_buf = host_tdi->measurements->buf;
+ comm_work.cache_offset = &host_tdi->measurements->offset;
+ comm_work.cache_size = host_tdi->measurements->size;
+ } else {
+ comm_work.cache_buf = NULL;
+ comm_work.cache_offset = NULL;
+ comm_work.cache_size = 0;
+ }
+
+ queue_work(comm_data->work_queue, &comm_work.work);
+ wait_for_completion(&comm_work.complete);
+ destroy_work_on_stack(&comm_work.work);
+
+ if (comm_work.cache_size == 0)
+ return -ENXIO;
+ return 0;
+}
+
+static inline void vdev_measurement_param_free(struct rmi_vdev_measurement_params *param)
+{
+ return free_page((unsigned long)param);
+}
+DEFINE_FREE(measurement_param_free, struct rmi_vdev_measurement_params *, if (_T) vdev_measurement_param_free(_T))
+
+int cca_vdev_get_device_measurements(struct pci_dev *pdev, unsigned long flags, u8 *nonce)
+{
+ struct realm *realm;
+ phys_addr_t rd_phys;
+ phys_addr_t rmm_pdev_phys;
+ phys_addr_t rmm_vdev_phys;
+ struct cca_host_tdi *host_tdi;
+ struct cca_host_pf0_dsc *pf0_dsc;
+
+ host_tdi = to_cca_host_tdi(pdev);
+ rmm_vdev_phys = virt_to_phys(host_tdi->rmm_vdev);
+ realm = &host_tdi->tdi.kvm->arch.realm;
+ rd_phys = virt_to_phys(realm->rd);
+
+ pf0_dsc = to_cca_pf0_dsc(pdev->tsm->dsm_dev);
+ rmm_pdev_phys = virt_to_phys(pf0_dsc->rmm_pdev);
+
+ struct rmi_vdev_measurement_params *params __free(measurement_param_free) =
+ (struct rmi_vdev_measurement_params *)get_zeroed_page(GFP_KERNEL_ACCOUNT);
+ if (!params)
+ return -ENOMEM;
+
+ params->flags = flags;
+
+ if (copy_from_user(params->nonce, nonce, sizeof(params->nonce)))
+ return -EFAULT;
+
+ if (rmi_vdev_get_device_measurements(rd_phys, rmm_pdev_phys,
+ rmm_vdev_phys, virt_to_phys(params)))
+ return -ENXIO;
+
+ /* get and update the interface report cache. */
+ return vdev_update_device_measurements_cache(pdev);
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 662cedd23c42..6304cee85874 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -151,4 +151,5 @@ int cca_vdev_get_object_size(struct pci_dev *pdev, int type);
int cca_vdev_read_cached_object(struct pci_dev *pdev, int type, unsigned long offset,
unsigned long max_len, void __user *user_buf);
int cca_vdev_get_interface_report(struct pci_dev *pdev);
+int cca_vdev_get_device_measurements(struct pci_dev *pdev, unsigned long flags, u8 *nonce);
#endif
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v3 06/12] coco: host: arm64: Fetch interface report via RMI
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:07 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
In-Reply-To: <20260312080743.3487326-1-aneesh.kumar@kernel.org>
- define `__RHI_DA_VDEV_GET_INTERFACE_REPORT` for guest requests and
expose the RMI SMC ID/wrapper for `RMI_VDEV_GET_INTERFACE_REPORT`
- teach the CCA host driver to handle the new guest request by fetching
the report from RMM using `rmi_vdev_get_interface_report()` and
refreshing the cached buffer
- add a helper that submits a DOE work to pull the latest report into
the cache
This lets guests request up-to-date interface reports via RHI
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rhi.h | 1 +
arch/arm64/include/asm/rmi_cmds.h | 12 ++++++
arch/arm64/include/asm/rmi_smc.h | 1 +
drivers/virt/coco/arm-cca-host/arm-cca.c | 4 ++
drivers/virt/coco/arm-cca-host/rmi-da.c | 55 +++++++++++++++++++++++-
drivers/virt/coco/arm-cca-host/rmi-da.h | 1 +
6 files changed, 73 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/rhi.h b/arch/arm64/include/asm/rhi.h
index 3c84fedba4ab..edb23614cdeb 100644
--- a/arch/arm64/include/asm/rhi.h
+++ b/arch/arm64/include/asm/rhi.h
@@ -82,5 +82,6 @@ enum rhi_tdi_state {
/* guest request operation nr */
#define __RHI_DA_OBJECT_SIZE 0x1
#define __RHI_DA_OBJECT_READ 0x2
+#define __RHI_DA_VDEV_GET_INTERFACE_REPORT 0x3
#endif
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 5964549aca23..ea9d4ec21e0e 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -661,4 +661,16 @@ static inline unsigned long rmi_vdev_destroy(unsigned long rd,
return res.a0;
}
+static inline unsigned long rmi_vdev_get_interface_report(unsigned long rd,
+ unsigned long pdev_phys,
+ unsigned long vdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_GET_INTERFACE_REPORT,
+ rd, pdev_phys, vdev_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 95ddbc6dd1e0..b3239f51de22 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -60,6 +60,7 @@
#define SMC_RMI_VDEV_DESTROY SMC_RMI_CALL(0x0188)
#define SMC_RMI_VDEV_GET_STATE SMC_RMI_CALL(0x0189)
#define SMC_RMI_VDEV_UNLOCK SMC_RMI_CALL(0x018A)
+#define SMC_RMI_VDEV_GET_INTERFACE_REPORT SMC_RMI_CALL(0x01D0)
#define SMC_RMI_VDEV_LOCK SMC_RMI_CALL(0x01D2)
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index 8678acd84d7d..de3c239345a8 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -342,6 +342,10 @@ static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi, enum pci_tsm_req_scope sco
/* error */
return len;
}
+ case __RHI_DA_VDEV_GET_INTERFACE_REPORT:
+ {
+ return cca_vdev_get_interface_report(pdev);
+ }
default:
return -EINVAL;
}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 123cda44535c..48a18905bb55 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -839,7 +839,7 @@ void cca_vdev_unlock_and_destroy(struct realm *realm,
host_tdi->realm = NULL;
}
-static void __maybe_unused vdev_fetch_object_workfn(struct work_struct *work)
+static void vdev_fetch_object_workfn(struct work_struct *work)
{
int state;
struct pci_tsm *tsm;
@@ -956,3 +956,56 @@ int cca_vdev_read_cached_object(struct pci_dev *pdev, int type,
return len;
}
+
+static int vdev_update_interface_report_cache(struct pci_dev *pdev)
+{
+ struct dev_comm_work comm_work;
+ struct cca_host_tdi *host_tdi = to_cca_host_tdi(pdev);
+ struct cca_host_comm_data *comm_data = to_cca_comm_data(pdev);
+
+ INIT_WORK_ONSTACK(&comm_work.work, vdev_fetch_object_workfn);
+ init_completion(&comm_work.complete);
+ comm_work.tsm = pdev->tsm;
+ if (host_tdi->interface_report) {
+ comm_work.cache_buf = host_tdi->interface_report->buf;
+ comm_work.cache_offset = &host_tdi->interface_report->offset;
+ comm_work.cache_size = host_tdi->interface_report->size;
+ } else {
+ comm_work.cache_buf = NULL;
+ comm_work.cache_offset = NULL;
+ comm_work.cache_size = 0;
+ }
+
+ queue_work(comm_data->work_queue, &comm_work.work);
+ wait_for_completion(&comm_work.complete);
+ destroy_work_on_stack(&comm_work.work);
+
+ if (comm_work.cache_size == 0)
+ return -ENXIO;
+ return 0;
+}
+
+int cca_vdev_get_interface_report(struct pci_dev *pdev)
+{
+ phys_addr_t rmm_pdev_phys;
+ phys_addr_t rmm_vdev_phys;
+ struct cca_host_pf0_dsc *pf0_dsc;
+ struct cca_host_tdi *host_tdi;
+ struct realm *realm;
+ phys_addr_t rd_phys;
+
+ host_tdi = to_cca_host_tdi(pdev);
+ rmm_vdev_phys = virt_to_phys(host_tdi->rmm_vdev);
+ realm = &host_tdi->tdi.kvm->arch.realm;
+ rd_phys = virt_to_phys(realm->rd);
+
+ pf0_dsc = to_cca_pf0_dsc(pdev->tsm->dsm_dev);
+ rmm_pdev_phys = virt_to_phys(pf0_dsc->rmm_pdev);
+
+ if (rmi_vdev_get_interface_report(rd_phys,
+ rmm_pdev_phys, rmm_vdev_phys))
+ return -ENXIO;
+
+ /* get and update the interface report cache. */
+ return vdev_update_interface_report_cache(pdev);
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index c4f31986389c..662cedd23c42 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -150,4 +150,5 @@ void cca_vdev_unlock_and_destroy(struct realm *realm, struct pci_dev *pdev,
int cca_vdev_get_object_size(struct pci_dev *pdev, int type);
int cca_vdev_read_cached_object(struct pci_dev *pdev, int type, unsigned long offset,
unsigned long max_len, void __user *user_buf);
+int cca_vdev_get_interface_report(struct pci_dev *pdev);
#endif
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v3 05/12] coco: host: arm64: Add helper for cached object fetches
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:07 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
In-Reply-To: <20260312080743.3487326-1-aneesh.kumar@kernel.org>
Introduce vdev_fetch_object_work() so we have a single workqueue handler
that refreshes any cached Realm object (interface report, measurements,
certificates). The helper receives the cache buffer/offset/size via
dev_comm_work, clears the existing contents under dsm_dev.object_lock,
performs the VDEV_COMMUNICATE call, and uses the updated size to signal
failures back to the caller once the work completes.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/virt/coco/arm-cca-host/rmi-da.c | 28 +++++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 3 +++
2 files changed, 31 insertions(+)
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index fb623e5e5b62..123cda44535c 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -839,6 +839,34 @@ void cca_vdev_unlock_and_destroy(struct realm *realm,
host_tdi->realm = NULL;
}
+static void __maybe_unused vdev_fetch_object_workfn(struct work_struct *work)
+{
+ int state;
+ struct pci_tsm *tsm;
+ struct cca_host_pf0_dsc *pf0_dsc;
+ struct dev_comm_work *setup_work;
+
+ setup_work = container_of(work, struct dev_comm_work, work);
+ tsm = setup_work->tsm;
+ pf0_dsc = to_cca_pf0_dsc(tsm->dsm_dev);
+
+ guard(mutex)(&pf0_dsc->object_lock);
+
+ if (setup_work->cache_size) {
+ memset(setup_work->cache_buf, 0, setup_work->cache_size);
+ *setup_work->cache_offset = 0;
+ }
+ state = do_dev_communicate(VDEV_COMMUNICATE, tsm, RMI_VDEV_ERROR);
+ /* return status through dev_comm_work.cache_cache */
+ if (state == RMI_VDEV_ERROR)
+ setup_work->cache_size = 0;
+ else
+ /* indicate success. This value is not used. */
+ setup_work->cache_size = CACHE_CHUNK_SIZE;
+
+ complete(&setup_work->complete);
+}
+
int cca_vdev_get_object_size(struct pci_dev *pdev, int type)
{
long len;
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 9cc587393d02..c4f31986389c 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -24,6 +24,9 @@ struct cache_object {
struct dev_comm_work {
struct pci_tsm *tsm;
int target_state;
+ u8 *cache_buf;
+ int *cache_offset;
+ int cache_size;
struct work_struct work;
struct completion complete;
};
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v3 04/12] coco: host: arm64: Add support for da object read RHI handling
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:07 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
In-Reply-To: <20260312080743.3487326-1-aneesh.kumar@kernel.org>
Device assignment-related RHI calls result in a REC exit, which is
handled by the tsm guest_request callback.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rhi.h | 4 ++
arch/arm64/include/uapi/asm/rmi-da.h | 19 +++++
drivers/virt/coco/arm-cca-host/arm-cca.c | 75 +++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 91 ++++++++++++++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.h | 4 ++
5 files changed, 193 insertions(+)
create mode 100644 arch/arm64/include/uapi/asm/rmi-da.h
diff --git a/arch/arm64/include/asm/rhi.h b/arch/arm64/include/asm/rhi.h
index 8f9ea4a4bb7c..3c84fedba4ab 100644
--- a/arch/arm64/include/asm/rhi.h
+++ b/arch/arm64/include/asm/rhi.h
@@ -79,4 +79,8 @@ enum rhi_tdi_state {
#define RHI_DA_VDEV_SET_TDI_STATE SMC_RHI_CALL(0x0054)
#define RHI_DA_VDEV_ABORT SMC_RHI_CALL(0x0056)
+/* guest request operation nr */
+#define __RHI_DA_OBJECT_SIZE 0x1
+#define __RHI_DA_OBJECT_READ 0x2
+
#endif
diff --git a/arch/arm64/include/uapi/asm/rmi-da.h b/arch/arm64/include/uapi/asm/rmi-da.h
new file mode 100644
index 000000000000..8743d9a2e5f7
--- /dev/null
+++ b/arch/arm64/include/uapi/asm/rmi-da.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+
+#ifndef _UAPI__ASM_RMI_DA_H
+#define _UAPI__ASM_RMI_DA_H
+
+#include <linux/types.h>
+
+struct arm64_vdev_object_size_guest_req {
+ __u32 req_type;
+ __u32 object_type;
+};
+
+struct arm64_vdev_object_read_guest_req {
+ __u32 req_type;
+ __u32 object_type;
+ __aligned_u64 offset;
+};
+
+#endif
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index 1c17269809a1..8678acd84d7d 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -13,6 +13,7 @@
#include <linux/cleanup.h>
#include <linux/kvm_host.h>
#include <linux/pci.h>
+#include <asm/rmi-da.h>
#include "rmi-da.h"
@@ -277,6 +278,79 @@ static void cca_tsm_unbind(struct pci_tdi *tdi)
kfree(host_tdi);
}
+static ssize_t cca_tsm_guest_req(struct pci_tdi *tdi, enum pci_tsm_req_scope scope,
+ sockptr_t req, size_t req_len,
+ sockptr_t resp, size_t resp_len,
+ u64 *tsm_code)
+{
+ struct pci_dev *pdev = tdi->pdev;
+
+ if (req.is_kernel || resp.is_kernel)
+ return -EINVAL;
+
+ switch (scope) {
+ case PCI_TSM_REQ_INFO: {
+ u32 req_type;
+
+ if (get_user(req_type, (u32 __user *)req.user))
+ return -EFAULT;
+
+ switch (req_type) {
+ case __RHI_DA_OBJECT_SIZE: {
+ int object_size;
+ struct arm64_vdev_object_size_guest_req req_obj;
+
+ if (req_len != sizeof(req_obj))
+ return -EINVAL;
+
+ if (copy_from_user((void *)&req_obj, req.user, req_len))
+ return -EFAULT;
+ object_size = cca_vdev_get_object_size(pdev, req_obj.object_type);
+ if (object_size > 0) {
+ if (resp_len < sizeof(object_size))
+ return -EINVAL;
+ if (copy_to_user(resp.user, &object_size, sizeof(object_size)))
+ return -EFAULT;
+
+ if (resp_len != sizeof(object_size))
+ return resp_len - sizeof(object_size);
+ return 0;
+ }
+ /* error */
+ return object_size;
+ }
+ case __RHI_DA_OBJECT_READ:
+ {
+ int len;
+ struct arm64_vdev_object_read_guest_req req_obj;
+
+ if (req_len != sizeof(req_obj))
+ return -EINVAL;
+
+ if (copy_from_user((void *)&req_obj, req.user, req_len))
+ return -EFAULT;
+
+ len = cca_vdev_read_cached_object(pdev,
+ req_obj.object_type,
+ req_obj.offset,
+ resp_len, resp.user);
+ if (len > 0) {
+ if (resp_len != len)
+ return resp_len - len;
+ return 0;
+ }
+ /* error */
+ return len;
+ }
+ default:
+ return -EINVAL;
+ }
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
static struct pci_tsm_ops cca_link_pci_ops = {
.probe = cca_tsm_pci_probe,
.remove = cca_tsm_pci_remove,
@@ -284,6 +358,7 @@ static struct pci_tsm_ops cca_link_pci_ops = {
.disconnect = cca_tsm_disconnect,
.bind = cca_tsm_bind,
.unbind = cca_tsm_unbind,
+ .guest_req = cca_tsm_guest_req,
};
static void cca_link_tsm_remove(void *tsm_dev)
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 2181430c47b5..fb623e5e5b62 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -12,6 +12,7 @@
#include <keys/asymmetric-type.h>
#include <keys/x509-parser.h>
#include <linux/kvm_types.h>
+#include <linux/kvm_host.h>
#include <asm/kvm_rmi.h>
#include "rmi-da.h"
@@ -837,3 +838,93 @@ void cca_vdev_unlock_and_destroy(struct realm *realm,
host_tdi->rmm_vdev = NULL;
host_tdi->realm = NULL;
}
+
+int cca_vdev_get_object_size(struct pci_dev *pdev, int type)
+{
+ long len;
+ struct pci_tsm *tsm = pdev->tsm;
+ struct cca_host_pf0_dsc *pf0_dsc;
+ struct cca_host_tdi *host_tdi;
+
+ if (!tsm)
+ return -EINVAL;
+
+ pf0_dsc = to_cca_pf0_dsc(tsm->dsm_dev);
+ host_tdi = to_cca_host_tdi(pdev);
+
+ guard(mutex)(&pf0_dsc->object_lock);
+ /* Determine the buffer that should be used */
+ if (type == RHI_DA_OBJECT_INTERFACE_REPORT) {
+ if (!host_tdi->interface_report)
+ return -EINVAL;
+ len = host_tdi->interface_report->offset;
+ } else if (type == RHI_DA_OBJECT_MEASUREMENT) {
+ if (!host_tdi->measurements)
+ return -EINVAL;
+ len = host_tdi->measurements->offset;
+ } else if (type == RHI_DA_OBJECT_CERTIFICATE) {
+ if (!pf0_dsc->cert_chain.cache)
+ return -EINVAL;
+ len = pf0_dsc->cert_chain.cache->offset;
+ } else if (type == RHI_DA_OBJECT_VCA) {
+ if (!pf0_dsc->vca)
+ return -EINVAL;
+ len = pf0_dsc->vca->offset;
+ } else {
+ return -EINVAL;
+ }
+
+ return len;
+}
+
+int cca_vdev_read_cached_object(struct pci_dev *pdev, int type,
+ unsigned long offset,
+ unsigned long max_len, void __user *user_buf)
+{
+ void *buf;
+ unsigned long len;
+ struct cca_host_pf0_dsc *pf0_dsc;
+ struct cca_host_tdi *host_tdi;
+ struct pci_tsm *tsm = pdev->tsm;
+
+ if (!tsm)
+ return -EINVAL;
+
+ pf0_dsc = to_cca_pf0_dsc(tsm->dsm_dev);
+ host_tdi = to_cca_host_tdi(pdev);
+
+ guard(mutex)(&pf0_dsc->object_lock);
+ /* Determine the buffer that should be used */
+ if (type == RHI_DA_OBJECT_INTERFACE_REPORT) {
+ if (!host_tdi->interface_report)
+ return -EINVAL;
+ len = host_tdi->interface_report->offset;
+ buf = host_tdi->interface_report->buf;
+ } else if (type == RHI_DA_OBJECT_MEASUREMENT) {
+ if (!host_tdi->measurements)
+ return -EINVAL;
+ len = host_tdi->measurements->offset;
+ buf = host_tdi->measurements->buf;
+ } else if (type == RHI_DA_OBJECT_CERTIFICATE) {
+ if (!pf0_dsc->cert_chain.cache)
+ return -EINVAL;
+ len = pf0_dsc->cert_chain.cache->offset;
+ buf = pf0_dsc->cert_chain.cache->buf;
+ } else if (type == RHI_DA_OBJECT_VCA) {
+ if (!pf0_dsc->vca)
+ return -EINVAL;
+ len = pf0_dsc->vca->offset;
+ buf = pf0_dsc->vca->buf;
+ } else {
+ return -EINVAL;
+ }
+
+ /* Assume that the buffer is large enough for the whole report */
+ if (max_len < len)
+ return -E2BIG;
+
+ if (copy_to_user(user_buf, buf + offset, len))
+ return -EIO;
+
+ return len;
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 9b0af1ac208f..9cc587393d02 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -11,6 +11,7 @@
#include <linux/pci-tsm.h>
#include <linux/sizes.h>
#include <asm/rmi_smc.h>
+#include <asm/rhi.h>
#define MAX_CACHE_OBJ_SIZE SZ_16M
#define CACHE_CHUNK_SIZE SZ_4K
@@ -143,4 +144,7 @@ void *cca_vdev_create(struct realm *realm, struct pci_dev *pdev,
struct pci_dev *pf0_dev, u32 guest_rid);
void cca_vdev_unlock_and_destroy(struct realm *realm, struct pci_dev *pdev,
struct pci_dev *pf0_dev);
+int cca_vdev_get_object_size(struct pci_dev *pdev, int type);
+int cca_vdev_read_cached_object(struct pci_dev *pdev, int type, unsigned long offset,
+ unsigned long max_len, void __user *user_buf);
#endif
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v3 03/12] coco: host: arm64: Add helpers to unlock and destroy RMM vdev
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:07 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
In-Reply-To: <20260312080743.3487326-1-aneesh.kumar@kernel.org>
- define the SMCCC IDs and inline wrappers for `RMI_VDEV_UNLOCK` and
RMI_VDEV_DESTROY
- extend `vdev_create()` to treat communication failures as fatal and
tear down the newly created vdev
- provide `vdev_unlock_and_destroy()` that drives the vdev back to the
unlocked state, issues the destroy call, and frees the delegated granule
- hook the new helper into the TSM unbind path so host cleanup always
unlock and destroy RMM vdev and releases cached buffers
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 22 ++++++++++
arch/arm64/include/asm/rmi_smc.h | 2 +
drivers/virt/coco/arm-cca-host/arm-cca.c | 25 ++++++++++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 52 ++++++++++++++++++++++--
drivers/virt/coco/arm-cca-host/rmi-da.h | 2 +
5 files changed, 100 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 2a86de5eb160..5964549aca23 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -639,4 +639,26 @@ static inline unsigned long rmi_vdev_lock(unsigned long rd,
return res.a0;
}
+static inline unsigned long rmi_vdev_unlock(unsigned long rd,
+ unsigned long pdev_phys,
+ unsigned long vdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_UNLOCK, rd, pdev_phys, vdev_phys, &res);
+
+ return res.a0;
+}
+
+static inline unsigned long rmi_vdev_destroy(unsigned long rd,
+ unsigned long pdev_phys,
+ unsigned long vdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_DESTROY, rd, pdev_phys, vdev_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 20c36a01df94..95ddbc6dd1e0 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -57,7 +57,9 @@
#define SMC_RMI_VDEV_ABORT SMC_RMI_CALL(0x0185)
#define SMC_RMI_VDEV_COMMUNICATE SMC_RMI_CALL(0x0186)
#define SMC_RMI_VDEV_CREATE SMC_RMI_CALL(0x0187)
+#define SMC_RMI_VDEV_DESTROY SMC_RMI_CALL(0x0188)
#define SMC_RMI_VDEV_GET_STATE SMC_RMI_CALL(0x0189)
+#define SMC_RMI_VDEV_UNLOCK SMC_RMI_CALL(0x018A)
#define SMC_RMI_VDEV_LOCK SMC_RMI_CALL(0x01D2)
diff --git a/drivers/virt/coco/arm-cca-host/arm-cca.c b/drivers/virt/coco/arm-cca-host/arm-cca.c
index ae62749f36e8..1c17269809a1 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -253,12 +253,37 @@ static struct pci_tdi *cca_tsm_bind(struct pci_dev *pdev, struct kvm *kvm, u32 t
return &no_free_ptr(host_tdi)->tdi;
}
+/*
+ * All device memory should be unmapped by now.
+ * 1. A pci device destroy will cause a driver remove (vfio) which will have
+ * done a dmabuf based unmap
+ * 2. A vdevice/idevice destroy from VMM should have done a unmap_private_range
+ * vm ioctl before
+ * 3. A guest unlock request should have done a rsi_invalidiate_mem_mapping
+ * before unlock rhi
+ * 4. vfio_pci_core_close_device() should trigger tsm unbind if vdevice is not
+ * already distroyed and that path involves vfio_pci_dma_buf_cleanup() which
+ * should get kvm to unmap the devmap
+ */
+static void cca_tsm_unbind(struct pci_tdi *tdi)
+{
+ struct cca_host_tdi *host_tdi;
+ struct realm *realm = &tdi->kvm->arch.realm;
+
+ host_tdi = container_of(tdi, struct cca_host_tdi, tdi);
+ cca_vdev_unlock_and_destroy(realm, tdi->pdev, tdi->pdev->tsm->dsm_dev);
+ kvfree(host_tdi->interface_report);
+ kvfree(host_tdi->measurements);
+ kfree(host_tdi);
+}
+
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,
.bind = cca_tsm_bind,
+ .unbind = cca_tsm_unbind,
};
static void cca_link_tsm_remove(void *tsm_dev)
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 336a4f5a832d..2181430c47b5 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -771,15 +771,25 @@ void *cca_vdev_create(struct realm *realm, struct pci_dev *pdev,
host_tdi->rmm_vdev = rmm_vdev;
host_tdi->realm = realm;
- submit_vdev_state_transition_work(pdev, RMI_VDEV_UNLOCKED);
+ ret = submit_vdev_state_transition_work(pdev, RMI_VDEV_UNLOCKED);
+ /* failure is treated as rmi_vdev_create failure */
+ if (ret)
+ goto err_vdev_comm;
- ret = rmi_vdev_lock(rd_phys, rmm_pdev_phys, rmm_vdev_phys);
+ if (rmi_vdev_lock(rd_phys, rmm_pdev_phys, rmm_vdev_phys)) {
+ ret = -ENXIO;
+ goto err_vdev_comm;
+ }
- submit_vdev_state_transition_work(pdev, RMI_VDEV_LOCKED);
+ ret = submit_vdev_state_transition_work(pdev, RMI_VDEV_LOCKED);
+ if (ret)
+ goto err_vdev_comm;
free_page((unsigned long)params);
return rmm_vdev;
+err_vdev_comm:
+ rmi_vdev_destroy(rd_phys, rmm_pdev_phys, rmm_vdev_phys);
err_vdev_create:
free_page((unsigned long)params);
err_params_alloc:
@@ -791,3 +801,39 @@ void *cca_vdev_create(struct realm *realm, struct pci_dev *pdev,
err_out:
return ERR_PTR(ret);
}
+
+void cca_vdev_unlock_and_destroy(struct realm *realm,
+ struct pci_dev *pdev, struct pci_dev *pf0_dev)
+{
+ int ret;
+ phys_addr_t rmm_pdev_phys;
+ phys_addr_t rmm_vdev_phys;
+ struct cca_host_pf0_dsc *pf0_dsc;
+ struct cca_host_tdi *host_tdi;
+ phys_addr_t rd_phys = virt_to_phys(realm->rd);
+
+ host_tdi = to_cca_host_tdi(pdev);
+ rmm_vdev_phys = virt_to_phys(host_tdi->rmm_vdev);
+
+ pf0_dsc = to_cca_pf0_dsc(pf0_dev);
+ rmm_pdev_phys = virt_to_phys(pf0_dsc->rmm_pdev);
+ if (rmi_vdev_unlock(rd_phys, rmm_pdev_phys, rmm_vdev_phys)) {
+ pci_err(pdev, "failed to unlock vdev\n");
+ goto unlock_err;
+ }
+
+ ret = submit_vdev_state_transition_work(pdev, RMI_VDEV_UNLOCKED);
+ if (ret)
+ pci_err(pdev, "failed to unlock vdev (%d)\n", ret);
+
+unlock_err:
+ /* Try to destroy even in case of error */
+ if (rmi_vdev_destroy(rd_phys, rmm_pdev_phys, rmm_vdev_phys))
+ pci_err(pdev, "failed to destroy vdev\n");
+
+ if (!rmi_granule_undelegate(rmm_vdev_phys))
+ free_page((unsigned long)host_tdi->rmm_vdev);
+
+ host_tdi->rmm_vdev = NULL;
+ host_tdi->realm = NULL;
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index e92078ae9a90..9b0af1ac208f 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -141,4 +141,6 @@ int cca_pdev_ide_setup(struct pci_dev *pdev);
void cca_pdev_stop_and_destroy(struct pci_dev *pdev);
void *cca_vdev_create(struct realm *realm, struct pci_dev *pdev,
struct pci_dev *pf0_dev, u32 guest_rid);
+void cca_vdev_unlock_and_destroy(struct realm *realm, struct pci_dev *pdev,
+ struct pci_dev *pf0_dev);
#endif
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v3 02/12] coco: host: arm64: Add support for RMM vdev objects
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:07 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
In-Reply-To: <20260312080743.3487326-1-aneesh.kumar@kernel.org>
An RMM vdev object represents the binding between a device function and
a Realm. For example, a vdev can represent a physical function of a PCIe
device or a virtual function of a multi-function PCIe device. Each vdev
is associated with one pdev.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 25 ++++++
arch/arm64/include/asm/rmi_smc.h | 24 +++++
drivers/virt/coco/arm-cca-host/arm-cca.c | 25 ++++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 110 ++++++++++++++++++++++-
drivers/virt/coco/arm-cca-host/rmi-da.h | 2 +
5 files changed, 185 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 0754d420faad..2a86de5eb160 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -614,4 +614,29 @@ static inline unsigned long rmi_vdev_abort(unsigned long vdev_phys)
return res.a0;
}
+
+static inline unsigned long rmi_vdev_create(unsigned long rd,
+ unsigned long pdev_phys,
+ unsigned long vdev_phys,
+ unsigned long vdev_params_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_CREATE, rd, pdev_phys,
+ vdev_phys, vdev_params_phys, &res);
+
+ return res.a0;
+}
+
+static inline unsigned long rmi_vdev_lock(unsigned long rd,
+ unsigned long pdev_phys,
+ unsigned long vdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_LOCK, rd, pdev_phys, vdev_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 14a2090cbac8..20c36a01df94 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -56,8 +56,11 @@
#define SMC_RMI_PDEV_STOP SMC_RMI_CALL(0x017c)
#define SMC_RMI_VDEV_ABORT SMC_RMI_CALL(0x0185)
#define SMC_RMI_VDEV_COMMUNICATE SMC_RMI_CALL(0x0186)
+#define SMC_RMI_VDEV_CREATE SMC_RMI_CALL(0x0187)
#define SMC_RMI_VDEV_GET_STATE SMC_RMI_CALL(0x0189)
+#define SMC_RMI_VDEV_LOCK SMC_RMI_CALL(0x01D2)
+
#define RMI_ABI_MAJOR_VERSION 1
#define RMI_ABI_MINOR_VERSION 0
@@ -455,4 +458,25 @@ enum rmi_vdev_state {
RMI_VDEV_STARTED,
RMI_VDEV_ERROR,
};
+
+#define MAX_VDEV_AUX_GRANULES 32
+
+struct rmi_vdev_params {
+ union {
+ struct {
+ u64 flags;
+ u64 vdev_id;
+ u64 tdi_id;
+ u64 num_aux;
+ };
+ u8 padding1[0x100];
+ };
+ union { /* 0x100 */
+ struct {
+ unsigned long aux[MAX_VDEV_AUX_GRANULES];
+ };
+ u8 padding2[0x900];
+ };
+};
+
#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 987c1be566ba..ae62749f36e8 100644
--- a/drivers/virt/coco/arm-cca-host/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-host/arm-cca.c
@@ -12,6 +12,7 @@
#include <linux/vmalloc.h>
#include <linux/cleanup.h>
#include <linux/kvm_host.h>
+#include <linux/pci.h>
#include "rmi-da.h"
@@ -229,11 +230,35 @@ static void cca_tsm_disconnect(struct pci_dev *pdev)
clear_bit(stream_id, cca_stream_ids);
}
+static struct pci_tdi *cca_tsm_bind(struct pci_dev *pdev, struct kvm *kvm, u32 tdi_id)
+{
+ void *rmm_vdev;
+ struct pci_dev *dsm_dev = pdev->tsm->dsm_dev;
+ struct realm *realm = &kvm->arch.realm;
+
+ struct cca_host_tdi *host_tdi __free(kfree) =
+ kzalloc(sizeof(struct cca_host_tdi), GFP_KERNEL);
+ if (!host_tdi)
+ return ERR_PTR(-ENOMEM);
+
+ pci_tsm_tdi_constructor(pdev, &host_tdi->tdi, kvm, tdi_id);
+ /* Assign the tdi such that vdev_create can use that to lookup */
+ pdev->tsm->tdi = &host_tdi->tdi;
+ rmm_vdev = cca_vdev_create(realm, pdev, dsm_dev, tdi_id);
+ if (IS_ERR_OR_NULL(rmm_vdev)) {
+ pdev->tsm->tdi = NULL;
+ return rmm_vdev;
+ }
+
+ return &no_free_ptr(host_tdi)->tdi;
+}
+
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,
+ .bind = cca_tsm_bind,
};
static void cca_link_tsm_remove(void *tsm_dev)
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index af0632544911..336a4f5a832d 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -665,7 +665,7 @@ static int wait_for_vdev_state(struct pci_tsm *tsm, enum rmi_vdev_state target_s
return wait_for_dev_state(VDEV_COMMUNICATE, tsm, target_state, RMI_VDEV_ERROR);
}
-static __maybe_unused void vdev_state_transition_workfn(struct work_struct *work)
+static void vdev_state_transition_workfn(struct work_struct *work)
{
unsigned long state;
struct pci_tsm *tsm;
@@ -683,3 +683,111 @@ static __maybe_unused void vdev_state_transition_workfn(struct work_struct *work
complete(&setup_work->complete);
}
+
+static int submit_vdev_state_transition_work(struct pci_dev *pdev, int target_state)
+{
+ enum rmi_vdev_state state;
+ struct dev_comm_work comm_work;
+ struct cca_host_comm_data *comm_data = to_cca_comm_data(pdev);
+ struct cca_host_tdi *host_tdi = to_cca_host_tdi(pdev);
+
+ INIT_WORK_ONSTACK(&comm_work.work, vdev_state_transition_workfn);
+ init_completion(&comm_work.complete);
+ comm_work.tsm = pdev->tsm;
+ comm_work.target_state = target_state;
+
+ queue_work(comm_data->work_queue, &comm_work.work);
+
+ wait_for_completion(&comm_work.complete);
+ destroy_work_on_stack(&comm_work.work);
+
+ /* check if we reached target state */
+ if (rmi_vdev_get_state(virt_to_phys(host_tdi->rmm_vdev), &state))
+ return -ENXIO;
+
+ if (state != target_state)
+ /* Protocol didn't take it to expected target state */
+ return -EPROTO;
+ return 0;
+}
+
+static unsigned long pci_get_tdi_id(struct pci_dev *pdev)
+{
+ /* requester segment is marked reserved. */
+ return pci_dev_id(pdev);
+}
+
+void *cca_vdev_create(struct realm *realm, struct pci_dev *pdev,
+ struct pci_dev *pf0_dev, u32 guest_rid)
+{
+ phys_addr_t rd_phys = virt_to_phys(realm->rd);
+ struct rmi_vdev_params *params = NULL;
+ struct cca_host_pf0_dsc *pf0_dsc;
+ struct cca_host_tdi *host_tdi;
+ phys_addr_t rmm_pdev_phys;
+ phys_addr_t rmm_vdev_phys;
+ bool should_free = true;
+ void *rmm_vdev;
+ int ret;
+
+ pf0_dsc = to_cca_pf0_dsc(pf0_dev);
+ if (!pf0_dsc->rmm_pdev) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ rmm_vdev = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!rmm_vdev) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ rmm_vdev_phys = virt_to_phys(rmm_vdev);
+ if (rmi_granule_delegate(rmm_vdev_phys)) {
+ ret = -ENXIO;
+ goto err_granule_delegate;
+ }
+
+ params = (struct rmi_vdev_params *)get_zeroed_page(GFP_KERNEL);
+ if (!params) {
+ ret = -ENOMEM;
+ goto err_params_alloc;
+ }
+
+ params->flags = 0;
+ params->vdev_id = guest_rid;
+ params->tdi_id = pci_get_tdi_id(pdev);
+ params->num_aux = 0;
+
+ rmm_pdev_phys = virt_to_phys(pf0_dsc->rmm_pdev);
+ if (rmi_vdev_create(rd_phys, rmm_pdev_phys,
+ rmm_vdev_phys, virt_to_phys(params))) {
+ ret = -ENXIO;
+ goto err_vdev_create;
+ }
+
+ /* setup host_tdi before call to device communicate */
+ host_tdi = to_cca_host_tdi(pdev);
+ host_tdi->rmm_vdev = rmm_vdev;
+ host_tdi->realm = realm;
+
+ submit_vdev_state_transition_work(pdev, RMI_VDEV_UNLOCKED);
+
+ ret = rmi_vdev_lock(rd_phys, rmm_pdev_phys, rmm_vdev_phys);
+
+ submit_vdev_state_transition_work(pdev, RMI_VDEV_LOCKED);
+
+ free_page((unsigned long)params);
+ return rmm_vdev;
+
+err_vdev_create:
+ free_page((unsigned long)params);
+err_params_alloc:
+ if (rmi_granule_undelegate(rmm_vdev_phys))
+ should_free = false;
+err_granule_delegate:
+ if (should_free)
+ free_page((unsigned long)rmm_vdev);
+err_out:
+ return ERR_PTR(ret);
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 914a3c297c24..e92078ae9a90 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -139,4 +139,6 @@ static inline struct cca_host_tdi *to_cca_host_tdi(struct pci_dev *pdev)
int cca_pdev_create(struct pci_dev *pdev);
int cca_pdev_ide_setup(struct pci_dev *pdev);
void cca_pdev_stop_and_destroy(struct pci_dev *pdev);
+void *cca_vdev_create(struct realm *realm, struct pci_dev *pdev,
+ struct pci_dev *pf0_dev, u32 guest_rid);
#endif
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v3 01/12] coco: host: arm64: Add support for virtual device communication
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:07 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
In-Reply-To: <20260312080743.3487326-1-aneesh.kumar@kernel.org>
Add support for vdev_communicate with RMM.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rmi_cmds.h | 31 +++++++++++++
arch/arm64/include/asm/rmi_smc.h | 10 +++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 60 ++++++++++++++++++++++---
drivers/virt/coco/arm-cca-host/rmi-da.h | 20 +++++++++
4 files changed, 114 insertions(+), 7 deletions(-)
diff --git a/arch/arm64/include/asm/rmi_cmds.h b/arch/arm64/include/asm/rmi_cmds.h
index 339bea517760..0754d420faad 100644
--- a/arch/arm64/include/asm/rmi_cmds.h
+++ b/arch/arm64/include/asm/rmi_cmds.h
@@ -583,4 +583,35 @@ static inline unsigned long rmi_pdev_set_pubkey(unsigned long pdev_phys, unsigne
return res.a0;
}
+static inline unsigned long rmi_vdev_communicate(unsigned long rd_phys,
+ unsigned long pdev_phys,
+ unsigned long vdev_phys,
+ unsigned long vdev_comm_data_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_COMMUNICATE, rd_phys,
+ pdev_phys, vdev_phys, vdev_comm_data_phys, &res);
+
+ return res.a0;
+}
+
+static inline unsigned long rmi_vdev_get_state(unsigned long vdev_phys, enum rmi_vdev_state *state)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_GET_STATE, vdev_phys, &res);
+
+ *state = res.a1;
+ return res.a0;
+}
+
+static inline unsigned long rmi_vdev_abort(unsigned long vdev_phys)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RMI_VDEV_ABORT, vdev_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 907e00f4855a..14a2090cbac8 100644
--- a/arch/arm64/include/asm/rmi_smc.h
+++ b/arch/arm64/include/asm/rmi_smc.h
@@ -54,6 +54,9 @@
#define SMC_RMI_PDEV_GET_STATE SMC_RMI_CALL(0x0178)
#define SMC_RMI_PDEV_SET_PUBKEY SMC_RMI_CALL(0x017b)
#define SMC_RMI_PDEV_STOP SMC_RMI_CALL(0x017c)
+#define SMC_RMI_VDEV_ABORT SMC_RMI_CALL(0x0185)
+#define SMC_RMI_VDEV_COMMUNICATE SMC_RMI_CALL(0x0186)
+#define SMC_RMI_VDEV_GET_STATE SMC_RMI_CALL(0x0189)
#define RMI_ABI_MAJOR_VERSION 1
#define RMI_ABI_MINOR_VERSION 0
@@ -445,4 +448,11 @@ struct rmi_public_key_params {
};
};
+enum rmi_vdev_state {
+ RMI_VDEV_NEW,
+ RMI_VDEV_UNLOCKED,
+ RMI_VDEV_LOCKED,
+ RMI_VDEV_STARTED,
+ RMI_VDEV_ERROR,
+};
#endif /* __ASM_RMI_SMC_H */
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.c b/drivers/virt/coco/arm-cca-host/rmi-da.c
index 029758ada136..af0632544911 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.c
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.c
@@ -11,6 +11,8 @@
#include <crypto/internal/rsa.h>
#include <keys/asymmetric-type.h>
#include <keys/x509-parser.h>
+#include <linux/kvm_types.h>
+#include <asm/kvm_rmi.h>
#include "rmi-da.h"
@@ -209,6 +211,7 @@ static int _do_dev_communicate(enum dev_comm_type type, struct pci_tsm *tsm)
int nbytes, cp_len;
struct cache_object **cache_objp, *cache_obj;
struct cca_host_pf0_dsc *pf0_dsc = to_cca_pf0_dsc(tsm->dsm_dev);
+ struct cca_host_tdi *host_tdi = to_cca_host_tdi(tsm->pdev);
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;
@@ -219,7 +222,11 @@ static int _do_dev_communicate(enum dev_comm_type type, struct pci_tsm *tsm)
rmi_ret = rmi_pdev_communicate(virt_to_phys(pf0_dsc->rmm_pdev),
virt_to_phys(comm_data->io_params));
else
- rmi_ret = RMI_ERROR_INPUT;
+ rmi_ret = rmi_vdev_communicate(virt_to_phys(host_tdi->realm->rd),
+ virt_to_phys(pf0_dsc->rmm_pdev),
+ virt_to_phys(host_tdi->rmm_vdev),
+ virt_to_phys(comm_data->io_params));
+
if (rmi_ret != RMI_SUCCESS) {
if (rmi_ret == RMI_BUSY)
return -EBUSY;
@@ -236,6 +243,12 @@ static int _do_dev_communicate(enum dev_comm_type type, struct pci_tsm *tsm)
case RMI_DEV_CERTIFICATE:
cache_objp = &pf0_dsc->cert_chain.cache;
break;
+ case RMI_DEV_INTERFACE_REPORT:
+ cache_objp = &host_tdi->interface_report;
+ break;
+ case RMI_DEV_MEASUREMENTS:
+ cache_objp = &host_tdi->measurements;
+ break;
default:
return -EINVAL;
}
@@ -337,9 +350,11 @@ 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, unsigned long error_state)
{
- int ret, state = error_state;
+ int ret, state;
+ unsigned long rmi_ret;
struct rmi_dev_comm_enter *io_enter;
struct cca_host_pf0_dsc *pf0_dsc = to_cca_pf0_dsc(tsm->dsm_dev);
+ struct cca_host_tdi *host_tdi = to_cca_host_tdi(tsm->pdev);
io_enter = &pf0_dsc->comm_data.io_params->enter;
io_enter->resp_len = 0;
@@ -349,16 +364,23 @@ static int do_dev_communicate(enum dev_comm_type type,
if (ret) {
if (type == PDEV_COMMUNICATE)
rmi_pdev_abort(virt_to_phys(pf0_dsc->rmm_pdev));
+ else
+ rmi_vdev_abort(virt_to_phys(host_tdi->rmm_vdev));
+
+ state = error_state;
} 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(pf0_dsc->rmm_pdev),
- (enum rmi_pdev_state *)&state))
- state = error_state;
- }
+ if (type == PDEV_COMMUNICATE)
+ rmi_ret = rmi_pdev_get_state(virt_to_phys(pf0_dsc->rmm_pdev),
+ (enum rmi_pdev_state *)&state);
+ else
+ rmi_ret = rmi_vdev_get_state(virt_to_phys(host_tdi->rmm_vdev),
+ (enum rmi_vdev_state *)&state);
+ if (rmi_ret)
+ state = error_state;
}
if (state == error_state)
@@ -637,3 +659,27 @@ void cca_pdev_stop_and_destroy(struct pci_dev *pdev)
free_page((unsigned long)pf0_dsc->rmm_pdev);
pf0_dsc->rmm_pdev = NULL;
}
+
+static int wait_for_vdev_state(struct pci_tsm *tsm, enum rmi_vdev_state target_state)
+{
+ return wait_for_dev_state(VDEV_COMMUNICATE, tsm, target_state, RMI_VDEV_ERROR);
+}
+
+static __maybe_unused void vdev_state_transition_workfn(struct work_struct *work)
+{
+ unsigned long state;
+ struct pci_tsm *tsm;
+ struct dev_comm_work *setup_work;
+ struct cca_host_pf0_dsc *pf0_dsc;
+
+ setup_work = container_of(work, struct dev_comm_work, work);
+ tsm = setup_work->tsm;
+
+ pf0_dsc = to_cca_pf0_dsc(tsm->dsm_dev);
+ guard(mutex)(&pf0_dsc->object_lock);
+
+ state = wait_for_vdev_state(tsm, setup_work->target_state);
+ WARN_ON(state != setup_work->target_state);
+
+ complete(&setup_work->complete);
+}
diff --git a/drivers/virt/coco/arm-cca-host/rmi-da.h b/drivers/virt/coco/arm-cca-host/rmi-da.h
index 38550103c2a5..914a3c297c24 100644
--- a/drivers/virt/coco/arm-cca-host/rmi-da.h
+++ b/drivers/virt/coco/arm-cca-host/rmi-da.h
@@ -82,6 +82,16 @@ struct cca_host_fn_dsc {
enum dev_comm_type {
PDEV_COMMUNICATE = 0x1,
+ VDEV_COMMUNICATE = 0x2,
+};
+
+struct cca_host_tdi {
+ struct pci_tdi tdi;
+ struct realm *realm;
+ void *rmm_vdev;
+ /* protected by cca_host_pf0_dsc.object_lock */
+ struct cache_object *interface_report;
+ struct cache_object *measurements;
};
static inline struct cca_host_pf0_dsc *to_cca_pf0_dsc(struct pci_dev *pdev)
@@ -116,6 +126,16 @@ static inline struct cca_host_comm_data *to_cca_comm_data(struct pci_dev *pdev)
return NULL;
}
+static inline struct cca_host_tdi *to_cca_host_tdi(struct pci_dev *pdev)
+{
+ struct pci_tsm *tsm = pdev->tsm;
+
+ if (!tsm || !tsm->tdi)
+ return NULL;
+
+ return container_of(tsm->tdi, struct cca_host_tdi, tdi);
+}
+
int cca_pdev_create(struct pci_dev *pdev);
int cca_pdev_ide_setup(struct pci_dev *pdev);
void cca_pdev_stop_and_destroy(struct pci_dev *pdev);
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v3 00/12] coco/TSM: Implement host-side support for Arm CCA TDISP setup
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:07 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel; +Cc: linux-kernel, Aneesh Kumar K.V (Arm)
This patch series implements the host-side changes needed for end-to-end
Arm CCA TDISP setup. It adds the RMI/RHI plumbing required to create and
manage Realm vdev objects, service device-attestation object requests, and
complete the KVM/RMM flows needed for device run-time transitions.
The series is based on the RMM ALP17 specification [1] and the
RHI v1.0 BET1 specification [5].
At a high level, the series adds support for:
- host-side vdev communication and lifecycle management
- host handling of RHI DA object read/size requests
- host-side fetching and caching of interface reports and measurements
- KVM handling of vdev request/complete exits
- KVM handling of map/validation exits and teardown on granule destroy
- vdev transition to TDISP RUN state
- enabling DA in Realm create parameters
The series builds upon the TSM framework patches posted at [2] and depends on
the KVM CCA patchset [3]. A git repository containing all related changes is
available at [4].
Previous posting:
rfc-v1: https://lore.kernel.org/all/20250728135216.48084-1-aneesh.kumar@kernel.org
There is no rfc-v2 posting. This series is marked rfc-v3 to stay aligned
with the rest of the CCA patchsets that are being posted as v3.
Changes from v1:
- rebase to latest kernel and core TSM changes
- address review feedback
[1] https://developer.arm.com/-/cdn-downloads/permalink/Architectures/Armv9/DEN0137_1.1-alp17.zip
[2] https://lore.kernel.org/all/20260303000207.1836586-1-dan.j.williams@intel.com
[3] https://lore.kernel.org/all/461fa23f-9add-40e5-a0d0-759030e7c70b@arm.com
[4] https://gitlab.arm.com/linux-arm/linux-cca.git cca/topics/cca-tdisp-upstream-rfc-v3
[5] https://developer.arm.com/documentation/den0148/latest/ RHI
Aneesh Kumar K.V (Arm) (12):
coco: host: arm64: Add support for virtual device communication
coco: host: arm64: Add support for RMM vdev objects
coco: host: arm64: Add helpers to unlock and destroy RMM vdev
coco: host: arm64: Add support for da object read RHI handling
coco: host: arm64: Add helper for cached object fetches
coco: host: arm64: Fetch interface report via RMI
coco: host: arm64: Fetch device measurements via RMI
coco: host: KVM: arm64: Handle vdev request exits and completion
coco: host: KVM: arm64: Handle vdev map/validation exits
KVM: arm64: Unmap device mappings when a private granule is destroyed
coco: host: arm64: Transition vdevs to TDISP RUN state
KVM: arm64: CCA: enable DA in realm create parameters
Documentation/virt/kvm/api.rst | 22 +
arch/arm64/include/asm/kvm_rmi.h | 4 +
arch/arm64/include/asm/rhi.h | 9 +
arch/arm64/include/asm/rmi_cmds.h | 163 +++++++
arch/arm64/include/asm/rmi_smc.h | 75 +++-
arch/arm64/include/uapi/asm/rmi-da.h | 43 ++
arch/arm64/kvm/rmi-exit.c | 55 +++
arch/arm64/kvm/rmi.c | 183 +++++++-
drivers/virt/coco/arm-cca-host/arm-cca.c | 200 +++++++++
drivers/virt/coco/arm-cca-host/rmi-da.c | 544 ++++++++++++++++++++++-
drivers/virt/coco/arm-cca-host/rmi-da.h | 39 ++
include/linux/kvm_host.h | 1 +
include/uapi/linux/kvm.h | 10 +
virt/kvm/kvm_main.c | 6 +
14 files changed, 1337 insertions(+), 17 deletions(-)
create mode 100644 arch/arm64/include/uapi/asm/rmi-da.h
--
2.43.0
^ permalink raw reply
* [RFC PATCH v3 11/11] coco: guest: arm64: Enable vdev DMA after attestation
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:04 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price, Jonathan Cameron
In-Reply-To: <20260312080442.3485633-1-aneesh.kumar@kernel.org>
- define SMC_RSI_VDEV_DMA_ENABLE and add wrapper in rsi_cmds.h
- invoke the new helper from the guest accept path once the device
passes attestation, rolling back to TDI_LOCKED on failure
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/rsi_cmds.h | 16 ++++++++++++++++
arch/arm64/include/asm/rsi_smc.h | 2 ++
drivers/virt/coco/arm-cca-guest/rsi-da.c | 14 ++++++++++++++
3 files changed, 32 insertions(+)
diff --git a/arch/arm64/include/asm/rsi_cmds.h b/arch/arm64/include/asm/rsi_cmds.h
index f72d8e0cd422..1e0d1cd8841a 100644
--- a/arch/arm64/include/asm/rsi_cmds.h
+++ b/arch/arm64/include/asm/rsi_cmds.h
@@ -226,4 +226,20 @@ static inline unsigned long rsi_vdev_get_info(unsigned long vdev_id,
return res.a0;
}
+static inline unsigned long __rsi_vdev_dma_enable(unsigned long vdev_id,
+ unsigned long flags,
+ unsigned long non_ats_plane,
+ unsigned long lock_nonce,
+ unsigned long meas_nonce,
+ unsigned long report_nonce)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_invoke(SMC_RSI_VDEV_DMA_ENABLE, vdev_id, flags,
+ non_ats_plane, lock_nonce,
+ meas_nonce, report_nonce, &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 5f1837282237..d2ea3656ea8f 100644
--- a/arch/arm64/include/asm/rsi_smc.h
+++ b/arch/arm64/include/asm/rsi_smc.h
@@ -186,6 +186,8 @@ struct realm_config {
*/
#define SMC_RSI_IPA_STATE_GET SMC_RSI_FID(0x198)
+#define SMC_RSI_VDEV_DMA_ENABLE SMC_RSI_FID(0x19C)
+
struct rsi_vdevice_info {
union {
struct {
diff --git a/drivers/virt/coco/arm-cca-guest/rsi-da.c b/drivers/virt/coco/arm-cca-guest/rsi-da.c
index 4030fa213ff4..74594066f46c 100644
--- a/drivers/virt/coco/arm-cca-guest/rsi-da.c
+++ b/drivers/virt/coco/arm-cca-guest/rsi-da.c
@@ -231,9 +231,17 @@ int cca_verify_digests(u64 hash_algo,
return 0;
}
+static inline int rsi_vdev_enable_dma(int vdev_id, struct dsm_device_info *dev_info)
+{
+ /* No ATS support */
+ return __rsi_vdev_dma_enable(vdev_id, 0, 0, dev_info->lock_nonce,
+ dev_info->meas_nonce, dev_info->report_nonce);
+}
+
int cca_device_accept(struct pci_dev *pdev, unsigned long lock_nonce)
{
int ret;
+ int vdev_id = rsi_vdev_id(pdev);
struct cca_guest_dsc *dsc = to_cca_guest_dsc(pdev);
if (lock_nonce != dsc->dev_info.lock_nonce) {
@@ -270,6 +278,12 @@ int cca_device_accept(struct pci_dev *pdev, unsigned long lock_nonce)
return ret;
}
+ if (rsi_vdev_enable_dma(vdev_id, &dsc->dev_info)) {
+ rhi_vdev_set_tdi_state(pdev, RHI_DA_TDI_CONFIG_LOCKED);
+ pci_err(pdev, "failed to enable DMA from the device\n");
+ return -EIO;
+ }
+
dsc->pci.mmio = no_free_ptr(tsm_mmio);
return 0;
}
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v3 10/11] coco: arm64: dma: Update force_dma_unencrypted for accepted devices
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:04 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price
In-Reply-To: <20260312080442.3485633-1-aneesh.kumar@kernel.org>
This change updates the DMA behavior for accepted devices by assuming
they access only private memory. Currently, the DMA API does not provide
a mechanism for allocating shared memory that can be accessed by both
the secure realm and the non-secure host. Accepted devices are therefore
expected to operate entirely within the private memory space.
If future use cases require accepted devices to interact with shared
memory— for example, for host-device communication, we will need to
extend the DMA interface to support such allocation semantics. This
commit lays the groundwork for that by clearly defining the current
assumption and isolating the enforcement to force_dma_unencrypted.
Treat swiotlb and decrypted DMA pools as shared-memory paths and avoid them
for accepted devices by:
- returning false from is_swiotlb_for_alloc() for accepted devices
- returning false from is_swiotlb_active() for accepted devices
- bypassing dma-direct atomic pool usage for accepted devices
This is based on the current assumption that accepted devices operate on private
Realm memory only, and prevents accidental fallback to shared/decrypted DMA
backends.
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/mem_encrypt.h | 6 +-----
arch/arm64/mm/mem_encrypt.c | 10 ++++++++++
include/linux/swiotlb.h | 3 +++
kernel/dma/direct.c | 8 ++++++++
kernel/dma/swiotlb.c | 3 +++
5 files changed, 25 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/include/asm/mem_encrypt.h b/arch/arm64/include/asm/mem_encrypt.h
index 5541911eb028..ae0b0cac0900 100644
--- a/arch/arm64/include/asm/mem_encrypt.h
+++ b/arch/arm64/include/asm/mem_encrypt.h
@@ -15,17 +15,13 @@ 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);
+bool force_dma_unencrypted(struct device *dev);
#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)
-{
- return is_realm_world();
-}
-
/*
* For Arm CCA guests, canonical addresses are "encrypted", so no changes
* required for dma_addr_encrypted().
diff --git a/arch/arm64/mm/mem_encrypt.c b/arch/arm64/mm/mem_encrypt.c
index f5d64bc29c20..18dea5d879b8 100644
--- a/arch/arm64/mm/mem_encrypt.c
+++ b/arch/arm64/mm/mem_encrypt.c
@@ -18,6 +18,7 @@
#include <linux/err.h>
#include <linux/mm.h>
#include <linux/mem_encrypt.h>
+#include <linux/device.h>
static const struct arm64_mem_crypt_ops *crypt_ops;
@@ -67,3 +68,12 @@ size_t mem_decrypt_granule_size(void)
return PAGE_SIZE;
}
EXPORT_SYMBOL_GPL(mem_decrypt_granule_size);
+
+bool force_dma_unencrypted(struct device *dev)
+{
+ if (device_cc_accepted(dev))
+ return false;
+
+ return is_realm_world();
+}
+EXPORT_SYMBOL_GPL(force_dma_unencrypted);
diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h
index 0efb9b8e5dd0..224dcec6a58f 100644
--- a/include/linux/swiotlb.h
+++ b/include/linux/swiotlb.h
@@ -296,6 +296,9 @@ bool swiotlb_free(struct device *dev, struct page *page, size_t size);
static inline bool is_swiotlb_for_alloc(struct device *dev)
{
+ if (device_cc_accepted(dev))
+ return false;
+
return dev->dma_io_tlb_mem->for_alloc;
}
#else
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 34eccd047e9b..a7a9984db342 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -158,6 +158,14 @@ static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
*/
static bool dma_direct_use_pool(struct device *dev, gfp_t gfp)
{
+ /*
+ * Atomic pools are marked decrypted and are used if we require
+ * updation of pfn mem encryption attributes or for DMA non-coherent
+ * device allocation. Both is not true for trusted device.
+ */
+ if (device_cc_accepted(dev))
+ return false;
+
return !gfpflags_allow_blocking(gfp) && !is_swiotlb_for_alloc(dev);
}
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 309a8b398a7d..339147d1d42f 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -1634,6 +1634,9 @@ bool is_swiotlb_active(struct device *dev)
{
struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
+ if (device_cc_accepted(dev))
+ return false;
+
return mem && mem->nslabs;
}
--
2.43.0
^ permalink raw reply related
* [RFC PATCH v3 09/11] coco: guest: arm64: Hook TSM accept to Realm TDISP RUN transition
From: Aneesh Kumar K.V (Arm) @ 2026-03-12 8:04 UTC (permalink / raw)
To: linux-coco, kvmarm, linux-arm-kernel
Cc: linux-kernel, Aneesh Kumar K.V (Arm), Marc Zyngier,
Catalin Marinas, Will Deacon, Jonathan Cameron, Jason Gunthorpe,
Dan Williams, Alexey Kardashevskiy, Samuel Ortiz, Xu Yilun,
Suzuki K Poulose, Steven Price, Jonathan Cameron
In-Reply-To: <20260312080442.3485633-1-aneesh.kumar@kernel.org>
Add an accept callback in pci_tsm_ops and implement cca_device_accept() to:
- verify evidence generation (lock_nonce)
- allocate and register protected MMIO ranges
- transition TDI state to RUN
Cc: Marc Zyngier <maz@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Alexey Kardashevskiy <aik@amd.com>
Cc: Samuel Ortiz <sameo@rivosinc.com>
Cc: Xu Yilun <yilun.xu@linux.intel.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Steven Price <steven.price@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/virt/coco/arm-cca-guest/arm-cca.c | 20 +++++++++++
drivers/virt/coco/arm-cca-guest/rsi-da.c | 43 +++++++++++++++++++++++
drivers/virt/coco/arm-cca-guest/rsi-da.h | 1 +
3 files changed, 64 insertions(+)
diff --git a/drivers/virt/coco/arm-cca-guest/arm-cca.c b/drivers/virt/coco/arm-cca-guest/arm-cca.c
index 80ee20c8a7a6..84152f505b10 100644
--- a/drivers/virt/coco/arm-cca-guest/arm-cca.c
+++ b/drivers/virt/coco/arm-cca-guest/arm-cca.c
@@ -499,9 +499,29 @@ static void cca_tsm_unlock(struct pci_tsm *tsm)
kfree(cca_dsc);
}
+static int __cca_tsm_accept(struct pci_dev *pdev, unsigned long lock_nonce)
+{
+ int ret;
+
+ ret = cca_device_accept(pdev, lock_nonce);
+ if (ret) {
+ pci_err(pdev, "failed to transition the device to run state (%d)\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int cca_tsm_accept(struct pci_dev *pdev)
+{
+ struct cca_guest_dsc *dsc = to_cca_guest_dsc(pdev);
+
+ return __cca_tsm_accept(pdev, dsc->dev_info.lock_nonce);
+}
+
static struct pci_tsm_ops cca_devsec_pci_ops = {
.lock = cca_tsm_lock,
.unlock = cca_tsm_unlock,
+ .accept = cca_tsm_accept,
};
static void cca_devsec_tsm_remove(void *tsm_dev)
diff --git a/drivers/virt/coco/arm-cca-guest/rsi-da.c b/drivers/virt/coco/arm-cca-guest/rsi-da.c
index 6f40329ac2f9..4030fa213ff4 100644
--- a/drivers/virt/coco/arm-cca-guest/rsi-da.c
+++ b/drivers/virt/coco/arm-cca-guest/rsi-da.c
@@ -230,3 +230,46 @@ int cca_verify_digests(u64 hash_algo,
}
return 0;
}
+
+int cca_device_accept(struct pci_dev *pdev, unsigned long lock_nonce)
+{
+ int ret;
+ struct cca_guest_dsc *dsc = to_cca_guest_dsc(pdev);
+
+ if (lock_nonce != dsc->dev_info.lock_nonce) {
+ pci_err(pdev, "Device evidence generation mismatch\n");
+ return -EIO;
+ }
+
+ /* Allocation private mmio range based on interface report. */
+ struct pci_tsm_mmio *tsm_mmio __free(kfree) = pci_tsm_mmio_alloc(pdev);
+ if (!tsm_mmio) {
+ pci_err(pdev, "Protected mmio range allocation failure\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * Present the private mmio range in the resource hierarchy.
+ * We don't use this for ioremap, ioremap check the RIPAS value.
+ */
+ ret = pci_tsm_mmio_setup(pdev, tsm_mmio);
+ if (ret) {
+ pci_err(pdev, "Protected mmio setup failure\n");
+ return ret;
+ }
+
+ ret = cca_map_evidence_report_range(pdev, tsm_mmio);
+ if (ret) {
+ pci_err(pdev, "failed to validate the interface report\n");
+ return ret;
+ }
+
+ ret = rhi_vdev_set_tdi_state(pdev, RHI_DA_TDI_CONFIG_RUN);
+ if (ret) {
+ pci_err(pdev, "failed to switch the device (%u) to RUN state\n", ret);
+ return ret;
+ }
+
+ dsc->pci.mmio = no_free_ptr(tsm_mmio);
+ return 0;
+}
diff --git a/drivers/virt/coco/arm-cca-guest/rsi-da.h b/drivers/virt/coco/arm-cca-guest/rsi-da.h
index 4903a770412e..c550926145a0 100644
--- a/drivers/virt/coco/arm-cca-guest/rsi-da.h
+++ b/drivers/virt/coco/arm-cca-guest/rsi-da.h
@@ -61,4 +61,5 @@ int cca_verify_digests(u64 hash_algo,
uint8_t *interface_report, size_t interface_report_size,
uint8_t *measurements, size_t measurements_size,
struct rsi_vdevice_info *dev_info);
+int cca_device_accept(struct pci_dev *pdev, unsigned long lock_nonce);
#endif
--
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