Linux Confidential Computing Development
 help / color / mirror / Atom feed
* Re: [PATCH v8 13/46] KVM: guest_memfd: Add base support for KVM_SET_MEMORY_ATTRIBUTES2
From: Ackerley Tng @ 2026-07-06 18:17 UTC (permalink / raw)
  To: Suzuki K Poulose, aik, andrew.jones, binbin.wu, brauner,
	chao.p.peng, david, jmattson, jthoughton, michael.roth, oupton,
	pankaj.gupta, qperret, rick.p.edgecombe, rientjes, shivankg,
	steven.price, tabba, willy, wyihan, yan.y.zhao, forkloop,
	pratyush, aneesh.kumar, liam, Paolo Bonzini, Sean Christopherson,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Steven Rostedt, Masami Hiramatsu,
	Mathieu Desnoyers, Jonathan Corbet, Shuah Khan, Shuah Khan,
	Vishal Annapurve, Andrew Morton, Chris Li, Kairui Song,
	Kemeng Shi, Nhat Pham, Barry Song, Axel Rasmussen, Yuanchu Xie,
	Wei Xu, Youngjun Park, Qi Zheng, Shakeel Butt, Kiryl Shutsemau,
	Baoquan He, Jason Gunthorpe, Vlastimil Babka
  Cc: kvm, linux-kernel, linux-trace-kernel, linux-doc, linux-kselftest,
	linux-mm, linux-coco
In-Reply-To: <114e2488-97ed-4740-a8e8-1edd991f26c5@arm.com>

Suzuki K Poulose <suzuki.poulose@arm.com> writes:

>
> [...snip...]
>
>> +static int __kvm_gmem_set_attributes(struct inode *inode, pgoff_t start,
>> +				     size_t nr_pages, uint64_t attrs)
>> +{
>> +	struct address_space *mapping = inode->i_mapping;
>> +	struct gmem_inode *gi = GMEM_I(inode);
>> +	pgoff_t end = start + nr_pages;
>> +	struct maple_tree *mt;
>> +	struct ma_state mas;
>> +	int r;
>> +
>> +	mt = &gi->attributes;
>> +
>> +	filemap_invalidate_lock(mapping);
>> +
>> +	mas_init(&mas, mt, start);
>> +	r = kvm_gmem_mas_preallocate(&mas, attrs, start, nr_pages);
>> +	if (r)
>> +		goto out;
>> +
>> +	/*
>> +	 * From this point on guest_memfd has performed necessary
>> +	 * checks and can proceed to do guest-breaking changes.
>> +	 */
>> +
>> +	kvm_gmem_invalidate_start(inode, start, end);
>
> I added support for Arm CCA KVM patches with the inplace conversion and
> I am hitting the following issue.
>
> 1. I am supporting INIT_SHARED + MMAP flags.
> 2. VMM creates the Gmem_fd with both the flags above.
> 3. Uses the shared gmem-mmap to load the initial payloads (kernel, dtb).
> 4. At the VM finalization time, Populate the loaded regions one by one
>     by
>      a) copying the images to a temparory buffer - Since CCA can't really
>         load the contents in-place.

Sounds good :). I see that you blocked this in the kernel by returning
-EOPNOTSUPP if (!src_page) [0].

>      b) Set the "region" to Private in the gmem_fd (via
> SET_MEMORY_ATTRIBUTES2)
>      c) Invoke CCA backend to populate the private memory via
>         ioctl(KVM_ARM_RMI_POPULATE,..) [0]
>

This flow sounds right.

> [0]
> https://lore.kernel.org/all/20260513131757.116630-27-steven.price@arm.com/
>
>
> 5. Additionally, VMM can mark the entire RAM to be private before the VM
>     starts running, again via SET_MEMORY_ATTRIBUTES2. On CCA, this
> action is measured and doesn't require the Host to "commit" memory to
> the VM.
> Instead the host can lazily donate memory on a fault.
>

For both TDX and SNP, the host can also lazily donate memory,
guest_memfd supports this.

> But step (5) triggers the invalidation of both private and shared
> mappings of the gmem area, from the kvm_gmem_invalidate_start()
> above.
>
> This is because, the entire DRAM now has, some portions PRIVATE (the
> loaded regions) and the rest are SHARED (from the Gmem_fd creation).
>   Thus, kvm_gmem_get_invalidate_filter(Dram_start, Dram_end) causes the
> invalidation of both "PRIVATE" and "SHARED" regions, which results
> in the destruction of the already loaded data and things go south.
>

This destruction will happen for TDX as well. I think we managed to get
around this because we didn't apply conversion on the already-private
ranges.

IIUC on SNP, zapping pages in the stage 2 page tables doesn't destroy
the data, so that's probably why it has been fine for SNP.

> When we know that the kvm_gmem_invalidate_xx is triggered by a
> conversion, we don't need to invalidate the existing pages that
> are in the requested state. i.e., the following patch on top of
> this series does the trick for me :
>
>
> diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
> index a97fcac34a0e..62e0427a49f4 100644
> --- a/virt/kvm/guest_memfd.c
> +++ b/virt/kvm/guest_memfd.c
> @@ -250,16 +250,23 @@ static void __kvm_gmem_invalidate_start(struct
> gmem_file *f, pgoff_t start,
>                  KVM_MMU_UNLOCK(kvm);
>   }
>
> +static void kvm_gmem_invalidate_start_filter(struct inode *inode,
> pgoff_t start,
> +                                            pgoff_t end,
> +                                            enum kvm_gfn_range_filter
> attr_filter)
> +{
> +       struct gmem_file *f;
> +
> +       kvm_gmem_for_each_file(f, inode)
> +               __kvm_gmem_invalidate_start(f, start, end, attr_filter);
> +}
> +
>   static void kvm_gmem_invalidate_start(struct inode *inode, pgoff_t start,
>                                        pgoff_t end)
>   {
>          enum kvm_gfn_range_filter attr_filter;
> -       struct gmem_file *f;
> -
>          attr_filter = kvm_gmem_get_invalidate_filter(inode, start, end);
>
> -       kvm_gmem_for_each_file(f, inode)
> -               __kvm_gmem_invalidate_start(f, start, end, attr_filter);
> +       kvm_gmem_invalidate_start_filter(inode, start, end, attr_filter);
>   }
>
>   static void __kvm_gmem_invalidate_end(struct gmem_file *f, pgoff_t start,
> @@ -724,9 +731,14 @@ static int __kvm_gmem_set_attributes(struct inode
> *inode, pgoff_t start,
>          /*
>           * From this point on guest_memfd has performed necessary
>           * checks and can proceed to do guest-breaking changes.
> +        * Also, we don't have to invalidate the regions that
> +        * may already be in the requested state. Hence, we could
> +        * explicitly filter the invalidations to the opposite
> +        * state.
>           */
>
> -       kvm_gmem_invalidate_start(inode, start, end);
> +       kvm_gmem_invalidate_start_filter(inode, start, end,
> +                                       to_private ? KVM_FILTER_SHARED :
> KVM_FILTER_PRIVATE);
>

I think this makes sense. Thanks for catching this.

>          if (!to_private)
>                  kvm_gmem_invalidate(inode, start, end);
>
>
> Thoughts ?
>
> Suzuki
>
>
>>
>> [...snip...]
>>

^ permalink raw reply

* Re: [PATCH v5 01/51] x86/apic: Provide helpers to set local APIC timer period in hz and khz
From: Sean Christopherson @ 2026-07-06 18:00 UTC (permalink / raw)
  To: Michael Kelley
  Cc: Jonathan Corbet, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86@kernel.org, Kiryl Shutsemau,
	Rick Edgecombe, K. Y. Srinivasan, Haiyang Zhang, Wei Liu,
	Dexuan Cui, Long Li, Ajay Kaher, Alexey Makhalov, Jan Kiszka,
	Andy Lutomirski, Peter Zijlstra, Juergen Gross, Daniel Lezcano,
	John Stultz, Shuah Khan, H. Peter Anvin, Vitaly Kuznetsov,
	Broadcom internal kernel review list, Boris Ostrovsky,
	Stephen Boyd, linux-doc@vger.kernel.org, kvm@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev,
	linux-hyperv@vger.kernel.org, virtualization@lists.linux.dev,
	xen-devel@lists.xenproject.org, Tom Lendacky, Nikunj A Dadhania,
	David Woodhouse, David Woodhouse, Thomas Gleixner
In-Reply-To: <SN6PR02MB41578D3C34AB283B892C27A4D4F52@SN6PR02MB4157.namprd02.prod.outlook.com>

On Thu, Jul 02, 2026, Michael Kelley wrote:
> > @@ -796,6 +796,16 @@ bool __init apic_needs_pit(void)
> >  	return lapic_timer_period == 0;
> >  }
> > 
> > +void apic_set_timer_period_khz(u64 period_khz, const char *source)
> > +{
> > +	lapic_timer_period = mul_u64_u32_div(period_khz, 1000, HZ);
> > +}
> > +
> > +void apic_set_timer_period_hz(u64 period_hz, const char *source)
> > +{
> > +	lapic_timer_period = div_u64(period_hz, HZ);
> > +}
> 
> A string "source" argument is passed in, but not used. Is there an
> envisioned future use? Also, this function doesn't output a pr_info()
> message like the existing Hyper-V and VMware code does. 

It was a complete goof on my part (Sashiko also pointed out the oddity[*]).  I
fully intended to log a message and provide equivalent Hyper-V/VMware behavior,
and totally spaced it.

[*] https://lore.kernel.org/all/20260701194621.4BD691F000E9@smtp.kernel.org

> I don't know that the message is all that useful, though I do remember one
> case where I was debugging some clock/timer issue when I looked at it. 


^ permalink raw reply

* Re: [PATCH v2 16/17] KVM: TDX: Add in-kernel Quote generation
From: Sean Christopherson @ 2026-07-06 17:57 UTC (permalink / raw)
  To: Peter Fang
  Cc: Rick P Edgecombe, djbw@kernel.org, kvm@vger.kernel.org,
	linux-coco@lists.linux.dev, Xiaoyao Li, Dave Hansen,
	dave.hansen@linux.intel.com, baolu.lu@linux.intel.com,
	Adrian Hunter, kas@kernel.org, tony.lindgren@linux.intel.com,
	Yilun Xu, linux-kernel@vger.kernel.org, Sohil Mehta,
	Zhenzhong Duan, Kishen Maloor, yilun.xu@linux.intel.com,
	x86@kernel.org
In-Reply-To: <20260704054342.GB2169894@pedri>

On Fri, Jul 03, 2026, Peter Fang wrote:
> On Wed, Jul 01, 2026 at 11:45:53AM -0700, Edgecombe, Rick P wrote:
> > On Wed, 2026-07-01 at 10:25 -0700, Sean Christopherson wrote:
> > > > > That is a good question. The answer is partly historical reasons, but I
> > > > > think the pros/cons don’t really move the needle too much.
> > > > > 
> > > > > The main benefit of doing it with the host in the loop is that the guest
> > > > > side TDVMCALL quoting interface can stay the same. There is also a wrinkle
> > > > > in that there is a limited HW resource involved in the quoting,
> > > 
> > > What is this magical resource?
> > 
> > It's a HW crypto thing. I'll let Peter explain more.
> 
> It's called S3M (Secured Startup Services). There was once a public
> document about it, but it was removed for some reason. It basically
> provides a number of boot and security services through dedicated HW.
> Attestation is one of them. This is part of our plan to develop new
> attestation schemes without using SGX.
> 
> There was a lot of internal debate about how this HW should be managed.
> We decided to let the TDX module handle it and sort of hide it from the
> host kernel and only expose it as SEAMCALLs. It however impacted how the
> quoting SEAMCALLs look, so apologies for not calling out these factors.
> 
> > 
> > > 
> > > > > so we want to do these operations one at a time. Having a mutex on the
> > > > > host is the KISS way of accomplishing some level of fairness for DOS
> > > > > prevention.
> > > 
> > > At the risk of unintentionally causing effecitvely DoS by introducing a
> > > system-wide lock.
> > 
> > The DoS that people are interested in trying to prevent is a TD getting starved
> > of quotes forever. So the lock is supposed to give some eventual fairness.
> > Eventually the guest gets its quote even if it has to wait.
> > 
> > If instead contention throws a BUSY code to the guest, the guest spins trying.
> > That wouldn't have the guarantees.
> > 
> > So I think the two options are: have some lock in the TDX module, or have a lock
> > of some sort in the host. I think actually a waiting lock in the TDX module is
> > possible. But I think there are tradeoffs besides where the extra code is. If
> > it's in Linux we get a lot more control, lockdep, etc.
> > 
> > BTW the "historical reasons" part I mentioned involves a past effort to create
> > configurable host controlled policies around managing these resources. So part
> > of the simplification is doing a simple eventually fair global lock instead of
> > something more complicated.
> 
> Yeah my bad for not including a lot more background. There were several
> design aspects that influenced this implementation. I'll do a better job
> of explaining them in the next version, but basically:
> 
>   1. Performance requirements: For this first implementation, the
>      priority is to not starve a legit guest from getting a quote, even
>      when there's a malicious guest DoSing the host. A mutex is sort of
>      a "fair enough queue" for this problem. A guest's authenticity can
>      only be verified after the quote is generated.
> 
>   2. HW limitation: The TDX module implements different attestation
>      schemes, some of which use the S3M during TDH.QUOTE.GET. This

What is "the S3M" though?  Is it a separate chip a la AMD's PSP/ASP?  Is it a
per-package thing?  Per-core?  How is it accessed, and what are the "rules" for
for those accesses?  What types of latencies are we looking at?  What else uses
the S3M?  Do we have to worry about contending with non-TDX usage?

>      limits the amount of concurrency this SEAMCALL supports and is also
>      part of why we chose a mutex for TDH.QUOTE.GET. (The shared buffer
>      in core TDX doesn't seem great, so I will improve it.)
> 
>   3. Host policies: Preserving the GetQuote GHCI also allows the host to
>      participate in the quoting process, e.g. add host-controlled
>      policies or fetch host-side quote information. It's a bit similar
>      to SEV-SNP's KVM_EXIT_SNP_REQ_CERTS [1]. This series does not
>      implement host policies though.
> 
> 
> Ultimately, there are ways to push more of this into the module, and the
> current design is kind of a tradeoff between the complexities of the
> work needed in the guest, the host, and the TDX module. Like Rick said,
> there were past versions that required way more work in the host and the
> guest, such as managing the S3M, new GHCI calls, etc.
> 
> [1] Commit fa9893fadbc2 ("KVM: Introduce KVM_EXIT_SNP_REQ_CERTS for SNP
> certificate-fetching"
> 
> > 

^ permalink raw reply

* Re: [PATCH] KVM: x86/tdx: Do not print error message on non-present feature
From: Edgecombe, Rick P @ 2026-07-06 16:54 UTC (permalink / raw)
  To: jirislaby@kernel.org, seanjc@google.com
  Cc: Gao, Chao, x86@kernel.org, bp@alien8.de, kas@kernel.org,
	dave.hansen@linux.intel.com, mingo@redhat.com,
	dan.j.williams@intel.com, hpa@zytor.com, tglx@kernel.org,
	linux-coco@lists.linux.dev, linux-kernel@vger.kernel.org,
	kvm@vger.kernel.org
In-Reply-To: <20260702043204.81741-1-jirislaby@kernel.org>

On Thu, 2026-07-02 at 06:32 +0200, Jiri Slaby (SUSE) wrote:
> Before commit 165e77353831 below, the kernel did not print anything when
> X86_FEATURE_TDX_HOST_PLATFORM was not present in a cpu.

This isn't quite correct. It would print something if KVM tried to enable it,
which is controlled by a module param.

> 
> After the commit, this is emitted to the log, with the error level:
> virt/tdx: TDX not supported by the host platform
> 
> This is disturbing as it is not an error running CPUs without the
> feature.

Agree. If the kernel is now trying to enabling TDX automatically, it is not
error print worthy if it fails because TDX is not supported.

But after the below change, something is still printed if the kernel param is
used. Can we be a little more specific?

> 
> Drop the error message completely to revert to the state before
> 165e77353831.

'state' is a little ambiguous here. Sounds like a full revert.

> 
> Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org>
> Fixes: 165e77353831 ("KVM: x86/tdx: Do VMXON and TDX-Module initialization during subsys init")
> Cc: Dan Williams <dan.j.williams@intel.com>
> Cc: Chao Gao <chao.gao@intel.com>
> Cc: Dave Hansen <dave.hansen@linux.intel.com>
> Cc: Sean Christopherson <seanjc@google.com>
> 
> --
> 
> Cc: Thomas Gleixner <tglx@kernel.org>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Borislav Petkov <bp@alien8.de>
> Cc: Dave Hansen <dave.hansen@linux.intel.com>
> Cc: x86@kernel.org
> Cc: "H. Peter Anvin" <hpa@zytor.com>
> Cc: Kiryl Shutsemau <kas@kernel.org>
> Cc: Rick Edgecombe <rick.p.edgecombe@intel.com>
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-coco@lists.linux.dev
> Cc: kvm@vger.kernel.org
> ---
>  arch/x86/virt/vmx/tdx/tdx.c | 4 +---
>  1 file changed, 1 insertion(+), 3 deletions(-)
> 
> diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
> index 42df8ea464c4..bdfc3bf12a9c 100644
> --- a/arch/x86/virt/vmx/tdx/tdx.c
> +++ b/arch/x86/virt/vmx/tdx/tdx.c
> @@ -1228,10 +1228,8 @@ static __init int tdx_enable(void)
>  	enum cpuhp_state state;
>  	int ret;
>  
> -	if (!cpu_feature_enabled(X86_FEATURE_TDX_HOST_PLATFORM)) {
> -		pr_err("TDX not supported by the host platform\n");
> +	if (!cpu_feature_enabled(X86_FEATURE_TDX_HOST_PLATFORM))
>  		return -ENODEV;
> -	}

Seems like a reasonable change to me, but the log is a bit misleading.

>  
>  	if (!cpu_feature_enabled(X86_FEATURE_XSAVE)) {
>  		pr_err("XSAVE is required for TDX\n");


^ permalink raw reply

* Re: [PATCH v14 26/44] arm64: RMI: Allow populating initial contents
From: Ackerley Tng @ 2026-07-06 16:43 UTC (permalink / raw)
  To: Steven Price, kvm, kvmarm
  Cc: Catalin Marinas, Marc Zyngier, Will Deacon, James Morse,
	Oliver Upton, Suzuki K Poulose, 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, WeiLin.Chang, Lorenzo.Pieralisi2
In-Reply-To: <20260513131757.116630-27-steven.price@arm.com>

Steven Price <steven.price@arm.com> writes:

>
> [...snip...]
>
> +
> +static int populate_region_cb(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
> +			      struct page *src_page, void *opaque)
> +{
> +	unsigned long data_flags = *(unsigned long *)opaque;
> +	phys_addr_t ipa = gfn_to_gpa(gfn);
> +
> +	if (!src_page)
> +		return -EOPNOTSUPP;

Came here from [0]!

Sean recently moved this check to before kvm_gmem_populate() was called
and returned -EINVAL [1]. I think the rationale would also apply for
CCA.

[0] https://lore.kernel.org/all/114e2488-97ed-4740-a8e8-1edd991f26c5@arm.com/
[1] https://lore.kernel.org/all/20260630213711.479692-3-seanjc@google.com/

> +
> +	return realm_data_map_init(kvm, ipa, pfn, page_to_pfn(src_page),
> +				   data_flags);
> +}
> +
>
> [...snip...]
>

^ permalink raw reply

* Re: [PATCH v7 1/6] firmware: smccc: Add an Arm SMCCC bus
From: Sudeep Holla @ 2026-07-06 15:09 UTC (permalink / raw)
  To: Aneesh Kumar K.V
  Cc: linux-coco, linux-arm-kernel, linux-kernel, Sudeep Holla,
	Catalin Marinas, Greg KH, Jeremy Linton, Jonathan Cameron,
	Lorenzo Pieralisi, Mark Rutland, Will Deacon, Steven Price,
	Suzuki K Poulose, Andre Przywara
In-Reply-To: <yq5a4iici3u3.fsf@kernel.org>

On Mon, Jul 06, 2026 at 08:05:00PM +0530, Aneesh Kumar K.V wrote:
> Sudeep Holla <sudeep.holla@kernel.org> writes:
> 
> > On Thu, Jun 11, 2026 at 06:34:24PM +0530, Aneesh Kumar K.V (Arm) wrote:
> 
> ...
> 
> >> +#define to_arm_smccc_driver(d) \
> >> +	container_of_const(d, struct arm_smccc_driver, driver)
> >> +
> >> +int arm_smccc_driver_register(struct arm_smccc_driver *driver,
> >> +		struct module *owner, const char *mod_name);
> >> +void arm_smccc_driver_unregister(struct arm_smccc_driver *driver);
> >> +struct arm_smccc_device *arm_smccc_device_register(const char *name);
> >> +void arm_smccc_device_unregister(struct arm_smccc_device *smcc_dev);
> >> +
> >
> > I may be overthinking but what will happen if HAVE_ARM_SMCCC_DISCOVERY=n
> > and some driver is compiled using this header ? It should be fine if it
> > fails to compile, just thinking out loud if we need to handle that are not.
> > As long as all the drivers using these depends on HAVE_ARM_SMCCC_DISCOVERY
> > it should be fine I think.
> 
> This will result in a build failure. The driver should either select
> HAVE_ARM_SMCCC_DISCOVERY or depend on HAVE_ARM_SMCCC_DISCOVERY.
> 

That should be fine for now.

> >
> >> +#define arm_smccc_register(driver) \
> >> +	arm_smccc_driver_register(driver, THIS_MODULE, KBUILD_MODNAME)
> >> +#define arm_smccc_unregister(driver) \
> >> +	arm_smccc_driver_unregister(driver)
> >> +
> >> +#define module_arm_smccc_driver(__arm_smccc_driver) \
> >> +	module_driver(__arm_smccc_driver, arm_smccc_register, \
> >> +		      arm_smccc_unregister)
> >> +
> >> +extern const struct bus_type arm_smccc_bus_type;
> >> +
> >> +#endif /* __LINUX_ARM_SMCCC_BUS_H */
> >> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
> >> index 23ff24080dfd..c9cee8c5a0b2 100644
> >> --- a/include/linux/mod_devicetable.h
> >> +++ b/include/linux/mod_devicetable.h
> >
> > This file seems to be reworked recently, so you need to rebase it moving
> > smccc specific changes to separate file I think.
> >
> 
> Do we need to add a separate file? I was able to rebase the series on
> 7.2-rc1
> 

Then could be something changed between -rc1 to -rc2, I couldn't apply the
patch.


[...]

> >> +
> >> +/**
> >> + * struct arm_smccc_device_id - Arm SMCCC bus device identifier
> >> + * @name: SMCCC device name
> >> + * @driver_data: driver data
> >> + */
> >> +struct arm_smccc_device_id {
> >> +	char name[ARM_SMCCC_NAME_SIZE];
> >> +	kernel_ulong_t driver_data;
> >
> > Can't find any users of the above driver_data ?
> > Is it for future ? If so, can you add details on how it is supposed to be used
> > if you don't want to drop it.
> >
> 
> That would depend on the driver, wouldn’t it? I was comparing this with
> struct auxiliary_device_id, and very few drivers use driver_data;
> mei_gsc_probe() is one example.
> 

Indeed, but I don't see any attempt to use it currently which is fine but
I also fail how this can be used in future in SMCCC context.

IIUC driver_data is a per-match-entry cookie. It is useful when one driver
supports multiple IDs and wants each ID to select  slightly different
behavior.

The issues as I see are:
1. No driver is using it in this series.
2. The SMCCC bus match code finds the matching table entry, but then
   throws it away. It is not passed to probe for example.

> I am not sure there is a generic rule here. IIUC, it provides a place
> where a driver can add driver-specific data that can be used during the
> probe routine.
>

How is that possible unless we either add a function like

  |  const struct arm_smccc_device_id *
  |  arm_smccc_get_device_id(struct arm_smccc_device *sdev);

to get the pointer to the id_table or that driver_data from the probe.

Or change probe to receive the matched ID/driver_data:

  int (*probe)(struct arm_smccc_device *sdev,
  	     const struct arm_smccc_device_id *id);

Please drop it if you have no plans to use it or see any future usage.

-- 
Regards,
Sudeep

^ permalink raw reply

* Re: [PATCH v5 02/10] irqchip/gic-v3-its: Align shared ITS allocations to the CoCo shared granule size
From: Marc Zyngier @ 2026-07-06 14:44 UTC (permalink / raw)
  To: Aneesh Kumar K.V
  Cc: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu,
	Catalin Marinas, Jason Gunthorpe, Marek Szyprowski, Robin Murphy,
	Steven Price, Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <yq5a7bn8i69y.fsf@kernel.org>

On Mon, 06 Jul 2026 14:42:17 +0100,
Aneesh Kumar K.V <aneesh.kumar@kernel.org> wrote:
> 
> Marc Zyngier <maz@kernel.org> writes:
> 
> > On Mon, 06 Jul 2026 07:04:24 +0100,
> > "Aneesh Kumar K.V (Arm)" <aneesh.kumar@kernel.org> wrote:
> >> 
> >> ITS tables allocated by the coco guest are shared with the hypervisor.
> >> These allocations must satisfy the host shared-buffer granule size so that
> >> the full converted range is safe for host access.
> >> 
> >> Allocate ITS pages using a size rounded up to the shared granule size and
> >> use the same allocation order when encrypting, decrypting and freeing the
> >> memory. Also grow the ITT cache in shared-granule sized chunks instead of
> >> assuming PAGE_SIZE is sufficient.
> >> 
> >> Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
> >> ---
> >>  drivers/irqchip/irq-gic-v3-its.c | 17 +++++++++++------
> >>  1 file changed, 11 insertions(+), 6 deletions(-)
> >> 
> >> diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
> >> index b57d81ad33a0..5d6d38aa0dae 100644
> >> --- a/drivers/irqchip/irq-gic-v3-its.c
> >> +++ b/drivers/irqchip/irq-gic-v3-its.c
> >> @@ -213,16 +213,17 @@ static gfp_t gfp_flags_quirk;
> >>  static struct page *its_alloc_pages_node(int node, gfp_t gfp,
> >>  					 unsigned int order)
> >>  {
> >> +	unsigned int alloc_order;
> >>  	struct page *page;
> >>  	int ret = 0;
> >>  
> >> -	page = alloc_pages_node(node, gfp | gfp_flags_quirk, order);
> >> -
> >> +	alloc_order = get_order(mem_cc_align_to_shared_granule(PAGE_SIZE << order));
> >> +	page = alloc_pages_node(node, gfp | gfp_flags_quirk, alloc_order);
> >>  	if (!page)
> >>  		return NULL;
> >>  
> >>  	ret = set_memory_decrypted((unsigned long)page_address(page),
> >> -				   1 << order);
> >> +				   1 << alloc_order);
> >>  	/*
> >>  	 * If set_memory_decrypted() fails then we don't know what state the
> >>  	 * page is in, so we can't free it. Instead we leak it.
> >> @@ -241,13 +242,16 @@ static struct page *its_alloc_pages(gfp_t gfp, unsigned int order)
> >>  
> >>  static void its_free_pages(void *addr, unsigned int order)
> >>  {
> >> +	int alloc_order;
> >> +
> >> +	alloc_order = get_order(mem_cc_align_to_shared_granule(PAGE_SIZE << order));
> >>  	/*
> >>  	 * If the memory cannot be encrypted again then we must leak the pages.
> >>  	 * set_memory_encrypted() will already have WARNed.
> >>  	 */
> >> -	if (set_memory_encrypted((unsigned long)addr, 1 << order))
> >> +	if (set_memory_encrypted((unsigned long)addr, 1 << alloc_order))
> >>  		return;
> >> -	free_pages((unsigned long)addr, order);
> >> +	free_pages((unsigned long)addr, alloc_order);
> >
> > The comments I had in [1] are still applicable.
> >
> > 	M.
> >
> > [1] https://lore.kernel.org/all/86zf2ozrb8.wl-maz@kernel.org/
> >
> 
> I renamed it to alloc_order to make it clear that it refers to the
> allocation order rather than the requested order. If you feel strongly
> about not introducing another variable, I'll drop the new variable.

I do. At the very least, to avoid you changing the type of the order
in its_free_pages(). These things do matter.

	M.

-- 
Without deviation from the norm, progress is not possible.

^ permalink raw reply

* Re: [PATCH v7 1/6] firmware: smccc: Add an Arm SMCCC bus
From: Aneesh Kumar K.V @ 2026-07-06 14:35 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: linux-coco, linux-arm-kernel, linux-kernel, Sudeep Holla,
	Catalin Marinas, Greg KH, Jeremy Linton, Jonathan Cameron,
	Lorenzo Pieralisi, Mark Rutland, Will Deacon, Steven Price,
	Suzuki K Poulose, Andre Przywara
In-Reply-To: <20260706-belligerent-excellent-saluki-a4e431@sudeepholla>

Sudeep Holla <sudeep.holla@kernel.org> writes:

> On Thu, Jun 11, 2026 at 06:34:24PM +0530, Aneesh Kumar K.V (Arm) wrote:

...

>> +static int arm_smccc_bus_match(struct device *dev,
>> +		const struct device_driver *drv)
>> +{
>> +	const struct arm_smccc_device_id *id_table;
>> +	struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev);
>> +
>> +	id_table = to_arm_smccc_driver(drv)->id_table;
>> +	if (!id_table)
>> +		return 0;
>> +
>
> Any reason for not having this check when registering the driver also so
> that it becomes a must to register ? I remember vaguely adding this as fix to
> FF-A driver recently.
>

I will add

int arm_smccc_driver_register(struct arm_smccc_driver *driver,
		struct module *owner, const char *mod_name)
{
	if (!driver->probe || !driver->id_table)
		return -EINVAL;


>
>> +	while (id_table->name[0]) {
>> +		if (!strcmp(smccc_dev->name, id_table->name))
>> +			return 1;
>> +		id_table++;
>> +	}
>> +
>> +	return 0;
>> +}
>> +

...

>> +struct arm_smccc_device *arm_smccc_device_register(const char *name)
>> +{
>> +	struct arm_smccc_device *smccc_dev;
>> +	int id, ret;
>> +
>> +	id = ida_alloc_min(&arm_smccc_bus_id, 1, GFP_KERNEL);
>> +	if (id < 0)
>> +		return ERR_PTR(id);
>> +
>> +	smccc_dev = kzalloc_obj(*smccc_dev);
>> +	if (!smccc_dev) {
>> +		ida_free(&arm_smccc_bus_id, id);
>> +		return ERR_PTR(-ENOMEM);
>> +	}
>> +
>> +	smccc_dev->id = id;
>> +	if (strscpy(smccc_dev->name, name) < 0) {
>
> Since this is exported symbol and one can call arm_smccc_device_register(ptr)
> where ptr = NULL, won't it blow up then ? IIUC strscpy() handles NULL dst
> but doesn't check for NULL src.
>

Will add the NULL check.

>> +		kfree(smccc_dev);
>> +		ida_free(&arm_smccc_bus_id, id);
>> +		return ERR_PTR(-EINVAL);
>> +	}
>> +	smccc_dev->dev.bus = &arm_smccc_bus_type;
>> +	smccc_dev->dev.release = arm_smccc_release_device;
>> +
>> +	ret = dev_set_name(&smccc_dev->dev, "%s-%d", smccc_dev->name, id);
>> +	if (ret) {
>> +		kfree(smccc_dev);
>> +		ida_free(&arm_smccc_bus_id, id);
>> +		return ERR_PTR(ret);
>> +	}
>> +
>> +	ret = device_register(&smccc_dev->dev);
>> +	if (ret) {
>> +		put_device(&smccc_dev->dev);
>> +		return ERR_PTR(ret);
>> +	}
>> +
>> +	return smccc_dev;
>> +}
>> +EXPORT_SYMBOL_GPL(arm_smccc_device_register);

...

>> +#define to_arm_smccc_driver(d) \
>> +	container_of_const(d, struct arm_smccc_driver, driver)
>> +
>> +int arm_smccc_driver_register(struct arm_smccc_driver *driver,
>> +		struct module *owner, const char *mod_name);
>> +void arm_smccc_driver_unregister(struct arm_smccc_driver *driver);
>> +struct arm_smccc_device *arm_smccc_device_register(const char *name);
>> +void arm_smccc_device_unregister(struct arm_smccc_device *smcc_dev);
>> +
>
> I may be overthinking but what will happen if HAVE_ARM_SMCCC_DISCOVERY=n
> and some driver is compiled using this header ? It should be fine if it
> fails to compile, just thinking out loud if we need to handle that are not.
> As long as all the drivers using these depends on HAVE_ARM_SMCCC_DISCOVERY
> it should be fine I think.

This will result in a build failure. The driver should either select
HAVE_ARM_SMCCC_DISCOVERY or depend on HAVE_ARM_SMCCC_DISCOVERY.

>
>> +#define arm_smccc_register(driver) \
>> +	arm_smccc_driver_register(driver, THIS_MODULE, KBUILD_MODNAME)
>> +#define arm_smccc_unregister(driver) \
>> +	arm_smccc_driver_unregister(driver)
>> +
>> +#define module_arm_smccc_driver(__arm_smccc_driver) \
>> +	module_driver(__arm_smccc_driver, arm_smccc_register, \
>> +		      arm_smccc_unregister)
>> +
>> +extern const struct bus_type arm_smccc_bus_type;
>> +
>> +#endif /* __LINUX_ARM_SMCCC_BUS_H */
>> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
>> index 23ff24080dfd..c9cee8c5a0b2 100644
>> --- a/include/linux/mod_devicetable.h
>> +++ b/include/linux/mod_devicetable.h
>
> This file seems to be reworked recently, so you need to rebase it moving
> smccc specific changes to separate file I think.
>

Do we need to add a separate file? I was able to rebase the series on
7.2-rc1

modified   include/linux/mod_devicetable.h
@@ -879,6 +879,19 @@ struct auxiliary_device_id {
 	kernel_ulong_t driver_data;
 };
 
+#define ARM_SMCCC_NAME_SIZE 40
+#define ARM_SMCCC_MODULE_PREFIX "arm_smccc:"
+
+/**
+ * struct arm_smccc_device_id - Arm SMCCC bus device identifier
+ * @name: SMCCC device name
+ * @driver_data: driver data
+ */
+struct arm_smccc_device_id {
+	char name[ARM_SMCCC_NAME_SIZE];
+	kernel_ulong_t driver_data;
+};
+
 /* Surface System Aggregator Module */
 
 #define SSAM_MATCH_TARGET	0x1


>> @@ -876,6 +876,19 @@ struct auxiliary_device_id {
>>  	kernel_ulong_t driver_data;
>>  };
>>  
>> +#define ARM_SMCCC_NAME_SIZE 40
>> +#define ARM_SMCCC_MODULE_PREFIX "arm_smccc:"
>> +
>> +/**
>> + * struct arm_smccc_device_id - Arm SMCCC bus device identifier
>> + * @name: SMCCC device name
>> + * @driver_data: driver data
>> + */
>> +struct arm_smccc_device_id {
>> +	char name[ARM_SMCCC_NAME_SIZE];
>> +	kernel_ulong_t driver_data;
>
> Can't find any users of the above driver_data ?
> Is it for future ? If so, can you add details on how it is supposed to be used
> if you don't want to drop it.
>

That would depend on the driver, wouldn’t it? I was comparing this with
struct auxiliary_device_id, and very few drivers use driver_data;
mei_gsc_probe() is one example.

I am not sure there is a generic rule here. IIUC, it provides a place
where a driver can add driver-specific data that can be used during the
probe routine.

>> +};
>> +
>>  /* Surface System Aggregator Module */
>>  
>>  #define SSAM_MATCH_TARGET	0x1
>> diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c
>> index b4178c42d08f..a485011ff137 100644
>> --- a/scripts/mod/devicetable-offsets.c
>> +++ b/scripts/mod/devicetable-offsets.c
>> @@ -254,6 +254,9 @@ int main(void)
>>  	DEVID(auxiliary_device_id);
>>  	DEVID_FIELD(auxiliary_device_id, name);
>>  
>> +	DEVID(arm_smccc_device_id);
>> +	DEVID_FIELD(arm_smccc_device_id, name);
>> +
>>  	DEVID(ssam_device_id);
>>  	DEVID_FIELD(ssam_device_id, match_flags);
>>  	DEVID_FIELD(ssam_device_id, domain);

-aneesh

^ permalink raw reply

* Re: [PATCH 09/15] PCI, device core: Move "untrusted" concept to DEVICE_TRUST_ADVERSARY
From: Jason Gunthorpe @ 2026-07-06 13:49 UTC (permalink / raw)
  To: Dan Williams
  Cc: linux-coco, linux-pci, driver-core, ankita, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich, Will Deacon, Robin Murphy,
	Bjorn Helgaas, Alexey Kardashevskiy, Xu Yilun, Aneesh Kumar K.V,
	Dexuan Cui
In-Reply-To: <20260705220819.2472765-10-djbw@kernel.org>

On Sun, Jul 05, 2026 at 03:08:13PM -0700, Dan Williams wrote:

> diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
> index a08523d348d8..a4233bdf9804 100644
> --- a/drivers/base/Kconfig
> +++ b/drivers/base/Kconfig
> @@ -291,6 +291,14 @@ config DEVICE_TRUST_NONE
>  	  possible, the device is blocked by an IOMMU from accessing
>  	  assets.
>  
> +config DEVICE_TRUST_ADVERSARY
> +	bool "Adversary"
> +	help
> +	  Device is allowed to bind. Bus, IOMMU, and driver layers may
> +	  react to this trust level by disabling access validation
> +	  bypass mechanisms like PCI ATS. When device is unbound from a
> +	  driver the device is blocked by an IOMMU where possible.
> +
>  config DEVICE_TRUST_AUTO
>  	bool "Auto"
>  	help
> @@ -317,6 +325,11 @@ config BUILTIN_DEVICE_TRUST_AUTO
>  	  a driver and deploy all available mechanisms to allow performant
>  	  direct memory access This trust level does not grant TCB privileges.
>  
> +config BUILTIN_DEVICE_TRUST_ADVERSARY
> +	bool "Adversary"
> +	help
> +	  Deploy mitigations in the IOMMU layer and driver to limit access.

I'm not sure it makes sense to have these options as compile time
defaults. Arguably the non-built in path should only be auto or none
and everything else should be handled by the initrd at runtime. If
necessary a sysfs for the initrd to set a kernel default might make
sense, IDK.

Ideally built in devices should not be any kind that is hooked up to
the iommu.

> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> index 9abaec0703ef..957ef77911a9 100644
> --- a/drivers/iommu/dma-iommu.c
> +++ b/drivers/iommu/dma-iommu.c
> @@ -588,16 +588,11 @@ static int iova_reserve_iommu_regions(struct device *dev,
>  	return ret;
>  }
>  
> -static bool dev_is_untrusted(struct device *dev)
> -{
> -	return dev_is_pci(dev) && to_pci_dev(dev)->untrusted;
> -}
> -
>  static bool dev_use_swiotlb(struct device *dev, size_t size,
>  			    enum dma_data_direction dir)
>  {
>  	return IS_ENABLED(CONFIG_SWIOTLB) &&
> -		(dev_is_untrusted(dev) ||
> +		(device_untrusted(dev) ||
>  		 dma_kmalloc_needs_bounce(dev, size, dir));
>  }

I think it would be nice to use the new 'adversarial' word here and in
other places instead of untrusted.

Jason

^ permalink raw reply

* Re: [PATCH 08/15] device core: Initial device trust infrastructure
From: Jason Gunthorpe @ 2026-07-06 13:45 UTC (permalink / raw)
  To: Dan Williams
  Cc: linux-coco, linux-pci, driver-core, ankita, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich, Luis Chamberlain, Petr Pavlu,
	Daniel Gomez, Sami Tolvanen, Aaron Tomlin, Bjorn Helgaas,
	Samuel Ortiz, Alexey Kardashevskiy, Xu Yilun, Aneesh Kumar K.V,
	Dexuan Cui
In-Reply-To: <20260705220819.2472765-9-djbw@kernel.org>

On Sun, Jul 05, 2026 at 03:08:12PM -0700, Dan Williams wrote:
> Introduce a per-device trust concept [1]. To start, it plumbs "auto" and
> "none" levels to enable a scheme of requiring explicit opt-in to operate a
> device. This supports a Confidential Computing use case of operating a
> limited / vetted device and driver set to bootstrap the environment. It
> leans on module policy to effect changes to device's trust level.
> 
> The motivations for using module policy to effect trust are:
> 
> 1/ It exists, no new ABI needed to inject "device firewall" policy into the
>    kernel [2].
> 2/ Trust is a function of how the device is operated. A driver can be
>    trusted to carefully operate an untrusted device.
> 
> It may prove too coarse for some situations, like a driver that talks to a
> mix of trusted and untrusted devices, or a module that contains multiple
> drivers, but it is a place to start.

I'm a little confused what this is is for?

Is this how we bootstrap enough drivers to get into the initrd? Ie
is it only relavent for key boot time things like CLOCK, IRQ and IOMMU
drivers?

Once we get to the initrd eveything should be driven on a per-device
basis without anything to do with modules. We already have cases for
mlx5 where it will have to drive trusted and untrusted devices in the
same system.

> Specifically this "module trust" scheme allows for only attaching drivers
> from built-in modules by default, or modules that explicitly pass a
> "trust=auto" option by building with CONFIG_DEVICE_TRUST_NONE=y. Modules
> can also be configured to skip device attach by default by setting
> /sys/module/module/parameters/require_trust.

So I'd imagine most systems would make all modules have
require_trust=1 and some userspace compoment in the initrd will
evaluate and assign trust to devices to trigger binding and module
loading?

> The new dev->p->trust level is the operational trust level of a device. It is
> only in effect after device_add(). It is private to the device core because
> the "tcb" level is not suitable to be changed while a driver is attached to
> the device. The result force_dma_unencrypted() must be stable while the
> device is attached to its driver. That core coordination responsibility is
> not suitable to export to non-core code.

I don't think force_dma_unencrypted() should be driven from the trust
level.. It is a property of the interconnect, the trust level sits on
top of that.

> +config DEVICE_TRUST
> +	bool "Device core support for specifying trust levels for devices"
> +	help
> +	  Enable support for generic device trust levels when building a kernel
> +	  that needs to operate in the presence of potentially adversarial
> +	  devices. This is selected by buses that want to operate devices with
> +	  reduced privileges, like externally connected devices, and it is a
> +	  pre-requisite for operating devices with access to private memory in a
> +	  Confidential Computing VM. See
> +	  Documentation/ABI/testing/sysfs-kernel-iommu_groups for the type of
> +	  IOMMU enforcement in effect.
> +
> +choice
> +	prompt "Default device trust"
> +	default DEVICE_TRUST_AUTO
> +	depends on DEVICE_TRUST
> +	help
> +	  Specify the device trust level at initial attachment. Any choice other
> +	  than "Auto" assumes auditing the built-in driver set for trusted
> +	  drivers, and an enlightened userspace modprobe policy for other
> +	  devices + drivers.
> +
> +config DEVICE_TRUST_NONE
> +	bool "None"
> +	help
> +	  Devices are disallowed from attaching to a driver, and where
> +	  possible, the device is blocked by an IOMMU from accessing
> +	  assets.

It is a confusing what devices this applies to? I guess it is all
devices that are not built in?

I think it should also be in a kernel commandline, we should be able
to support a single kernel image that works for CC VM and non CC VM, I
think. You'd want the CC VM to have a kernel command line to turn on
some of this stuff.

> +choice
> +	prompt "Trust devices with built-in drivers"
> +	default BUILTIN_DEVICE_TRUST_AUTO
> +	depends on DEVICE_TRUST
> +	help
> +	  Built-in drivers always bind to devices they match. Only
> +	  select a setting other than "Auto" when building a kernel image
> +	  targeted for use in Confidential Computing or other known
> +	  adversarial environments.
> +
> +config BUILTIN_DEVICE_TRUST_AUTO
> +	bool "Auto"
> +	help
> +	  Typical historical driver model, devices eagerly attempt to attach to
> +	  a driver and deploy all available mechanisms to allow performant
> +	  direct memory access This trust level does not grant TCB privileges.

So what's the other choice?

Is this "AUTO" or is it just all builtin devices?

I think it does grant TCB privileges in some cases, ie the VIOMMU
driver that binds is in the TCB and does have T=1 equivilent DMA
access.

What we want for a CC VM is a curated set of pre-initrd devices can
auto probe:
 - Drivers internally have some kind of attestation, eg on ARM the
   VIOMMU driver is going to have to ask the RMM to prove the VIOMMU is
   legitimate
 - Drivers that don't do any DMA at all, and have approved drivers
 - Drivers that are known to be hardened for untrusted DMA


Maybe these options should be named a bit differently?

config DEVICE_TRUST  <-- If the device trust framework is compiled in at all.

DEVICE_TRUST_BUILTIN_DEFAULT = ALL, AUTO
  Decide how pre-initrd devices are handled. ALL is current Linux,
  every built in driver binds and runs, AUTO follows some module policy?

DEVICE_TRUST_MODULE_DEFUALT = AUTO, NONE
  Decide how post-initrd devices are handled. AUTO is current Linux,
  follow the ACPI/etc. NONE means the initrd must run a policy agent
  to assign trust.

> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (C) 2026 NVIDIA Corporation & Affiliates */
> +#ifndef __DEVICE_TRUST_H__
> +#define __DEVICE_TRUST_H__
> +
> +/**
> + * enum device_trust - Level of restrictions and privileges for a
> + * device. Trust is initially assigned by the bus, and the bus is
> + * responsible for coordinating transitions between trust levels with
> + * DMA/IOMMU and its own device security mechanisms.
> + *
> + * @DEVICE_TRUST_UNSET: Unregistered device object with no current bus
> + * @DEVICE_TRUST_NONE: Blocked when idle, cannot bind
> + * @DEVICE_TRUST_AUTO: All typical privileges granted

I expect there would be two enums, the policy - eg from the sysfs/etc
which is possibly this, and then the actual in-effect trust level on
the actual device which should only be DISABLED/FULL/ADVERSARY ?

So maybe call this enum device_trust_policy ?

Jason

^ permalink raw reply

* Re: [PATCH v5 02/10] irqchip/gic-v3-its: Align shared ITS allocations to the CoCo shared granule size
From: Aneesh Kumar K.V @ 2026-07-06 13:42 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu,
	Catalin Marinas, Jason Gunthorpe, Marek Szyprowski, Robin Murphy,
	Steven Price, Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <86ik6sp7fc.wl-maz@kernel.org>

Marc Zyngier <maz@kernel.org> writes:

> On Mon, 06 Jul 2026 07:04:24 +0100,
> "Aneesh Kumar K.V (Arm)" <aneesh.kumar@kernel.org> wrote:
>> 
>> ITS tables allocated by the coco guest are shared with the hypervisor.
>> These allocations must satisfy the host shared-buffer granule size so that
>> the full converted range is safe for host access.
>> 
>> Allocate ITS pages using a size rounded up to the shared granule size and
>> use the same allocation order when encrypting, decrypting and freeing the
>> memory. Also grow the ITT cache in shared-granule sized chunks instead of
>> assuming PAGE_SIZE is sufficient.
>> 
>> Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
>> ---
>>  drivers/irqchip/irq-gic-v3-its.c | 17 +++++++++++------
>>  1 file changed, 11 insertions(+), 6 deletions(-)
>> 
>> diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
>> index b57d81ad33a0..5d6d38aa0dae 100644
>> --- a/drivers/irqchip/irq-gic-v3-its.c
>> +++ b/drivers/irqchip/irq-gic-v3-its.c
>> @@ -213,16 +213,17 @@ static gfp_t gfp_flags_quirk;
>>  static struct page *its_alloc_pages_node(int node, gfp_t gfp,
>>  					 unsigned int order)
>>  {
>> +	unsigned int alloc_order;
>>  	struct page *page;
>>  	int ret = 0;
>>  
>> -	page = alloc_pages_node(node, gfp | gfp_flags_quirk, order);
>> -
>> +	alloc_order = get_order(mem_cc_align_to_shared_granule(PAGE_SIZE << order));
>> +	page = alloc_pages_node(node, gfp | gfp_flags_quirk, alloc_order);
>>  	if (!page)
>>  		return NULL;
>>  
>>  	ret = set_memory_decrypted((unsigned long)page_address(page),
>> -				   1 << order);
>> +				   1 << alloc_order);
>>  	/*
>>  	 * If set_memory_decrypted() fails then we don't know what state the
>>  	 * page is in, so we can't free it. Instead we leak it.
>> @@ -241,13 +242,16 @@ static struct page *its_alloc_pages(gfp_t gfp, unsigned int order)
>>  
>>  static void its_free_pages(void *addr, unsigned int order)
>>  {
>> +	int alloc_order;
>> +
>> +	alloc_order = get_order(mem_cc_align_to_shared_granule(PAGE_SIZE << order));
>>  	/*
>>  	 * If the memory cannot be encrypted again then we must leak the pages.
>>  	 * set_memory_encrypted() will already have WARNed.
>>  	 */
>> -	if (set_memory_encrypted((unsigned long)addr, 1 << order))
>> +	if (set_memory_encrypted((unsigned long)addr, 1 << alloc_order))
>>  		return;
>> -	free_pages((unsigned long)addr, order);
>> +	free_pages((unsigned long)addr, alloc_order);
>
> The comments I had in [1] are still applicable.
>
> 	M.
>
> [1] https://lore.kernel.org/all/86zf2ozrb8.wl-maz@kernel.org/
>

I renamed it to alloc_order to make it clear that it refers to the
allocation order rather than the requested order. If you feel strongly
about not introducing another variable, I'll drop the new variable.

-aneesh

^ permalink raw reply

* Re: [PATCH v5 02/10] irqchip/gic-v3-its: Align shared ITS allocations to the CoCo shared granule size
From: Marc Zyngier @ 2026-07-06 13:35 UTC (permalink / raw)
  To: Aneesh Kumar K.V (Arm)
  Cc: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu,
	Catalin Marinas, Jason Gunthorpe, Marek Szyprowski, Robin Murphy,
	Steven Price, Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-3-aneesh.kumar@kernel.org>

On Mon, 06 Jul 2026 07:04:24 +0100,
"Aneesh Kumar K.V (Arm)" <aneesh.kumar@kernel.org> wrote:
> 
> ITS tables allocated by the coco guest are shared with the hypervisor.
> These allocations must satisfy the host shared-buffer granule size so that
> the full converted range is safe for host access.
> 
> Allocate ITS pages using a size rounded up to the shared granule size and
> use the same allocation order when encrypting, decrypting and freeing the
> memory. Also grow the ITT cache in shared-granule sized chunks instead of
> assuming PAGE_SIZE is sufficient.
> 
> Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
> ---
>  drivers/irqchip/irq-gic-v3-its.c | 17 +++++++++++------
>  1 file changed, 11 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
> index b57d81ad33a0..5d6d38aa0dae 100644
> --- a/drivers/irqchip/irq-gic-v3-its.c
> +++ b/drivers/irqchip/irq-gic-v3-its.c
> @@ -213,16 +213,17 @@ static gfp_t gfp_flags_quirk;
>  static struct page *its_alloc_pages_node(int node, gfp_t gfp,
>  					 unsigned int order)
>  {
> +	unsigned int alloc_order;
>  	struct page *page;
>  	int ret = 0;
>  
> -	page = alloc_pages_node(node, gfp | gfp_flags_quirk, order);
> -
> +	alloc_order = get_order(mem_cc_align_to_shared_granule(PAGE_SIZE << order));
> +	page = alloc_pages_node(node, gfp | gfp_flags_quirk, alloc_order);
>  	if (!page)
>  		return NULL;
>  
>  	ret = set_memory_decrypted((unsigned long)page_address(page),
> -				   1 << order);
> +				   1 << alloc_order);
>  	/*
>  	 * If set_memory_decrypted() fails then we don't know what state the
>  	 * page is in, so we can't free it. Instead we leak it.
> @@ -241,13 +242,16 @@ static struct page *its_alloc_pages(gfp_t gfp, unsigned int order)
>  
>  static void its_free_pages(void *addr, unsigned int order)
>  {
> +	int alloc_order;
> +
> +	alloc_order = get_order(mem_cc_align_to_shared_granule(PAGE_SIZE << order));
>  	/*
>  	 * If the memory cannot be encrypted again then we must leak the pages.
>  	 * set_memory_encrypted() will already have WARNed.
>  	 */
> -	if (set_memory_encrypted((unsigned long)addr, 1 << order))
> +	if (set_memory_encrypted((unsigned long)addr, 1 << alloc_order))
>  		return;
> -	free_pages((unsigned long)addr, order);
> +	free_pages((unsigned long)addr, alloc_order);

The comments I had in [1] are still applicable.

	M.

[1] https://lore.kernel.org/all/86zf2ozrb8.wl-maz@kernel.org/

-- 
Without deviation from the norm, progress is not possible.

^ permalink raw reply

* Re: [PATCH v6 04/11] x86/virt/tdx: Allocate ref counts for Dynamic PAMT memory
From: Chao Gao @ 2026-07-06 13:22 UTC (permalink / raw)
  To: Rick Edgecombe
  Cc: bp, dave.hansen, hpa, kas, kvm, linux-coco, linux-doc,
	linux-kernel, mingo, nik.borisov, pbonzini, seanjc, tglx,
	vannapurve, x86, yan.y.zhao, kai.huang, Kirill A. Shutemov
In-Reply-To: <20260526023515.288829-5-rick.p.edgecombe@intel.com>

>+/*
>+ * Allocate PAMT reference counters for all physical memory.
>+ *
>+ * It consumes 2MiB for every 1TiB of physical memory.
>+ */
>+static int init_pamt_refcounts(void)
>+{
>+	size_t size = DIV_ROUND_UP(max_pfn, PTRS_PER_PTE) * sizeof(*pamt_refcounts);
>+
>+	if (!tdx_supports_dynamic_pamt(&tdx_sysinfo))
>+		return 0;
>+
>+	pamt_refcounts = __vmalloc(size, GFP_KERNEL | __GFP_ZERO);
>+	if (!pamt_refcounts)
>+		return -ENOMEM;
>+
>+	return 0;
>+}
>+
>+static void free_pamt_refcounts(void)
>+{
>+	if (!tdx_supports_dynamic_pamt(&tdx_sysinfo))
>+		return;
>+
>+	vfree(pamt_refcounts);
>+	pamt_refcounts = NULL;
>+}

Both functions are only called during boot. They can be annotated with __init.

Reviewed-by: Chao Gao <chao.gao@intel.com>

^ permalink raw reply

* Re: [PATCH v7 1/6] firmware: smccc: Add an Arm SMCCC bus
From: Sudeep Holla @ 2026-07-06 13:18 UTC (permalink / raw)
  To: Aneesh Kumar K.V (Arm)
  Cc: linux-coco, linux-arm-kernel, linux-kernel, Sudeep Holla,
	Catalin Marinas, Greg KH, Jeremy Linton, Jonathan Cameron,
	Lorenzo Pieralisi, Mark Rutland, Will Deacon, Steven Price,
	Suzuki K Poulose, Andre Przywara
In-Reply-To: <20260611130429.295516-2-aneesh.kumar@kernel.org>

On Thu, Jun 11, 2026 at 06:34:24PM +0530, Aneesh Kumar K.V (Arm) wrote:
> SMCCC-discovered firmware services are currently represented by separate
> platform devices, such as smccc_trng and arm-cca-dev. Those devices do not
> represent independent DT/ACPI-described platform resources; they are
> features of the SMCCC firmware interface.
> 
> Add an Arm SMCCC bus for services discovered through the SMCCC firmware
> interface. The bus provides SMCCC device and driver registration helpers,
> name-based matching, modalias generation, and a sysfs modalias attribute so
> SMCCC service drivers can bind to discovered firmware services and autoload
> as modules.
> 
> Follow-up changes can then register SMCCC firmware services as arm-smccc
> devices instead of creating independent per-feature platform devices.
> 
> Based on arm_ffa code
> 
> Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
> ---
>  drivers/firmware/smccc/Makefile   |   2 +-
>  drivers/firmware/smccc/bus.c      | 164 ++++++++++++++++++++++++++++++
>  include/linux/arm-smccc-bus.h     |  49 +++++++++
>  include/linux/mod_devicetable.h   |  13 +++
>  scripts/mod/devicetable-offsets.c |   3 +
>  scripts/mod/file2alias.c          |   8 ++
>  6 files changed, 238 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/firmware/smccc/bus.c
>  create mode 100644 include/linux/arm-smccc-bus.h
> 
> diff --git a/drivers/firmware/smccc/Makefile b/drivers/firmware/smccc/Makefile
> index 40d19144a860..68bbff1407b8 100644
> --- a/drivers/firmware/smccc/Makefile
> +++ b/drivers/firmware/smccc/Makefile
> @@ -1,4 +1,4 @@
>  # SPDX-License-Identifier: GPL-2.0
>  #
> -obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY)	+= smccc.o kvm_guest.o
> +obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY)	+= bus.o smccc.o kvm_guest.o
>  obj-$(CONFIG_ARM_SMCCC_SOC_ID)	+= soc_id.o
> diff --git a/drivers/firmware/smccc/bus.c b/drivers/firmware/smccc/bus.c
> new file mode 100644
> index 000000000000..fe7e893130ce
> --- /dev/null
> +++ b/drivers/firmware/smccc/bus.c
> @@ -0,0 +1,164 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2026 Arm Limited
> + */
> +
> +#include <linux/arm-smccc-bus.h>
> +#include <linux/idr.h>
> +#include <linux/slab.h>
> +
> +static DEFINE_IDA(arm_smccc_bus_id);
> +
> +static int arm_smccc_bus_match(struct device *dev,
> +		const struct device_driver *drv)
> +{
> +	const struct arm_smccc_device_id *id_table;
> +	struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev);
> +
> +	id_table = to_arm_smccc_driver(drv)->id_table;
> +	if (!id_table)
> +		return 0;
> +

Any reason for not having this check when registering the driver also so
that it becomes a must to register ? I remember vaguely adding this as fix to
FF-A driver recently.

> +	while (id_table->name[0]) {
> +		if (!strcmp(smccc_dev->name, id_table->name))
> +			return 1;
> +		id_table++;
> +	}
> +
> +	return 0;
> +}
> +
> +static int arm_smccc_bus_probe(struct device *dev)
> +{
> +	struct arm_smccc_driver *smccc_drv = to_arm_smccc_driver(dev->driver);
> +
> +	return smccc_drv->probe(to_arm_smccc_device(dev));
> +}
> +
> +static void arm_smccc_bus_remove(struct device *dev)
> +{
> +	struct arm_smccc_driver *smcc_drv = to_arm_smccc_driver(dev->driver);
> +
> +	if (smcc_drv->remove)
> +		smcc_drv->remove(to_arm_smccc_device(dev));
> +}
> +
> +static int arm_smccc_bus_uevent(const struct device *dev,
> +		struct kobj_uevent_env *env)
> +{
> +	const struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev);
> +
> +	return add_uevent_var(env, "MODALIAS=" ARM_SMCCC_MODULE_PREFIX "%s",
> +			      smccc_dev->name);
> +}
> +
> +static ssize_t modalias_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev);
> +
> +	return sysfs_emit(buf, ARM_SMCCC_MODULE_PREFIX "%s\n", smccc_dev->name);
> +}
> +static DEVICE_ATTR_RO(modalias);
> +
> +static struct attribute *arm_smccc_device_attrs[] = {
> +	&dev_attr_modalias.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(arm_smccc_device);
> +
> +const struct bus_type arm_smccc_bus_type = {
> +	.name = "arm_smccc",
> +	.match = arm_smccc_bus_match,
> +	.probe = arm_smccc_bus_probe,
> +	.remove = arm_smccc_bus_remove,
> +	.uevent = arm_smccc_bus_uevent,
> +	.dev_groups = arm_smccc_device_groups,
> +};
> +EXPORT_SYMBOL_GPL(arm_smccc_bus_type);
> +
> +int arm_smccc_driver_register(struct arm_smccc_driver *driver,
> +		struct module *owner, const char *mod_name)
> +{
> +	if (!driver->probe)
> +		return -EINVAL;
> +
> +	driver->driver.bus = &arm_smccc_bus_type;
> +	driver->driver.name = driver->name;
> +	driver->driver.owner = owner;
> +	driver->driver.mod_name = mod_name;
> +
> +	return driver_register(&driver->driver);
> +}
> +EXPORT_SYMBOL_GPL(arm_smccc_driver_register);
> +
> +void arm_smccc_driver_unregister(struct arm_smccc_driver *driver)
> +{
> +	driver_unregister(&driver->driver);
> +}
> +EXPORT_SYMBOL_GPL(arm_smccc_driver_unregister);
> +
> +static void arm_smccc_release_device(struct device *dev)
> +{
> +	struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev);
> +
> +	ida_free(&arm_smccc_bus_id, smccc_dev->id);
> +	kfree(smccc_dev);
> +}
> +
> +struct arm_smccc_device *arm_smccc_device_register(const char *name)
> +{
> +	struct arm_smccc_device *smccc_dev;
> +	int id, ret;
> +
> +	id = ida_alloc_min(&arm_smccc_bus_id, 1, GFP_KERNEL);
> +	if (id < 0)
> +		return ERR_PTR(id);
> +
> +	smccc_dev = kzalloc_obj(*smccc_dev);
> +	if (!smccc_dev) {
> +		ida_free(&arm_smccc_bus_id, id);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	smccc_dev->id = id;
> +	if (strscpy(smccc_dev->name, name) < 0) {

Since this is exported symbol and one can call arm_smccc_device_register(ptr)
where ptr = NULL, won't it blow up then ? IIUC strscpy() handles NULL dst
but doesn't check for NULL src.

> +		kfree(smccc_dev);
> +		ida_free(&arm_smccc_bus_id, id);
> +		return ERR_PTR(-EINVAL);
> +	}
> +	smccc_dev->dev.bus = &arm_smccc_bus_type;
> +	smccc_dev->dev.release = arm_smccc_release_device;
> +
> +	ret = dev_set_name(&smccc_dev->dev, "%s-%d", smccc_dev->name, id);
> +	if (ret) {
> +		kfree(smccc_dev);
> +		ida_free(&arm_smccc_bus_id, id);
> +		return ERR_PTR(ret);
> +	}
> +
> +	ret = device_register(&smccc_dev->dev);
> +	if (ret) {
> +		put_device(&smccc_dev->dev);
> +		return ERR_PTR(ret);
> +	}
> +
> +	return smccc_dev;
> +}
> +EXPORT_SYMBOL_GPL(arm_smccc_device_register);
> +
> +void arm_smccc_device_unregister(struct arm_smccc_device *smccc_dev)
> +{
> +	if (!smccc_dev)
> +		return;
> +
> +	device_unregister(&smccc_dev->dev);
> +}
> +EXPORT_SYMBOL_GPL(arm_smccc_device_unregister);
> +
> +static int __init arm_smccc_bus_init(void)
> +{
> +	return bus_register(&arm_smccc_bus_type);
> +}
> +subsys_initcall(arm_smccc_bus_init);
> +
> diff --git a/include/linux/arm-smccc-bus.h b/include/linux/arm-smccc-bus.h
> new file mode 100644
> index 000000000000..188891441e57
> --- /dev/null
> +++ b/include/linux/arm-smccc-bus.h
> @@ -0,0 +1,49 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2026 Arm Limited
> + */
> +#ifndef __LINUX_ARM_SMCCC_BUS_H
> +#define __LINUX_ARM_SMCCC_BUS_H
> +
> +#include <linux/device.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +
> +struct arm_smccc_device {
> +	int id;
> +	char name[ARM_SMCCC_NAME_SIZE];
> +	struct device dev;
> +};
> +
> +#define to_arm_smccc_device(d) container_of(d, struct arm_smccc_device, dev)
> +
> +struct arm_smccc_driver {
> +	const char *name;
> +	int (*probe)(struct arm_smccc_device *sdev);
> +	void (*remove)(struct arm_smccc_device *sdev);
> +	const struct arm_smccc_device_id *id_table;
> +
> +	struct device_driver driver;
> +};
> +
> +#define to_arm_smccc_driver(d) \
> +	container_of_const(d, struct arm_smccc_driver, driver)
> +
> +int arm_smccc_driver_register(struct arm_smccc_driver *driver,
> +		struct module *owner, const char *mod_name);
> +void arm_smccc_driver_unregister(struct arm_smccc_driver *driver);
> +struct arm_smccc_device *arm_smccc_device_register(const char *name);
> +void arm_smccc_device_unregister(struct arm_smccc_device *smcc_dev);
> +

I may be overthinking but what will happen if HAVE_ARM_SMCCC_DISCOVERY=n
and some driver is compiled using this header ? It should be fine if it
fails to compile, just thinking out loud if we need to handle that are not.
As long as all the drivers using these depends on HAVE_ARM_SMCCC_DISCOVERY
it should be fine I think.

> +#define arm_smccc_register(driver) \
> +	arm_smccc_driver_register(driver, THIS_MODULE, KBUILD_MODNAME)
> +#define arm_smccc_unregister(driver) \
> +	arm_smccc_driver_unregister(driver)
> +
> +#define module_arm_smccc_driver(__arm_smccc_driver) \
> +	module_driver(__arm_smccc_driver, arm_smccc_register, \
> +		      arm_smccc_unregister)
> +
> +extern const struct bus_type arm_smccc_bus_type;
> +
> +#endif /* __LINUX_ARM_SMCCC_BUS_H */
> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
> index 23ff24080dfd..c9cee8c5a0b2 100644
> --- a/include/linux/mod_devicetable.h
> +++ b/include/linux/mod_devicetable.h

This file seems to be reworked recently, so you need to rebase it moving
smccc specific changes to separate file I think.

> @@ -876,6 +876,19 @@ struct auxiliary_device_id {
>  	kernel_ulong_t driver_data;
>  };
>  
> +#define ARM_SMCCC_NAME_SIZE 40
> +#define ARM_SMCCC_MODULE_PREFIX "arm_smccc:"
> +
> +/**
> + * struct arm_smccc_device_id - Arm SMCCC bus device identifier
> + * @name: SMCCC device name
> + * @driver_data: driver data
> + */
> +struct arm_smccc_device_id {
> +	char name[ARM_SMCCC_NAME_SIZE];
> +	kernel_ulong_t driver_data;

Can't find any users of the above driver_data ?
Is it for future ? If so, can you add details on how it is supposed to be used
if you don't want to drop it.

> +};
> +
>  /* Surface System Aggregator Module */
>  
>  #define SSAM_MATCH_TARGET	0x1
> diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c
> index b4178c42d08f..a485011ff137 100644
> --- a/scripts/mod/devicetable-offsets.c
> +++ b/scripts/mod/devicetable-offsets.c
> @@ -254,6 +254,9 @@ int main(void)
>  	DEVID(auxiliary_device_id);
>  	DEVID_FIELD(auxiliary_device_id, name);
>  
> +	DEVID(arm_smccc_device_id);
> +	DEVID_FIELD(arm_smccc_device_id, name);
> +
>  	DEVID(ssam_device_id);
>  	DEVID_FIELD(ssam_device_id, match_flags);
>  	DEVID_FIELD(ssam_device_id, domain);
> diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
> index 2ad87a74bb03..92d3917f27cc 100644
> --- a/scripts/mod/file2alias.c
> +++ b/scripts/mod/file2alias.c
> @@ -1323,6 +1323,13 @@ static void do_auxiliary_entry(struct module *mod, void *symval)
>  	module_alias_printf(mod, false, AUXILIARY_MODULE_PREFIX "%s", *name);
>  }
>  
> +static void do_arm_smccc_entry(struct module *mod, void *symval)
> +{
> +	DEF_FIELD_ADDR(symval, arm_smccc_device_id, name);
> +
> +	module_alias_printf(mod, false, ARM_SMCCC_MODULE_PREFIX "%s", *name);
> +}
> +
>  /*
>   * Looks like: ssam:dNcNtNiNfN
>   *
> @@ -1493,6 +1500,7 @@ static const struct devtable devtable[] = {
>  	{"mhi", SIZE_mhi_device_id, do_mhi_entry},
>  	{"mhi_ep", SIZE_mhi_device_id, do_mhi_ep_entry},
>  	{"auxiliary", SIZE_auxiliary_device_id, do_auxiliary_entry},
> +	{"arm_smccc", SIZE_arm_smccc_device_id, do_arm_smccc_entry},
>  	{"ssam", SIZE_ssam_device_id, do_ssam_entry},
>  	{"dfl", SIZE_dfl_device_id, do_dfl_entry},
>  	{"ishtp", SIZE_ishtp_device_id, do_ishtp_entry},
> -- 
> 2.43.0
> 

-- 
Regards,
Sudeep

^ permalink raw reply

* Re: [PATCH 00/15] Device Evidence and Trust for PCI Security Protocol (TDISP)
From: Jason Gunthorpe @ 2026-07-06 12:51 UTC (permalink / raw)
  To: Dan Williams
  Cc: linux-coco, linux-pci, driver-core, ankita, Aaron Tomlin,
	Alexey Kardashevskiy, Alistair Francis, Aneesh Kumar K.V,
	Arnd Bergmann, Bjorn Helgaas, Daniel Gomez, Danilo Krummrich,
	Dexuan Cui, Donald Hunter, Greg Kroah-Hartman, Jakub Kicinski,
	Luis Chamberlain, Lukas Wunner, Petr Pavlu, Rafael J. Wysocki,
	Robin Murphy, Sami Tolvanen, Samuel Ortiz, Saravana Kannan,
	Will Deacon, Xu Yilun
In-Reply-To: <20260705220819.2472765-1-djbw@kernel.org>

On Sun, Jul 05, 2026 at 03:08:04PM -0700, Dan Williams wrote:
> * NONE: no usage of the device unless the trust is explicitly overridden
>   by user policy specified via a driver flag, module flag, or uapi (TBD).
> 
> * ADVERSARY: needs acknowledgement from the bus and IOMMU / DMA layers
>   that the device is limited to strict IOMMU translation behavior. Drivers
>   can use this as a signal to limit functionality. This designation
>   implies follow-on IOMMU and bus enabling work for features like
>   arranging for the device to attach to a blocked IOMMU domain when
>   detached from a driver.
> 
> * AUTO: typical / historical Linux driver model.
> 
> * TCB: a trust level that only exists in Confidential Computing
>   environments. When acked by the IOMMU / DMA layer it enables the device
>   to issue direct-DMA to private/encrypted addresses or otherwise attach to
>   a secure vIOMMU within the TCB.

I'm not sure I entirely like this one, certainly it needs to be
possible to have both T=1 and ADVERSARY together.

Arguably the T=0/1 decision is much more like link encryption, it
effects the transport of the DMA into the CPU. That it also impacts
how the VIOMMU works is the only thing that make it sort of
trust-like in this model.

I'd also argue this list is missing "FULL" trust, which is the
historical Linux behavior for a normal device. AUTO should be
selecting between FULL/ADVERSARY based on things like the ACPI/etc as
it does today.

In a CC VM if we have a T=0 device we probably want to operate it with
ADVERSARY (there is no T=0 VIOMMU so this is equivilent to FULL)

For a T=1 device we need to have the choice of FULL or ADVERSARY. When
a VIOMMU is present ADVERSARY will further restrict the T=1 traffic as
a defense in depth.

So, it is really necessary to have "TCB" here?

If the trust level is reduced to just be a command to the kernel how
it should operate the device then it would be up to userspace to
confirm things like T=1 before setting the trust. The kernel would
have to set the trust to NONE when security sensitive changes are
detected.

If we still want a kernel-side policy gate, eg kernel will not
progress unless T=1 is met, then I think that would be better as an
independent pair'd policy field, ie
   trust=FULL when policy=T=1, LINK_ENCRYPTION=1, etc

As bundling the two into "TCB" is probably going to turn problematic
as we have more and more combinations of policy conditions.

Jason

^ permalink raw reply

* Re: [PATCH 13/15] PCI, device core: Add private memory access for DEVICE_TRUST_TCB
From: Aneesh Kumar K.V @ 2026-07-06 12:42 UTC (permalink / raw)
  To: Dan Williams, linux-coco
  Cc: linux-pci, driver-core, ankita, Greg Kroah-Hartman,
	Rafael J. Wysocki, Danilo Krummrich, Bjorn Helgaas, Dexuan Cui
In-Reply-To: <20260705220819.2472765-14-djbw@kernel.org>

Dan Williams <djbw@kernel.org> writes:

> A device that wants to access private memory needs to have its trust
> elevated to DEVICE_TRUST_TCB. That trust is established either at compile
> time (unlikely), the bus knows the device is within the TCB to start (some
> paravisor setups), or the device is dynamically added to the TCB in
> coordination with a TSM driver (primary TDISP use case) and the trust is
> elevated by driver match.
>


Do we have the last case, where the device is dynamically added to the
TCB in coordination with a TSM, implemented in this series? Do we expect
the CCA driver to set that up?

>
> When a PCI device is associated with a TSM for security services the low
> level TSM driver in the CC VM has the opportunity validate DMA access.
> That validation happens at ->dma_configure() time when the device attaches
> to a driver. The TSM driver is responsible for proving to the platform TSM
> that the VM is enabling DMA with respect to the most recently generated
> evidence. If that fails, driver attach fails.
>
> When a PCI device is not associated with a TSM provider for security
> services, but the device is trusted there are 3 options.
>
> 1/ Arch requires all DMA enable events to be acked by TSM driver
>
> 2/ Arch does not require, but admin policy is responsible for knowing which
>    devices need TSM coordination to become active within the TCB.
>
> 3/ Device is approved by a paravisor to operate within the TCB, no TSM
>    coordination required.
>
> In cases 2 and 3 if the device needed TSM driver coordination, but the TSM
> driver or association to the device is missing, it triggers hardware
> errors. Those errors are a configuration error that the kernel does not
> actively prevent.
>
> Architectures still need to fixup force_dma_unencrypted() to call
> device_tcb_trusted() to tell the DMA layer that TCB access is granted.
>

-aneesh

^ permalink raw reply

* Re: [PATCH v6 03/11] x86/virt/tdx: Add tdx_alloc/free_control_page() helpers
From: Chao Gao @ 2026-07-06 12:31 UTC (permalink / raw)
  To: Rick Edgecombe
  Cc: bp, dave.hansen, hpa, kas, kvm, linux-coco, linux-doc,
	linux-kernel, mingo, nik.borisov, pbonzini, seanjc, tglx,
	vannapurve, x86, yan.y.zhao, kai.huang, Kirill A. Shutemov
In-Reply-To: <20260526023515.288829-4-rick.p.edgecombe@intel.com>

On Mon, May 25, 2026 at 07:35:07PM -0700, Rick Edgecombe wrote:
>From: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com>
>
>Add helpers to use when allocating or preparing pages that are handed to
>the TDX-Module for use as control/S-EPT pages, and thus need Dynamic PAMT

For the whole series: s/TDX-Module/TDX module, to match the existing
convention.

>+/*
>+ * Calculate the arg needed for operating on the DPAMT backing for
>+ * a given 4KB page.
>+ */
>+static u64 pamt_2mb_arg(kvm_pfn_t pfn)
>+{
>+	unsigned long hpa_2mb = ALIGN_DOWN(pfn << PAGE_SHIFT, PMD_SIZE);

The changelog and the comment don't explain why the pfn is aligned down to a
2MB boundary. And ...

>+
>+	return hpa_2mb | TDX_PS_2M;
>+}
>+
>+/* Add PAMT backing for the given page. */

... this adds PAMT backing for the whole 2MB region containing the given page,
not just the given page itself. Is it worth clarifying in the comment?

<snip>

>+/*
>+ * Return a page that can be gifted to the TDX-Module for use as a "control"
>+ * page, i.e. pages that are used for control and S-EPT structures for a given
>+ * TDX guest, and bound to said guest's HKID and thus obtain TDX protections,
>+ * including PAMT tracking.
>+ */

IIUC, S-EPT structures are not allocated through this helper. Why mention it?

>+struct page *tdx_alloc_control_page(void)
>+{
>+	struct page *page;
>+
>+	page = alloc_page(GFP_KERNEL_ACCOUNT);
>+	if (!page)
>+		return NULL;
>+
>+	if (tdx_pamt_get(page_to_pfn(page))) {
>+		__free_page(page);
>+		return NULL;
>+	}
>+
>+	return page;
>+}
>+EXPORT_SYMBOL_FOR_KVM(tdx_alloc_control_page);
>+
>+/*
>+ * Free a page that was gifted to the TDX-Module for use as a control/S-EPT
>+ * page. After this, the page is no longer protected by TDX.
>+ */

Ditto.

>+void tdx_free_control_page(struct page *page)
>+{
>+	if (!page)
>+		return;
>+
>+	tdx_pamt_put(page_to_pfn(page));
>+	__free_page(page);
>+}
>+EXPORT_SYMBOL_FOR_KVM(tdx_free_control_page);

With above nits fixed,

Reviewed-by: Chao Gao <chao.gao@intel.com>

^ permalink raw reply

* Re: [PATCH v10 3/6] x86/sev: Disable CPU hotplug while SNP is active
From: Jethro Beekman @ 2026-07-06 12:02 UTC (permalink / raw)
  To: Kalra, Ashish, tglx, mingo, bp, dave.hansen, x86, hpa, seanjc,
	peterz, thomas.lendacky, herbert, davem, ardb
  Cc: pbonzini, aik, Michael.Roth, KPrateek.Nayak, Tycho.Andersen,
	Nathan.Fontenot, ackerleytng, jackyli, pgonda, rientjes, jacobhxu,
	xin, pawan.kumar.gupta, babu.moger, dyoung, nikunj, john.allen,
	darwi, linux-kernel, linux-crypto, kvm, linux-coco
In-Reply-To: <5147d9bd-42f8-4ceb-aca4-6ac5fd5cb7f0@amd.com>

[-- Attachment #1: Type: text/plain, Size: 1770 bytes --]

On 2026-07-01 23:25, Kalra, Ashish wrote:
> 
> On 7/1/2026 4:40 AM, Jethro Beekman wrote:
>> Hi Ashish,
>>
>> I don't believe my concern has been addressed
>>
>> https://lore.kernel.org/lkml/0df3b665-3a9c-4c46-a7aa-14388e8e1577@fortanix.com/
>>
>> --
> 
> The disable tracks SNP_INIT, not "SNP" in general: SNP_INIT requires SnpEn to be set on all present CPUs, and a CPU brought online afterward wouldn't have it, so the kernel that runs SNP_INIT must keep its CPU set stable. Today the only kernel that runs SNP_INIT is the bare-metal host, so a plain L1 guest keeps full CPU hotplug.
> 
> Concretely, the path is gated by CC_ATTR_HOST_SEV_SNP, which bsp_determine_snp() sets only when X86_FEATURE_HYPERVISOR is clear and clears otherwise 
> (as Prateek pointed out). So a Linux L1 guest never has it set, never reaches snp_prepare()/snp_rmptable_init(), and keeps CPU hotplug — 
> including while running SEV/SEV-ES confidential L2 guests. Only SNP initialization disables hotplug; the other SEV variants don't. And KVM doesn't expose
> SNP to L1, so an L1 can't be an SNP host today in any case.
>   
> On the nested scenario you raised: if SNP-guest-as-L2 support is added, an L1 acting as an SNP host would run a *virtualized* SNP_INIT. A faithful virtualization carries the same constraint as physical SNP_INIT — all present (v)CPUs must be SnpEn — so that L1 would have the same (v)CPU-hotplug-disable requirement, just over its virtual CPUs, and this same code would apply at that level. So the disable isn't too broad; it correctly tracks SNP_INIT. It simply doesn't apply to a plain L1 guest today, because such a guest isn't running SNP_INIT.

Thanks, Ashish, Prateek, for the clarification.

--
Jethro Beekman | CTO | Fortanix

[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4839 bytes --]

^ permalink raw reply

* Re: [PATCH 1/2] x86/tdx: Add helper to query maximum TD Quote size
From: Peter Fang @ 2026-07-06  9:18 UTC (permalink / raw)
  To: Xiaoyao Li
  Cc: Dave Hansen, Kiryl Shutsemau, Rick Edgecombe,
	Kuppuswamy Sathyanarayanan, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, x86, H. Peter Anvin, linux-kernel, linux-coco,
	kvm
In-Reply-To: <cdb43316-6535-4f11-9fb0-abdf2adf4568@intel.com>

On Thu, Jul 02, 2026 at 06:16:21PM +0800, Xiaoyao Li wrote:
> > > 
> > > I have another nit other than Kiryl's
> > > 
> > > > +u32 tdx_get_max_quote_size(void)
> > > > +{
> > > > +	u64 val, ret;
> > > > +
> > > > +	ret = tdg_vm_rd(TDCS_QUOTE_MAX_SIZE, &val);
> > > > +
> > > > +	return ret ? 0 : (u32)val;
> > > > +}
> > > > +EXPORT_SYMBOL_GPL(tdx_get_max_quote_size);
> > > 
> > > Do we need to start to use
> > > 
> > > EXPORT_SYMBOL_FOR_MODULES(tdx_get_max_quote_size, "tdx-guest") ?
> > > 
> > 
> > This makes sense. But can we use a follow-up patch to improve this file
> > later? Right now there are only EXPORT_SYMBOL_GPL() usages, so using
> > EXPORT_SYMBOL_FOR_MODULES() here might look inconsistent.
> 
> If maintainers is going to merge the series as-is, then fine.
> 
> If we will have a v2, I don't see why cannot update to use
> EXPORT_SYMBOL_FOR_MODULES().
> 
> The existing ones can be updated in a separate patch before or after.

Thanks. I will respin a v2 to address some review feedback. But I'd like
to hold off on this improvement unless there's a strong push to do it
right now. I'm going to add this to my list of guest-side cleanup items
for after this series, along with the feedback I got in [1].

[1] https://lore.kernel.org/linux-coco/5f9474ed-bacb-44d5-a0fc-5a29a1e79b60@intel.com/

^ permalink raw reply

* Re: [PATCH v2 20/31] x86/virt/tdx: Add a helper to loop on TDX_INTERRUPTED_RESUMABLE
From: Xu Yilun @ 2026-07-06  9:16 UTC (permalink / raw)
  To: Ackerley Tng
  Cc: linux-coco, linux-pci, dan.j.williams, x86, chao.gao, dave.jiang,
	baolu.lu, yilun.xu, zhenzhong.duan, kvm, rick.p.edgecombe,
	dave.hansen, kas, xiaoyao.li, vishal.l.verma, linux-kernel
In-Reply-To: <CAEvNRgGgTkOPWELeg5U3pjjw9NZOBQ5zswWy=MRxOXsUXXuFhQ@mail.gmail.com>

> > +static u64 __maybe_unused __seamcall_ir_resched(sc_func_t sc_func, u64 fn,
> > +						struct tdx_module_args *args)
> > +{
> > +	struct tdx_module_args _args;
> > +	u64 r;
> > +
> > +	while (1) {
> > +		_args = *(args);
> 
> Is this copying meant to ensure that every retry has the same input
> args? Perhaps a comment should be added so it's obvious that every retry
> will have the same input, and outputs from each retry are dropped.

Yes, that's the intent.

But now I've found this is not a good convention for INTERRUPTED_RESUMABLE.
If outputs are necessary why we drop them, if outputs are not useful why
we output them to overwrite inputs. The fact is that TDX module only
outputs all 0's to zero out the inputs, that makes no sense.

So we now switch to another existing convention - if there is valid
output, use the output to update the input for next loop, or keep the input
unchanged. This way we only need to:

	do {
		r = seamcall(TDH_XX_XX, &args);
	} while (r == TDX_INTERRUPTED_RESUMABLE);

	if (r != TDX_SUCCESS)
		return -EFAULT;

https://lore.kernel.org/kvm/20260618081355.3253581-6-yilun.xu@linux.intel.com/
https://lore.kernel.org/kvm/20260618081355.3253581-5-yilun.xu@linux.intel.com/

> 
> > +		r = sc_retry(sc_func, fn, &_args);
> > +		if (r != TDX_INTERRUPTED_RESUMABLE)
> > +			break;
> > +
> > +		cond_resched();
> > +	}
> > +
> > +	*args = _args;
> > +
> > +	return r;
> > +}

^ permalink raw reply

* [PATCH v5 10/10] arm64: realm: Expose the CCA shared granule size through mem_encrypt ops
From: Aneesh Kumar K.V (Arm) @ 2026-07-06  6:04 UTC (permalink / raw)
  To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
  Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
	Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
	Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-1-aneesh.kumar@kernel.org>

CCA guests must align shared/private memory transitions to the size
reported by the host for IPA state changes. This size can be larger than
PAGE_SIZE, so allowing a guest to convert only a PAGE_SIZE-sized subrange
can leave the remaining part of that host-sized range in the wrong state.

Cache the RHI-reported IPA sate change alignment during Realm
initialization and expose it through a new arm64 mem_encrypt callback. Use
PAGE_SIZE as the default shared granule size when no backend callback is
registered.

Validate both the address and byte size passed to set_memory_encrypted()
and set_memory_decrypted() against mem_cc_shared_granule_size() before
calling into the backend. This prevents callers from converting only part
of a host-managed page.

Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
 arch/arm64/include/asm/mem_encrypt.h |  4 ++++
 arch/arm64/kernel/rsi.c              | 13 ++++++++++-
 arch/arm64/mm/mem_encrypt.c          | 32 ++++++++++++++++++++++++----
 3 files changed, 44 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/include/asm/mem_encrypt.h b/arch/arm64/include/asm/mem_encrypt.h
index f6325f30e844..5018581fed87 100644
--- a/arch/arm64/include/asm/mem_encrypt.h
+++ b/arch/arm64/include/asm/mem_encrypt.h
@@ -9,6 +9,7 @@ struct device;
 struct arm64_mem_crypt_ops {
 	int (*encrypt)(unsigned long addr, int numpages);
 	int (*decrypt)(unsigned long addr, int numpages);
+	size_t (*cc_shared_granule_size)(void);
 };
 
 int arm64_mem_crypt_ops_register(const struct arm64_mem_crypt_ops *ops);
@@ -17,6 +18,9 @@ int set_memory_encrypted(unsigned long addr, int numpages);
 int set_memory_decrypted(unsigned long addr, int numpages);
 int __set_memory_enc_dec(unsigned long addr, int numpages, bool encrypt);
 
+#define mem_cc_shared_granule_size mem_cc_shared_granule_size
+size_t mem_cc_shared_granule_size(void);
+
 static inline bool force_dma_unencrypted(struct device *dev)
 {
 	return is_realm_world();
diff --git a/arch/arm64/kernel/rsi.c b/arch/arm64/kernel/rsi.c
index f52f240bb44c..54fb929067c9 100644
--- a/arch/arm64/kernel/rsi.c
+++ b/arch/arm64/kernel/rsi.c
@@ -20,6 +20,7 @@
 
 static struct realm_config config;
 static struct kobject *cca_kobj;
+static unsigned long ipa_state_change_granule_size;
 
 unsigned long prot_ns_shared;
 EXPORT_SYMBOL(prot_ns_shared);
@@ -168,9 +169,17 @@ static int realm_set_memory_decrypted(unsigned long addr, int numpages)
 	return ret;
 }
 
+static size_t realm_cc_shared_granule_size(void)
+{
+	if (is_realm_world())
+		return ipa_state_change_granule_size;
+	return PAGE_SIZE;
+}
+
 static const struct arm64_mem_crypt_ops realm_crypt_ops = {
 	.encrypt = realm_set_memory_encrypted,
 	.decrypt = realm_set_memory_decrypted,
+	.cc_shared_granule_size = realm_cc_shared_granule_size,
 };
 
 static int realm_register_memory_enc_ops(void)
@@ -180,7 +189,7 @@ static int realm_register_memory_enc_ops(void)
 
 /* we need an aligned struct for rsi_host_call. slab is not yet ready */
 static struct rsi_host_call hostconf_call __initdata;
-static unsigned long __maybe_unused __init get_ipa_state_change_alignment(void)
+static unsigned long __init get_ipa_state_change_alignment(void)
 {
 	long ret;
 	unsigned long shared_granule_size;
@@ -230,6 +239,8 @@ void __init arm64_rsi_init(void)
 		return;
 	if (WARN_ON(rsi_get_realm_config(lm_alias(&config))))
 		return;
+
+	ipa_state_change_granule_size = get_ipa_state_change_alignment();
 	prot_ns_shared = __phys_to_pte_val(BIT(config.ipa_bits - 1));
 
 	if (arm64_ioremap_prot_hook_register(realm_ioremap_hook))
diff --git a/arch/arm64/mm/mem_encrypt.c b/arch/arm64/mm/mem_encrypt.c
index ee3c0ab04384..69783c6a3c08 100644
--- a/arch/arm64/mm/mem_encrypt.c
+++ b/arch/arm64/mm/mem_encrypt.c
@@ -17,8 +17,7 @@
 #include <linux/compiler.h>
 #include <linux/err.h>
 #include <linux/mm.h>
-
-#include <asm/mem_encrypt.h>
+#include <linux/mem_encrypt.h>
 
 static const struct arm64_mem_crypt_ops *crypt_ops;
 
@@ -33,18 +32,43 @@ int arm64_mem_crypt_ops_register(const struct arm64_mem_crypt_ops *ops)
 
 int set_memory_encrypted(unsigned long addr, int numpages)
 {
-	if (likely(!crypt_ops) || WARN_ON(!PAGE_ALIGNED(addr)))
+	unsigned long size = (unsigned long)numpages << PAGE_SHIFT;
+
+	if (likely(!crypt_ops))
 		return 0;
 
+	if (WARN_ON(!IS_ALIGNED(addr, mem_cc_shared_granule_size())))
+		return -EINVAL;
+
+	if (WARN_ON(!IS_ALIGNED(size, mem_cc_shared_granule_size())))
+		return -EINVAL;
+
 	return crypt_ops->encrypt(addr, numpages);
 }
 EXPORT_SYMBOL_GPL(set_memory_encrypted);
 
 int set_memory_decrypted(unsigned long addr, int numpages)
 {
-	if (likely(!crypt_ops) || WARN_ON(!PAGE_ALIGNED(addr)))
+	unsigned long size = (unsigned long)numpages << PAGE_SHIFT;
+
+	if (likely(!crypt_ops))
 		return 0;
 
+	if (WARN_ON(!IS_ALIGNED(addr, mem_cc_shared_granule_size())))
+		return -EINVAL;
+
+	if (WARN_ON(!IS_ALIGNED(size, mem_cc_shared_granule_size())))
+		return -EINVAL;
+
 	return crypt_ops->decrypt(addr, numpages);
 }
 EXPORT_SYMBOL_GPL(set_memory_decrypted);
+
+size_t mem_cc_shared_granule_size(void)
+{
+	if (likely(!crypt_ops) || !crypt_ops->cc_shared_granule_size)
+		return PAGE_SIZE;
+
+	return crypt_ops->cc_shared_granule_size();
+}
+EXPORT_SYMBOL_GPL(mem_cc_shared_granule_size);
-- 
2.43.0


^ permalink raw reply related

* [PATCH v5 09/10] arm64: realm: Add RHI helper to query IPA state change alignment
From: Aneesh Kumar K.V (Arm) @ 2026-07-06  6:04 UTC (permalink / raw)
  To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
  Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
	Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
	Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-1-aneesh.kumar@kernel.org>

Arm CCA guests need to know the granularity at which the host expects IPA
state changes to be performed. This can be larger than the guest page size
and is needed when deciding the alignment for memory shared with the host.

Add the Realm Host Interface host configuration definitions and an
get_ipa_state_change_alignment() helper. The helper uses RSI_HOST_CALL to
query the supported HostConf version and features, reads the IPA change
alignment when available, and falls back to PAGE_SIZE if the interface is
unavailable or returns an invalid value.

Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
 arch/arm64/include/asm/rsi_cmds.h | 10 +++++++
 arch/arm64/kernel/rsi.c           | 45 +++++++++++++++++++++++++++++++
 include/linux/arm-smccc-rhi.h     | 25 +++++++++++++++++
 include/linux/arm-smccc-rsi.h     |  7 +++++
 4 files changed, 87 insertions(+)
 create mode 100644 include/linux/arm-smccc-rhi.h

diff --git a/arch/arm64/include/asm/rsi_cmds.h b/arch/arm64/include/asm/rsi_cmds.h
index 8537d0fd3da6..c890e2334a15 100644
--- a/arch/arm64/include/asm/rsi_cmds.h
+++ b/arch/arm64/include/asm/rsi_cmds.h
@@ -86,4 +86,14 @@ static inline long rsi_set_addr_range_state(phys_addr_t start,
 	return res.a0;
 }
 
+static inline unsigned long rsi_host_call(struct rsi_host_call *rhi_call)
+{
+	phys_addr_t addr = virt_to_phys(rhi_call);
+	struct arm_smccc_res res;
+
+	arm_smccc_1_1_invoke(SMC_RSI_HOST_CALL, addr, &res);
+
+	return res.a0;
+}
+
 #endif /* __ASM_RSI_CMDS_H */
diff --git a/arch/arm64/kernel/rsi.c b/arch/arm64/kernel/rsi.c
index 5c566700974c..f52f240bb44c 100644
--- a/arch/arm64/kernel/rsi.c
+++ b/arch/arm64/kernel/rsi.c
@@ -11,6 +11,7 @@
 #include <linux/platform_device.h>
 #include <linux/kobject.h>
 #include <linux/sysfs.h>
+#include <linux/arm-smccc-rhi.h>
 
 #include <asm/io.h>
 #include <asm/mem_encrypt.h>
@@ -177,6 +178,50 @@ static int realm_register_memory_enc_ops(void)
 	return arm64_mem_crypt_ops_register(&realm_crypt_ops);
 }
 
+/* we need an aligned struct for rsi_host_call. slab is not yet ready */
+static struct rsi_host_call hostconf_call __initdata;
+static unsigned long __maybe_unused __init get_ipa_state_change_alignment(void)
+{
+	long ret;
+	unsigned long shared_granule_size;
+
+	hostconf_call.imm = 0;
+	hostconf_call.gprs[0] = RHI_HOSTCONF_VERSION;
+	ret = rsi_host_call(lm_alias(&hostconf_call));
+	if (ret != RSI_SUCCESS)
+		goto err_out;
+
+	if (hostconf_call.gprs[0] != RHI_HOSTCONF_VER_1_0)
+		goto err_out;
+
+	hostconf_call.imm = 0;
+	hostconf_call.gprs[0] = RHI_HOSTCONF_FEATURES;
+	ret = rsi_host_call(lm_alias(&hostconf_call));
+	if (ret != RSI_SUCCESS)
+		goto err_out;
+
+	if (!(hostconf_call.gprs[0] & __RHI_HOSTCONF_GET_IPA_CHANGE_ALIGNMENT))
+		goto err_out;
+
+	hostconf_call.imm = 0;
+	hostconf_call.gprs[0] = RHI_HOSTCONF_GET_IPA_CHANGE_ALIGNMENT;
+	ret = rsi_host_call(lm_alias(&hostconf_call));
+	if (ret != RSI_SUCCESS)
+		goto err_out;
+
+	shared_granule_size = hostconf_call.gprs[0];
+	if (shared_granule_size & (SZ_4K - 1))
+		goto err_out;
+
+	return max(PAGE_SIZE, shared_granule_size);
+err_out:
+	/*
+	 * For failure condition assume host is built with 4K page size
+	 * and hence IPA state change alignment can be guest PAGE_SIZE.
+	 */
+	return PAGE_SIZE;
+}
+
 void __init arm64_rsi_init(void)
 {
 	if (arm_smccc_1_1_get_conduit() != SMCCC_CONDUIT_SMC)
diff --git a/include/linux/arm-smccc-rhi.h b/include/linux/arm-smccc-rhi.h
new file mode 100644
index 000000000000..91a29996d72d
--- /dev/null
+++ b/include/linux/arm-smccc-rhi.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2026 ARM Ltd.
+ */
+
+#ifndef __LINUX_ARM_SMCCC_RHI_H_
+#define __LINUX_ARM_SMCCC_RHI_H_
+
+#include <linux/arm-smccc.h>
+
+#define SMC_RHI_CALL(func)				\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,		\
+			   ARM_SMCCC_SMC_64,		\
+			   ARM_SMCCC_OWNER_STANDARD_HYP,\
+			   (func))
+
+#define RHI_HOSTCONF_VER_1_0		0x10000
+#define RHI_HOSTCONF_VERSION		SMC_RHI_CALL(0x004E)
+
+#define __RHI_HOSTCONF_GET_IPA_CHANGE_ALIGNMENT BIT(0)
+#define RHI_HOSTCONF_FEATURES		SMC_RHI_CALL(0x004F)
+
+#define RHI_HOSTCONF_GET_IPA_CHANGE_ALIGNMENT	SMC_RHI_CALL(0x0050)
+
+#endif /* __LINUX_ARM_SMCCC_RHI_H_ */
diff --git a/include/linux/arm-smccc-rsi.h b/include/linux/arm-smccc-rsi.h
index ae663aa8fd7f..e33487ab000c 100644
--- a/include/linux/arm-smccc-rsi.h
+++ b/include/linux/arm-smccc-rsi.h
@@ -184,6 +184,13 @@ struct realm_config {
  */
 #define SMC_RSI_IPA_STATE_GET			SMC_RSI_FID(0x198)
 
+struct rsi_host_call {
+	union {
+		u16 imm;
+		u64 padding0;
+	};
+	u64 gprs[31];
+} __aligned(0x100);
 /*
  * Make a Host call.
  *
-- 
2.43.0


^ permalink raw reply related

* [PATCH v5 08/10] arm64: realm: Move Realm memory encryption ops to RSI code
From: Aneesh Kumar K.V (Arm) @ 2026-07-06  6:04 UTC (permalink / raw)
  To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
  Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
	Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
	Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-1-aneesh.kumar@kernel.org>

Realm memory encryption callbacks are CCA-specific. Keep the Realm callback
registration with the RSI initialization code instead of pageattr.c, which
only needs to provide the low-level page-attribute transition helper.

Export __set_memory_enc_dec() within arm64 so the RSI code can wrap it with
the Realm-specific encrypt/decrypt callbacks and warning policy.

No functional changes in this patch.

Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
 arch/arm64/include/asm/mem_encrypt.h |  3 +--
 arch/arm64/kernel/rsi.c              | 34 +++++++++++++++++++++++++
 arch/arm64/mm/pageattr.c             | 38 +---------------------------
 3 files changed, 36 insertions(+), 39 deletions(-)

diff --git a/arch/arm64/include/asm/mem_encrypt.h b/arch/arm64/include/asm/mem_encrypt.h
index 314b2b52025f..f6325f30e844 100644
--- a/arch/arm64/include/asm/mem_encrypt.h
+++ b/arch/arm64/include/asm/mem_encrypt.h
@@ -15,8 +15,7 @@ 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);
-
-int realm_register_memory_enc_ops(void);
+int __set_memory_enc_dec(unsigned long addr, int numpages, bool encrypt);
 
 static inline bool force_dma_unencrypted(struct device *dev)
 {
diff --git a/arch/arm64/kernel/rsi.c b/arch/arm64/kernel/rsi.c
index 1fb2abd79800..5c566700974c 100644
--- a/arch/arm64/kernel/rsi.c
+++ b/arch/arm64/kernel/rsi.c
@@ -143,6 +143,40 @@ static int realm_ioremap_hook(phys_addr_t phys, size_t size, pgprot_t *prot)
 	return 0;
 }
 
+static int realm_set_memory_encrypted(unsigned long addr, int numpages)
+{
+	int ret = __set_memory_enc_dec(addr, numpages, true);
+
+	/*
+	 * If the request to change state fails, then the only sensible cause
+	 * of action for the caller is to leak the memory
+	 */
+	WARN(ret, "Failed to encrypt memory, %d pages will be leaked",
+	     numpages);
+
+	return ret;
+}
+
+static int realm_set_memory_decrypted(unsigned long addr, int numpages)
+{
+	int ret = __set_memory_enc_dec(addr, numpages, false);
+
+	WARN(ret, "Failed to decrypt memory, %d pages will be leaked",
+	     numpages);
+
+	return ret;
+}
+
+static const struct arm64_mem_crypt_ops realm_crypt_ops = {
+	.encrypt = realm_set_memory_encrypted,
+	.decrypt = realm_set_memory_decrypted,
+};
+
+static int realm_register_memory_enc_ops(void)
+{
+	return arm64_mem_crypt_ops_register(&realm_crypt_ops);
+}
+
 void __init arm64_rsi_init(void)
 {
 	if (arm_smccc_1_1_get_conduit() != SMCCC_CONDUIT_SMC)
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index bbe98ac9ad8c..14b2a3801f40 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -275,9 +275,7 @@ int set_direct_map_default_noflush(struct page *page)
 				 PAGE_SIZE, set_mask, clear_mask);
 }
 
-static int __set_memory_enc_dec(unsigned long addr,
-				int numpages,
-				bool encrypt)
+int __set_memory_enc_dec(unsigned long addr, int numpages, bool encrypt)
 {
 	unsigned long set_prot = 0, clear_prot = 0;
 	phys_addr_t start, end;
@@ -321,40 +319,6 @@ static int __set_memory_enc_dec(unsigned long addr,
 				      __pgprot(PTE_PRESENT_INVALID));
 }
 
-static int realm_set_memory_encrypted(unsigned long addr, int numpages)
-{
-	int ret = __set_memory_enc_dec(addr, numpages, true);
-
-	/*
-	 * If the request to change state fails, then the only sensible cause
-	 * of action for the caller is to leak the memory
-	 */
-	WARN(ret, "Failed to encrypt memory, %d pages will be leaked",
-	     numpages);
-
-	return ret;
-}
-
-static int realm_set_memory_decrypted(unsigned long addr, int numpages)
-{
-	int ret = __set_memory_enc_dec(addr, numpages, false);
-
-	WARN(ret, "Failed to decrypt memory, %d pages will be leaked",
-	     numpages);
-
-	return ret;
-}
-
-static const struct arm64_mem_crypt_ops realm_crypt_ops = {
-	.encrypt = realm_set_memory_encrypted,
-	.decrypt = realm_set_memory_decrypted,
-};
-
-int realm_register_memory_enc_ops(void)
-{
-	return arm64_mem_crypt_ops_register(&realm_crypt_ops);
-}
-
 int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid)
 {
 	unsigned long addr = (unsigned long)page_address(page);
-- 
2.43.0


^ permalink raw reply related

* [PATCH v5 07/10] dma-buf: system_heap: Enforce shared-granule alignment for cc-shared buffers
From: Aneesh Kumar K.V (Arm) @ 2026-07-06  6:04 UTC (permalink / raw)
  To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
  Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
	Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
	Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-1-aneesh.kumar@kernel.org>

The system heap can allocate buffers that are decrypted and shared with the
host. For confidential-computing guests, those shared buffers must cover
whole shared-buffer granule; otherwise a userspace mmap of the dma-buf may
expose only part of a host-managed granule and allow unintended access to
adjacent private memory.

Require cc-shared system-heap allocations to have a size aligned to
mem_cc_shared_granule_size(), and allocate pages at least as large as the
required granule. Keep the allocation bounded by the existing heap orders,
but fall back to an exact minimum-order allocation when the required
granule is not one of the preferred heap orders.

Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
 drivers/dma-buf/heaps/system_heap.c | 50 +++++++++++++++++++++++------
 1 file changed, 41 insertions(+), 9 deletions(-)

diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c
index c8959eadc71d..9cbfcebe2088 100644
--- a/drivers/dma-buf/heaps/system_heap.c
+++ b/drivers/dma-buf/heaps/system_heap.c
@@ -55,7 +55,6 @@ struct dma_heap_attachment {
 #define HIGH_ORDER_GFP  (((GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN \
 				| __GFP_NORETRY) & ~__GFP_RECLAIM) \
 				| __GFP_COMP)
-static gfp_t order_flags[] = {HIGH_ORDER_GFP, HIGH_ORDER_GFP, LOW_ORDER_GFP};
 /*
  * The selection of the orders used for allocation (1MB, 64K, 4K) is designed
  * to match with the sizes often found in IOMMUs. Using order 4 pages instead
@@ -375,26 +374,44 @@ static const struct dma_buf_ops system_heap_buf_ops = {
 	.release = system_heap_dma_buf_release,
 };
 
+static struct page *system_heap_alloc_order(unsigned int order)
+{
+	gfp_t flags = order ? HIGH_ORDER_GFP : LOW_ORDER_GFP;
+
+	if (mem_accounting)
+		flags |= __GFP_ACCOUNT;
+
+	return alloc_pages(flags, order);
+}
+
 static struct page *alloc_largest_available(unsigned long size,
-					    unsigned int max_order)
+					    unsigned int max_order,
+					    unsigned int min_order)
 {
 	struct page *page;
 	int i;
-	gfp_t flags;
 
 	for (i = 0; i < NUM_ORDERS; i++) {
 		if (size <  (PAGE_SIZE << orders[i]))
 			continue;
-		if (max_order < orders[i])
+
+		if (max_order < orders[i] || orders[i] < min_order)
 			continue;
-		flags = order_flags[i];
-		if (mem_accounting)
-			flags |= __GFP_ACCOUNT;
-		page = alloc_pages(flags, orders[i]);
+
+		page = system_heap_alloc_order(orders[i]);
 		if (!page)
 			continue;
 		return page;
 	}
+	/*
+	 * The required minimum order might not be one of the preferred heap
+	 * orders. Allocate exactly min_order when it does not exceed the
+	 * remaining size.
+	 */
+	if (min_order && min_order <= max_order &&
+	    size >= (PAGE_SIZE << min_order))
+		return system_heap_alloc_order(min_order);
+
 	return NULL;
 }
 
@@ -409,6 +426,8 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
 	unsigned int max_order = orders[0];
 	struct system_heap_priv *priv = dma_heap_get_drvdata(heap);
 	bool cc_shared = priv->cc_shared;
+	unsigned int min_order = 0;
+	size_t cc_granule_size;
 	struct dma_buf *dmabuf;
 	struct sg_table *table;
 	struct scatterlist *sg;
@@ -425,6 +444,18 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
 	buffer->heap = heap;
 	buffer->len = len;
 	buffer->cc_shared = cc_shared;
+	if (cc_shared_buffer(buffer)) {
+		cc_granule_size = mem_cc_shared_granule_size();
+		if (!IS_ALIGNED(len, cc_granule_size)) {
+			ret = -EINVAL;
+			goto free_buffer;
+		}
+		min_order = get_order(cc_granule_size);
+		if (min_order > max_order) {
+			ret = -EINVAL;
+			goto free_buffer;
+		}
+	}
 
 	INIT_LIST_HEAD(&pages);
 	i = 0;
@@ -438,7 +469,8 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
 			goto free_buffer;
 		}
 
-		page = alloc_largest_available(size_remaining, max_order);
+		page = alloc_largest_available(size_remaining, max_order,
+					       min_order);
 		if (!page)
 			goto free_buffer;
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH v5 06/10] swiotlb: Reject misaligned restricted DMA pools for CoCo guests
From: Aneesh Kumar K.V (Arm) @ 2026-07-06  6:04 UTC (permalink / raw)
  To: linux-coco, kvmarm, linux-arm-kernel, linux-kernel, iommu
  Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Jason Gunthorpe,
	Marc Zyngier, Marek Szyprowski, Robin Murphy, Steven Price,
	Suzuki K Poulose, Thomas Gleixner, Will Deacon
In-Reply-To: <20260706060432.1375570-1-aneesh.kumar@kernel.org>

Restricted DMA pools are described by firmware reserved-memory nodes and
are not resized or realigned by the kernel. For confidential-computing
guests, such pools can only be shared safely when both the base address and
size are aligned to the shared-granule size.

Reject restricted DMA pools that are not aligned to
mem_cc_shared_granule_size() when guest memory encryption is active.

Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
 kernel/dma/swiotlb.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index 1d77746b6f31..ad8a1ccf3977 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -2002,6 +2002,20 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
 		 * if platform supports memory encryption,
 		 * restricted mem pool is shared by default
 		 */
+		if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
+			size_t cc_shared_granule_size = mem_cc_shared_granule_size();
+
+			if (!IS_ALIGNED(rmem->base, cc_shared_granule_size) ||
+			    !IS_ALIGNED(rmem->size, cc_shared_granule_size)) {
+				dev_err(dev, "Restricted DMA pool must be aligned to %#zx bytes for memory encryption\n",
+					cc_shared_granule_size);
+				kfree(pool->areas);
+				kfree(pool->slots);
+				kfree(mem);
+				return -EINVAL;
+			}
+		}
+
 		if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {
 			int ret;
 
-- 
2.43.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox