* Re: [PATCH v8 4/7] x86/sev: Add support to perform RMP optimizations asynchronously
From: K Prateek Nayak @ 2026-06-17 4:20 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, 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: <8c5f4082-e3a5-4f65-b058-33938a7ee324@amd.com>
Hello Ashish,
On 6/17/2026 1:26 AM, Kalra, Ashish wrote:
> Hello Prateek,
>
> On 6/16/2026 2:27 AM, K Prateek Nayak wrote:
>> Hello Ashish,
>>
>> On 6/16/2026 1:19 AM, Ashish Kalra wrote:
>>> + /*
>>> + * RMPOPT scans the RMP table, stores the result of the scan in the
>>> + * reserved processor memory. The RMP scan is the most expensive
>>> + * part. If a second RMPOPT occurs, it can skip the expensive scan
>>> + * if they can see a cached result in the reserved processor memory.
>>> + *
>>> + * Do RMPOPT on one CPU alone. Then, follow that up with RMPOPT
>>> + * on every other primary thread. Followers are "designed to"
>>> + * skip the scan if they see the "cached" scan results.
>>> + */
>>> + cpumask_copy(follower_mask, &rmpopt_cpumask);
>>
>> rmpopt_cpumask is constructed after hotplug is disabled but ...
>>
>>> +
>>> + /*
>>> + * Pin the worker to the current CPU for the leader loop so that
>>> + * this_cpu remains valid and the RMPOPT instruction executes on
>>> + * the correct CPU.
>>> + *
>>> + * Use migrate_disable() rather than get_cpu() to prevent
>>> + * migration while still allowing preemption.
>>> + */
>>> + migrate_disable();
>>> + this_cpu = smp_processor_id();
>>> +
>>> + if (cpumask_test_cpu(this_cpu, follower_mask)) {
>>> + /*
>>> + * Current CPU is a primary thread in rmpopt_cpumask.
>>> + * Run leader locally and remove from follower mask.
>>> + */
>>> + cpumask_clear_cpu(this_cpu, follower_mask);
>>> +
>>> + for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa += SZ_1G) {
>>> + rmpopt(pa);
>>> + cond_resched();
>>> + }
>>> + } else if (cpumask_intersects(topology_sibling_cpumask(this_cpu),
>>> + follower_mask)) {
>>> + /*
>>> + * Current CPU is a sibling thread whose primary is in
>>> + * rmpopt_cpumask. RMPOPT_BASE MSR is per-core, so it
>>> + * is safe to run the leader locally. Remove the sibling's
>>> + * primary from the follower mask as this core is already
>>> + * covered by the leader.
>>> + */
>>> + cpumask_andnot(follower_mask, follower_mask,
>>> + topology_sibling_cpumask(this_cpu));
>>> +
>>> + for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa += SZ_1G) {
>>> + rmpopt(pa);
>>> + cond_resched();
>>> + }
>>> + } else {
>>> + /*
>>> + * Current CPU does not have RMPOPT_BASE MSR programmed.
>>> + * Pick an explicit leader from the cpumask to avoid #UD.
>>> + * Use work_on_cpu() to run in process context on the leader,
>>> + * avoiding IPI latency.
>>> + */
>>
>> ... this_cpu is neither in the "rmpopt_cpumask", nor is any of its
>> siblings on "rmpopt_cpumask".
>>
>> How does that happen?
>
> Actually, this was the implementation before the CPU hotplug disable enforcement code was implemented and added in v8,
> and i should have fixed this rmpopt_work_handler() accordingly for v8.
>
> With the enforced cpu hotplug disable support, case #3 here (above) is now dead code, and removing it lets
> cases #1 and #2 collapse too.
>
> snp_prepare() requires cpu_online_mask == cpu_present_mask before SNP init — so when snp_setup_rmpopt() programs the MSRs, every
> core's primary is online -> every core is in rmpopt_cpumask.
>
> So now the work handler always runs on a CPU whose core is programmed. topology_sibling_cpumask(this_cpu) therefore always intersects
> rmpopt_cpumask -> case #1 or #2 always matches.
>
> So i should actually drop case #3 here - which is: "this_cpu is neither in the "rmpopt_cpumask", nor is any of its
> siblings on rmpopt_cpumask"
Ack.
Also the fact that cpu_mark_primary_thread() uses LSBs of APICID and if
you have some insanely weird configuration - like boot with maxcpus=1,
online all the secondary threads (CPUs 256-511 on a 256C/512T system),
launch an SNP guest - it can actually leave everything except CORE0 out
of the "rmpopt_cpumask".
>
>
>>
>>> + int leader_cpu = cpumask_first(follower_mask);
>>> +
>>> + if (WARN_ON_ONCE(leader_cpu >= nr_cpu_ids)) {
>>> + migrate_enable();
>>> + goto out;
>>> + }
>>> +
>>> + cpumask_clear_cpu(leader_cpu, follower_mask);
>>> +
>>> + /* Release migration pin before work_on_cpu(). */
>>> + migrate_enable();
>>> +
>>> + work_on_cpu(leader_cpu, rmpopt_leader_fn, NULL);
>>
>> This creates a delayed work and also waits for it to finish execution
>> which will add more latency than a simple IPI if the comment about IPI
>> latency above is accurate.
>>
>> I think there is some corner case in construction of the
>> "rmpopt_cpumask" that requires this not-so-pretty else block. Can you
>> elaborate why this is required?
>>
>> Perhaps the "rmpopt_cpumask" construction needs:
>>
>> for_each_online_cpu(cpu) {
>> /* Nominate the first CPU on the sibling mask for RMPOPT */
>> if (cpu != cpumask_first(topology_sibling_cpumask(cpu)))
>> continue;
>> cpumask_set_cpu(cpu, &rmpopt_cpumask);
>> }
>>
>>
>> and all you need here is:
>>
>> /* Do RMPOPt for local core */
>> for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa += SZ_1G)
>> rmpopt(pa);
>>
>> /* Skip this core from concurrent RMPOPT */
>> cpumask_and_not(follower_mask, &rmpopt_cpumask, topology_sibling_cpumask(cpu));
>>
>> No?
>>
>
> Yes, a simpler implementation will be like this:
> ...
>
> if (!alloc_cpumask_var(&follower_mask, GFP_KERNEL))
> return;
>
If you move the migrate_disable() here, you can simply do an andnot
without needing to copy the rmpopt_cpumask beforehand and save on one
cpumask iteration.
> cpumask_copy(follower_mask, &rmpopt_cpumask);
>
> /*
> * The current CPU's core always has RMPOPT_BASE programmed
> * (snp_prepare() required all CPUs online at setup and CPU hotplug
> * is disabled while SNP is active), so it can always be the leader.
> * RMPOPT_BASE is per-core; exclude this core from the followers.
> */
> migrate_disable();
> cpumask_andnot(follower_mask, follower_mask,
> topology_sibling_cpumask(smp_processor_id()));
>
> for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa += SZ_1G) {
> rmpopt(pa);
> cond_resched();
> }
> migrate_enable();
>
> cpus_read_lock();
I think you can even skip the cpus_read_lock() since we know for a
fact that hotplug is disabled when we are here.
Perhaps we can have a lockdep_assert_cpu_hotplug_disabled() which
ensures we'll get a splat if that assumption ever changes when
running with LOCKDEP?
I'll let others comment if that is a good idea or not.
> for (pa = rmpopt_pa_start; pa < rmpopt_pa_end; pa += SZ_1G) {
> on_each_cpu_mask(follower_mask, rmpopt_smp, (void *)pa, true);
> cond_resched();
> }
> cpus_read_unlock();
>
> free_cpumask_var(follower_mask);
>
>
> Here, the leader exclusion must use the sibling mask, not clear_cpu(this_cpu). That's why my collapsed version uses:
>
> cpumask_andnot(follower_mask, follower_mask,
> topology_sibling_cpumask(smp_processor_id()));
>
> - If this_cpu is a primary: its sibling mask contains itself (the primary) -> andnot removes this core's primary from the followers.
>
> - If this_cpu is a secondary: it isn't in follower_mask at all, but its sibling mask contains its primary, which is in
> follower_mask -> andnot still removes this core's primary.
>
> So either way the current core is dropped from the followers. (The old code needed two branches because case #1 used
> clear_cpu(this_cpu) — only correct when this_cpu is the primary — while case #2 used the sibling andnot. The single andnot works for
> both cases).
Ack! And I think this looks much cleaner (to my eyes at least ;-)
--
Thanks and Regards,
Prateek
^ permalink raw reply
* Re: [PATCH v8 3/7] crypto/ccp: Disable CPU hotplug while SNP is active
From: K Prateek Nayak @ 2026-06-17 4:33 UTC (permalink / raw)
To: Ashish Kalra, tglx, mingo, bp, dave.hansen, x86, hpa, seanjc,
peterz, thomas.lendacky, herbert, davem, ardb
Cc: pbonzini, aik, Michael.Roth, 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: <1feccf6e2a56d949b30f403c0ca7949f580e5982.1781419998.git.ashish.kalra@amd.com>
Hello Ashish,
On 6/16/2026 1:19 AM, Ashish Kalra wrote:
> From: Ashish Kalra <ashish.kalra@amd.com>
>
> The SEV firmware enumerates the CPUs at SNP initialization and is not
> aware of the OS bringing CPUs online or offline afterwards, so OS CPU
> hotplug can diverge from the firmware's expectations and break SNP.
> Disable CPU hotplug while SNP is active.
Dumb question: Is this specific to RMPOPT? Otherwise ...
>
> SNP is fully torn down only on the SNP_SHUTDOWN_EX x86_snp_shutdown
> path; the legacy path leaves SNP enabled in hardware while clearing
> snp_initialized, so __sev_snp_init_locked() can run again. Track the
> disable with a flag so it is balanced by a matching enable rather than
> stacked, and re-enable hotplug only on the x86_snp_shutdown path, after
> snp_shutdown() has cleared the per-core RMPOPT_BASE MSRs with hotplug
> still disabled.
>
> This also keeps the CPU set stable for the asynchronous RMPOPT scan
> added later in this series, and ensures cpus_read_lock() in the scan
> is uncontended.
>
> Suggested-by: Thomas Lendacky <thomas.lendacky@amd.com>
> Signed-off-by: Ashish Kalra <ashish.kalra@amd.com>
> ---
> drivers/crypto/ccp/sev-dev.c | 29 ++++++++++++++++++++++++++++-
> 1 file changed, 28 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
> index 217b6b19802e..c8c3c577463c 100644
> --- a/drivers/crypto/ccp/sev-dev.c
> +++ b/drivers/crypto/ccp/sev-dev.c
> @@ -106,6 +106,9 @@ struct snp_hv_fixed_pages_entry {
>
> static LIST_HEAD(snp_hv_fixed_pages);
>
> +/* Set while SNP has CPU hotplug disabled. */
> +static bool snp_cpu_hotplug_disabled;
> +
> /* Trusted Memory Region (TMR):
> * The TMR is a 1MB area that must be 1MB aligned. Use the page allocator
> * to allocate the memory, which will return aligned memory for the specified
> @@ -1479,6 +1482,17 @@ static int __sev_snp_init_locked(int *error, unsigned int max_snp_asid)
>
> snp_hv_fixed_pages_state_update(sev, HV_FIXED);
>
> + /*
> + * Disable CPU hotplug while SNP is active. Guard against stacking
> + * the disable count: the legacy SNP_SHUTDOWN_EX path clears
> + * snp_initialized without re-enabling hotplug, so this can run
> + * again while hotplug is already disabled.
> + */
> + if (!snp_cpu_hotplug_disabled) {
> + cpu_hotplug_disable();
> + snp_cpu_hotplug_disabled = true;
> + }
> +
... should this be done before __sev_do_cmd_locked(SEV_CMD_SNP_INIT_EX)
is issued?
I'm assuming that is when the firmware enumerates the CPUs during SNP
initialization and any hotplug after that should be disallowed?
> snp_setup_rmpopt();
>
> sev->snp_initialized = true;
--
Thanks and Regards,
Prateek
^ permalink raw reply
* Re: [PATCH v3] lib/raid/xor: x86: Add AVX-512 optimized xor_gen()
From: Christoph Hellwig @ 2026-06-17 5:44 UTC (permalink / raw)
To: Dave Hansen
Cc: Borislav Petkov, Eric Biggers, Richard Weinberger, x86,
Christoph Hellwig, linux-crypto, David Laight, linux-raid,
Andrew Morton, linux-kernel, linux-um
In-Reply-To: <a832eee3-55ec-4cf4-907f-346ff98870ca@intel.com>
On Mon, Jun 15, 2026 at 05:29:58PM -0700, Dave Hansen wrote:
> On 6/15/26 16:53, Borislav Petkov wrote:
> >
> >> In any case, I'd like these to go away:
> >>
> >> $ git grep cpu_has_xfeatures | wc -l
> >> 31
> > Yeah, all in crypto. I can certainly see why.
> >
> > @dhansen, any other thoughts?
>
> If we can get rid of cpu_has_xfeatures(), I'm all for it. I'm not quite
> sure how the code would look so I'm reserving judgement until I see the
> patches. But it's worth a try.
I think the most important part is to be consistent. Either use it
everywhere or not at all.
^ permalink raw reply
* Re: [PATCH v2] lib/raid/xor: x86: Add AVX-512 optimized xor_gen()
From: Christoph Hellwig @ 2026-06-17 5:52 UTC (permalink / raw)
To: Eric Biggers
Cc: David Laight, Andrew Morton, linux-kernel, Christoph Hellwig,
linux-crypto, x86, linux-raid
In-Reply-To: <20260615184435.GA17731@quark>
On Mon, Jun 15, 2026 at 11:44:35AM -0700, Eric Biggers wrote:
> > Doesn't zen4 only have a 256bit bus between the cpu and cache?
> > So avx512 reads take two clocks.
> > Since this is memory limited it is unlikely to run faster than the
> > avx256 version.
>
> On AMD Genoa (Zen 4 server processor), the AVX-512 code added by this
> patch is indeed about the same speed as the existing AVX-2 code.
The same is true for Zen 5 mobile which has the same AVX-512 limitations.
I don't think it's the bus width, but I'll leave the details to the
experts.
>
> > OTOH if it doesn't cause down-clocking as well then it won't be slower.
>
> Yes, as far as I know that's not an issue on AMD processors, even Zen 4.
> The "avoid AVX-512 due to downclocking" rule is historical guidance for
> Intel processors that had a bad implementation of AVX-512. There's no
> reason to exclude Zen 4 from executing AVX-512 optimized code. At worst
> it will just be the same, as we're seeing here.
It does not cause down clocking. But for some of the more complicated
code I've seen AVX512 being significantly slower than AVX2 on these.
So we need to watch out and not automatically assume AVX512 is faster.
^ permalink raw reply
* Re: [PATCH v3] lib/raid/xor: x86: Add AVX-512 optimized xor_gen()
From: Christoph Hellwig @ 2026-06-17 5:56 UTC (permalink / raw)
To: Eric Biggers
Cc: Andrew Morton, linux-kernel, Christoph Hellwig, linux-crypto, x86,
David Laight, linux-raid
In-Reply-To: <20260615190338.26581-1-ebiggers@kernel.org>
Can use the xor: prefix used for all other commits to lib/raid/xor?
> Benchmark on AMD Ryzen 9 9950X (Zen 5):
>
> src_cnt avx avx512 Improvement
> ======= ========== ========== ===========
> 1 56353 MB/s 75388 MB/s 33%
> 2 54274 MB/s 68409 MB/s 26%
> 3 44649 MB/s 64042 MB/s 43%
> 4 41315 MB/s 55002 MB/s 33%
On my Zen 5 mobile (AMD Ryzen AI 7 PRO 350) both the existing
AVX2 and this AVX512 code give numbers in the 200+ GB/s range. Not
sure if is just the different benchmarking or something else going on.
FYI, one or 2 sources are basically useless as they RAID5 configs
that have no benefits over simple mirroring and thus the numbers
aren't too interesting.
> +DO_XOR_BLOCKS(avx512_inner, xor_avx512_2, xor_avx512_3, xor_avx512_4,
> + xor_avx512_5);
Is there really much of a benefit of doing the historic DO_XOR_BLOCKS
vs doing the loop manually? Especially as the common cases for a
modern RAID will usually loop over more disks than this was built
for. I.e., in practice one or two source buffers only happen at the
end of a loop over more disks.
^ permalink raw reply
* [PATCH] crypto: hisilicon - Use more common code in sec_alg_skcipher_crypto()
From: Markus Elfring @ 2026-06-17 6:03 UTC (permalink / raw)
To: linux-crypto, David S. Miller, Herbert Xu, John Garry,
Jonathan Cameron, Kees Cook, Thomas Fourier, Thorsten Blum
Cc: LKML, kernel-janitors
From: Markus Elfring <elfring@users.sourceforge.net>
Date: Wed, 17 Jun 2026 07:51:26 +0200
Move a label so that a bit of common code can be better reused at the end
of this function implementation.
This issue was detected by using the Coccinelle software.
Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
---
drivers/crypto/hisilicon/sec/sec_algs.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/crypto/hisilicon/sec/sec_algs.c b/drivers/crypto/hisilicon/sec/sec_algs.c
index 85eecbb40e7e..024fe1001875 100644
--- a/drivers/crypto/hisilicon/sec/sec_algs.c
+++ b/drivers/crypto/hisilicon/sec/sec_algs.c
@@ -833,6 +833,7 @@ static int sec_alg_skcipher_crypto(struct skcipher_request *skreq,
kfree(splits_in);
kfree(splits_out_nents);
kfree(splits_out);
+err_free_split_sizes:
kfree(split_sizes);
return ret;
@@ -853,10 +854,7 @@ static int sec_alg_skcipher_crypto(struct skcipher_request *skreq,
err_unmap_in_sg:
sec_unmap_sg_on_err(skreq->src, steps, splits_in, splits_in_nents,
sec_req->len_in, info->dev);
-err_free_split_sizes:
- kfree(split_sizes);
-
- return ret;
+ goto err_free_split_sizes;
}
static int sec_alg_skcipher_encrypt(struct skcipher_request *req)
--
2.54.0
^ permalink raw reply related
* [PATCH v3] crypto: ti-dthev2:Fix potential invalid access when device list is empty
From: Hongling Zeng @ 2026-06-17 6:33 UTC (permalink / raw)
To: t-pratham, herbert, davem
Cc: linux-crypto, linux-kernel, zhongling0719, Hongling Zeng
list_first_entry() never returns NULL - if the list is empty, it still
returns a pointer to an invalid object, leading to potential invalid
memory access when dereferenced.
Fix this by using list_first_entry_or_null instead of list_first_entry.
Fixes: 52f641bc63a4 ("crypto: ti - Add driver for DTHE V2 AES Engine (ECB, CBC)")
Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
---
Change in v2
-Reorder dthe_remove(): unregister algorithms before removing from list
This prevents new allocations during removal.
---
Change in v3
-Fix spinlock inconsistency:dthe_get_dev() uses spin_lock_bh() while
dthe_probe() and dthe_remove() use spin_lock(). This can cause deadlock
if softirq interrupts process context holding the lock.
---
drivers/crypto/ti/dthev2-common.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/crypto/ti/dthev2-common.c b/drivers/crypto/ti/dthev2-common.c
index a2ad79bec105..b315c850f05d 100644
--- a/drivers/crypto/ti/dthev2-common.c
+++ b/drivers/crypto/ti/dthev2-common.c
@@ -39,11 +39,11 @@ struct dthe_data *dthe_get_dev(struct dthe_tfm_ctx *ctx)
if (ctx->dev_data)
return ctx->dev_data;
- spin_lock_bh(&dthe_dev_list.lock);
- dev_data = list_first_entry(&dthe_dev_list.dev_list, struct dthe_data, list);
+ spin_lock(&dthe_dev_list.lock);
+ dev_data = list_first_entry_or_null(&dthe_dev_list.dev_list, struct dthe_data, list);
if (dev_data)
list_move_tail(&dev_data->list, &dthe_dev_list.dev_list);
- spin_unlock_bh(&dthe_dev_list.lock);
+ spin_unlock(&dthe_dev_list.lock);
return dev_data;
}
@@ -201,12 +201,12 @@ static void dthe_remove(struct platform_device *pdev)
{
struct dthe_data *dev_data = platform_get_drvdata(pdev);
+ dthe_unregister_algs();
+
spin_lock(&dthe_dev_list.lock);
list_del(&dev_data->list);
spin_unlock(&dthe_dev_list.lock);
- dthe_unregister_algs();
-
crypto_engine_exit(dev_data->engine);
dma_release_channel(dev_data->dma_aes_rx);
--
2.25.1
^ permalink raw reply related
* Re: [PATCH v2] hw_random: timeriomem-rng: add configurable read width and data mask
From: Krzysztof Kozlowski @ 2026-06-17 8:08 UTC (permalink / raw)
To: Jad Keskes
Cc: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Alexander Clouter, linux-crypto, devicetree,
linux-kernel
In-Reply-To: <20260615201339.1264676-1-inasj268@gmail.com>
On Mon, Jun 15, 2026 at 09:13:39PM +0100, Jad Keskes wrote:
> The TODO for supporting read sizes other than 32 bits and masking has
> been sitting in this driver since 2009. Implement it.
>
> Add width (8, 16, or 32 bits) and mask properties to the platform data
> and device tree bindings. The read loop dispatches on width using
> readb/readw/readl so a configured 8-bit access doesn't trigger a bus
> error on hardware that rejects 32-bit reads to that address. The mask
> is ANDed with the value before storing.
>
> These are platform properties, not runtime policy -- width depends on
> SoC integration, mask reflects which output bits carry entropy.
>
> The alignment check in probe is updated to verify the resource is
> aligned to the configured width instead of hardcoding 4-byte alignment.
>
> Signed-off-by: Jad Keskes <inasj268@gmail.com>
> ---
>
> v2:
> - Remove old timeriomem_rng.yaml to avoid dt_binding_check conflict
> - Use IS_ALIGNED() instead of modulo for 32-bit PAE safety
>
>
> .../bindings/rng/timeriomem-rng.yaml | 76 ++++++++++++++++++
> .../bindings/rng/timeriomem_rng.yaml | 48 ------------
I don't undetstand this diff... what are you doing exactly? And more
important WHY?
Please run scripts/checkpatch.pl on the patches and fix reported
warnings. After that, run also 'scripts/checkpatch.pl --strict' on the
patches and (probably) fix more warnings. Some warnings can be ignored,
especially from --strict run, but the code here looks like it needs a
fix. Feel free to get in touch if the warning is not clear.
> drivers/char/hw_random/timeriomem-rng.c | 78 +++++++++++++++----
> include/linux/timeriomem-rng.h | 12 +++
> 4 files changed, 153 insertions(+), 61 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/rng/timeriomem-rng.yaml
> delete mode 100644 Documentation/devicetree/bindings/rng/timeriomem_rng.yaml
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH 0/2] Fix Qualcomm Crypto engine self tests failures
From: Kuldeep Singh @ 2026-06-17 9:45 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Eric Biggers, Bartosz Golaszewski, Thara Gopinath, Herbert Xu,
David S. Miller, Thara Gopinath, linux-crypto, linux-arm-msm,
linux-kernel
In-Reply-To: <4fzeulsheu5tam6pcymjqkqnqi3ibjgwchiefy27wr7b5i2yhk@4m47fpfbsmwf>
>> Seems Bartosz will be sending patches for algorithm removal changes.
>> The rest relevant selftests issues we'll fix accordingly.
>
> So, the old kernels will remain broken? Or do we expect to backport the
> cipher removal patches too?
Kindly see Barotsz series on top of this one[1].
Fixes will be backported on older kernels too.
[1]
https://lore.kernel.org/linux-arm-msm/20260615-qce-fix-self-tests-v2-0-dc911f1aad42@oss.qualcomm.com/
--
Regards
Kuldeep
^ permalink raw reply
* Re: [PATCH v3] lib/raid/xor: x86: Add AVX-512 optimized xor_gen()
From: David Laight @ 2026-06-17 10:05 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Eric Biggers, Andrew Morton, linux-kernel, linux-crypto, x86,
linux-raid
In-Reply-To: <20260617055653.GB19218@lst.de>
On Wed, 17 Jun 2026 07:56:53 +0200
Christoph Hellwig <hch@lst.de> wrote:
> Can use the xor: prefix used for all other commits to lib/raid/xor?
>
> > Benchmark on AMD Ryzen 9 9950X (Zen 5):
> >
> > src_cnt avx avx512 Improvement
> > ======= ========== ========== ===========
> > 1 56353 MB/s 75388 MB/s 33%
> > 2 54274 MB/s 68409 MB/s 26%
> > 3 44649 MB/s 64042 MB/s 43%
> > 4 41315 MB/s 55002 MB/s 33%
>
> On my Zen 5 mobile (AMD Ryzen AI 7 PRO 350) both the existing
> AVX2 and this AVX512 code give numbers in the 200+ GB/s range. Not
> sure if is just the different benchmarking or something else going on.
I'd expect benchmarking of the xor loop to show that it is doing
2 memory reads every clock.
At 5GHz (I'm sure my zen5 will reach that single threaded) that
is 320 GB/s for src_cnt == 1 and 160 GB/s for src_cnt == 3.
200 GB/s with 32 byte cache reads would need a 200/32 = 6GHz cpu
just for the reads.
But I expect Eric is benchmarking more code and may be limited
by data cache refills.
>
> FYI, one or 2 sources are basically useless as they RAID5 configs
> that have no benefits over simple mirroring and thus the numbers
> aren't too interesting.
With three disks you xor two buffers (src_count == 1) to get the parity
to write to the third - so that is a valid RAID5 config.
>
> > +DO_XOR_BLOCKS(avx512_inner, xor_avx512_2, xor_avx512_3, xor_avx512_4,
> > + xor_avx512_5);
>
> Is there really much of a benefit of doing the historic DO_XOR_BLOCKS
> vs doing the loop manually? Especially as the common cases for a
> modern RAID will usually loop over more disks than this was built
> for. I.e., in practice one or two source buffers only happen at the
> end of a loop over more disks.
I stopped looking at what was being tested at that point :-)
David
^ permalink raw reply
* [PATCH v3 1/2] dt-bindings: rng: timeriomem_rng: add width and mask properties
From: Jad Keskes @ 2026-06-17 11:26 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Olivia Lu, Herbert Xu, Rob Herring, Alexander Clouter,
linux-crypto, devicetree, linux-kernel, Jad Keskes
Add optional width (8, 16, 32) and mask properties to the binding.
The width selects the bus access size for reads. The mask is ANDed
with the raw register value to allow only the entropy-bearing bits
through.
Update the example to show a typical 8-bit configuration.
Update SPDX to dual license to match kernel convention.
Drop the misleading '32-bit aligned' constraint from the reg
description since alignment now depends on the configured width.
Signed-off-by: Jad Keskes <inasj268@gmail.com>
---
.../bindings/rng/timeriomem_rng.yaml | 48 +++++++++++++++----
1 file changed, 40 insertions(+), 8 deletions(-)
diff --git a/Documentation/devicetree/bindings/rng/timeriomem_rng.yaml b/Documentation/devicetree/bindings/rng/timeriomem_rng.yaml
index 4754174e9849..636305f211c8 100644
--- a/Documentation/devicetree/bindings/rng/timeriomem_rng.yaml
+++ b/Documentation/devicetree/bindings/rng/timeriomem_rng.yaml
@@ -1,10 +1,16 @@
-# SPDX-License-Identifier: GPL-2.0-only
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/rng/timeriomem_rng.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: TimerIO Random Number Generator
+title: Timer IOMEM Hardware Random Number Generator
+
+description: |
+ This binding covers platforms that have a single IO memory address which
+ provides periodic random data. The driver reads from the address at a
+ fixed interval, returning a configurable-width value masked to the desired
+ bits.
maintainers:
- Krzysztof Kozlowski <krzk@kernel.org>
@@ -13,9 +19,17 @@ properties:
compatible:
const: timeriomem_rng
+ reg:
+ maxItems: 1
+ description:
+ Base address to sample from. Must be aligned to the configured access
+ width (1, 2, or 4 bytes) and at least that wide.
+
period:
$ref: /schemas/types.yaml#/definitions/uint32
- description: wait time in microseconds to use between samples
+ description:
+ Interval in microseconds between reads. New random data is expected to
+ be available at this rate.
quality:
$ref: /schemas/types.yaml#/definitions/uint32
@@ -26,16 +40,26 @@ properties:
instead. Note that the default quality is usually zero which disables
using this rng to automatically fill the kernel's entropy pool.
- reg:
- maxItems: 1
+ width:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ default: 32
+ enum: [8, 16, 32]
description:
- Base address to sample from. Currently 'reg' must be at least four bytes
- wide and 32-bit aligned.
+ Access width in bits. Determines whether the read is performed as
+ an 8-bit, 16-bit, or 32-bit bus access.
+
+ mask:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ default: 0xFFFFFFFF
+ description:
+ Mask applied to the value read from the register. Bits set to 0 in
+ the mask are cleared in the output data. Default (no mask) passes
+ all bits through.
required:
- compatible
- - period
- reg
+ - period
additionalProperties: false
@@ -46,3 +70,11 @@ examples:
reg = <0x44 0x04>;
period = <1000000>;
};
+
+ rng@64 {
+ compatible = "timeriomem_rng";
+ reg = <0x64 0x01>;
+ period = <50000>;
+ width = <8>;
+ mask = <0xFF>;
+ };
--
2.54.0
^ permalink raw reply related
* [PATCH v3 2/2] hw_random: timeriomem-rng: add configurable read width and data mask
From: Jad Keskes @ 2026-06-17 11:26 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Olivia Lu, Herbert Xu, Rob Herring, Alexander Clouter,
linux-crypto, devicetree, linux-kernel, Jad Keskes
In-Reply-To: <20260617112642.1897096-1-inasj268@gmail.com>
The TODO for supporting read sizes other than 32 bits and masking has
been sitting in this driver since 2009. Implement it.
Add width (8, 16, or 32 bits) and mask properties to the platform data
and device tree bindings. The read loop dispatches on width using
readb/readw/readl so a configured 8-bit access doesn't trigger a bus
error on hardware that rejects 32-bit reads to that address. The mask
is ANDed with the value before storing.
These are platform properties, not runtime policy -- width depends on
SoC integration, mask reflects which output bits carry entropy.
The alignment check in probe is updated to verify the resource is
aligned to the configured width instead of hardcoding 4-byte alignment.
Signed-off-by: Jad Keskes <inasj268@gmail.com>
---
drivers/char/hw_random/timeriomem-rng.c | 78 ++++++++++++++++++++-----
include/linux/timeriomem-rng.h | 12 ++++
2 files changed, 77 insertions(+), 13 deletions(-)
diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c
index e61f06393209..4557326618c9 100644
--- a/drivers/char/hw_random/timeriomem-rng.c
+++ b/drivers/char/hw_random/timeriomem-rng.c
@@ -14,7 +14,9 @@
* has to do is provide the address and 'wait time' that new data becomes
* available.
*
- * TODO: add support for reading sizes other than 32bits and masking
+ * The read width (8, 16, or 32 bits) and an optional data mask can be
+ * configured through platform data or device tree properties. Default is
+ * 32-bit reads with no mask.
*/
#include <linux/completion.h>
@@ -34,6 +36,8 @@ struct timeriomem_rng_private {
void __iomem *io_base;
ktime_t period;
unsigned int present:1;
+ unsigned int width;
+ u32 mask;
struct hrtimer timer;
struct completion completion;
@@ -48,6 +52,7 @@ static int timeriomem_rng_read(struct hwrng *hwrng, void *data,
container_of(hwrng, struct timeriomem_rng_private, rng_ops);
int retval = 0;
int period_us = ktime_to_us(priv->period);
+ int chunk = priv->width / 8;
/*
* There may not have been enough time for new data to be generated
@@ -71,11 +76,28 @@ static int timeriomem_rng_read(struct hwrng *hwrng, void *data,
usleep_range(period_us,
period_us + max(1, period_us / 100));
- *(u32 *)data = readl(priv->io_base);
- retval += sizeof(u32);
- data += sizeof(u32);
- max -= sizeof(u32);
- } while (wait && max > sizeof(u32));
+ switch (priv->width) {
+ case 8: {
+ u8 val = readb(priv->io_base) & priv->mask;
+ *(u8 *)data = val;
+ break;
+ }
+ case 16: {
+ u16 val = readw(priv->io_base) & priv->mask;
+ *(u16 *)data = val;
+ break;
+ }
+ case 32: {
+ u32 val = readl(priv->io_base) & priv->mask;
+ *(u32 *)data = val;
+ break;
+ }
+ }
+
+ retval += chunk;
+ data += chunk;
+ max -= chunk;
+ } while (wait && max > chunk);
/*
* Block any new callers until the RNG has had time to generate new
@@ -125,11 +147,8 @@ static int timeriomem_rng_probe(struct platform_device *pdev)
if (IS_ERR(priv->io_base))
return PTR_ERR(priv->io_base);
- if (res->start % 4 != 0 || resource_size(res) < 4) {
- dev_err(&pdev->dev,
- "address must be at least four bytes wide and 32-bit aligned\n");
- return -EINVAL;
- }
+ priv->width = 32;
+ priv->mask = 0xFFFFFFFF;
if (pdev->dev.of_node) {
int i;
@@ -145,9 +164,42 @@ static int timeriomem_rng_probe(struct platform_device *pdev)
if (!of_property_read_u32(pdev->dev.of_node,
"quality", &i))
priv->rng_ops.quality = i;
+
+ of_property_read_u32(pdev->dev.of_node,
+ "width", &priv->width);
+ of_property_read_u32(pdev->dev.of_node,
+ "mask", &priv->mask);
} else {
period = pdata->period;
priv->rng_ops.quality = pdata->quality;
+
+ if (pdata->width_set)
+ priv->width = pdata->width;
+ if (pdata->mask_set)
+ priv->mask = pdata->mask;
+ }
+
+ if (priv->width == 0)
+ priv->width = 32;
+
+ switch (priv->width) {
+ case 8:
+ case 16:
+ case 32:
+ break;
+ default:
+ dev_err(&pdev->dev, "invalid width %u, must be 8, 16, or 32\n",
+ priv->width);
+ return -EINVAL;
+ }
+
+ if (!IS_ALIGNED(res->start, priv->width / 8) ||
+ resource_size(res) < priv->width / 8) {
+ dev_err(&pdev->dev,
+ "address must be at least %u-bit aligned (%u byte%s)\n",
+ priv->width, priv->width / 8,
+ priv->width / 8 > 1 ? "s" : "");
+ return -EINVAL;
}
priv->period = us_to_ktime(period);
@@ -167,8 +219,8 @@ static int timeriomem_rng_probe(struct platform_device *pdev)
return err;
}
- dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n",
- priv->io_base, period);
+ dev_info(&pdev->dev, "%ubit from %p @ %dus\n",
+ priv->width, priv->io_base, period);
return 0;
}
diff --git a/include/linux/timeriomem-rng.h b/include/linux/timeriomem-rng.h
index 672df7fbf6c1..b4202ad2f507 100644
--- a/include/linux/timeriomem-rng.h
+++ b/include/linux/timeriomem-rng.h
@@ -16,6 +16,18 @@ struct timeriomem_rng_data {
/* bits of entropy per 1024 bits read */
unsigned int quality;
+
+ /* read width (8, 16, or 32), 0 means 32 */
+ unsigned int width;
+
+ /* set to true if width is explicitly provided */
+ bool width_set;
+
+ /* mask applied to raw read value */
+ u32 mask;
+
+ /* set to true if mask is explicitly provided */
+ bool mask_set;
};
#endif /* _LINUX_TIMERIOMEM_RNG_H */
--
2.54.0
^ permalink raw reply related
* [PATCH v4 1/2] dt-bindings: rng: timeriomem_rng: add reg-io-width and mask properties
From: Jad Keskes @ 2026-06-17 11:44 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Olivia Mackall, Herbert Xu, Rob Herring, Conor Dooley,
Alexander Clouter, linux-crypto, devicetree, linux-kernel,
Jad Keskes
Add optional reg-io-width (1, 2, or 4 bytes) and mask properties to the
binding. reg-io-width selects the bus access size, mask is ANDed with
the raw register value to allow only the entropy-bearing bits through.
Update the example to show a typical 1-byte configuration.
Update SPDX to dual license to match kernel convention.
Drop the misleading '32-bit aligned' constraint from the reg
description since alignment now depends on the configured width.
Signed-off-by: Jad Keskes <inasj268@gmail.com>
---
.../bindings/rng/timeriomem_rng.yaml | 48 +++++++++++++++----
1 file changed, 40 insertions(+), 8 deletions(-)
diff --git a/Documentation/devicetree/bindings/rng/timeriomem_rng.yaml b/Documentation/devicetree/bindings/rng/timeriomem_rng.yaml
index 4754174e9849..740bc52bf474 100644
--- a/Documentation/devicetree/bindings/rng/timeriomem_rng.yaml
+++ b/Documentation/devicetree/bindings/rng/timeriomem_rng.yaml
@@ -1,10 +1,16 @@
-# SPDX-License-Identifier: GPL-2.0-only
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/rng/timeriomem_rng.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: TimerIO Random Number Generator
+title: Timer IOMEM Hardware Random Number Generator
+
+description: |
+ This binding covers platforms that have a single IO memory address which
+ provides periodic random data. The driver reads from the address at a
+ fixed interval, returning a configurable-width value masked to the desired
+ bits.
maintainers:
- Krzysztof Kozlowski <krzk@kernel.org>
@@ -13,9 +19,17 @@ properties:
compatible:
const: timeriomem_rng
+ reg:
+ maxItems: 1
+ description:
+ Base address to sample from. Must be aligned to the configured access
+ width (1, 2, or 4 bytes) and at least that wide.
+
period:
$ref: /schemas/types.yaml#/definitions/uint32
- description: wait time in microseconds to use between samples
+ description:
+ Interval in microseconds between reads. New random data is expected to
+ be available at this rate.
quality:
$ref: /schemas/types.yaml#/definitions/uint32
@@ -26,16 +40,26 @@ properties:
instead. Note that the default quality is usually zero which disables
using this rng to automatically fill the kernel's entropy pool.
- reg:
- maxItems: 1
+ reg-io-width:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ default: 4
+ enum: [1, 2, 4]
description:
- Base address to sample from. Currently 'reg' must be at least four bytes
- wide and 32-bit aligned.
+ Access width in bytes. Determines whether the read is performed as
+ an 8-bit, 16-bit, or 32-bit bus access.
+
+ mask:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ default: 0xFFFFFFFF
+ description:
+ Mask applied to the value read from the register. Bits set to 0 in
+ the mask are cleared in the output data. Default (no mask) passes
+ all bits through.
required:
- compatible
- - period
- reg
+ - period
additionalProperties: false
@@ -46,3 +70,11 @@ examples:
reg = <0x44 0x04>;
period = <1000000>;
};
+
+ rng@64 {
+ compatible = "timeriomem_rng";
+ reg = <0x64 0x01>;
+ period = <50000>;
+ reg-io-width = <1>;
+ mask = <0xFF>;
+ };
--
2.54.0
^ permalink raw reply related
* [PATCH v4 2/2] hw_random: timeriomem-rng: add configurable read width and data mask
From: Jad Keskes @ 2026-06-17 11:46 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Olivia Mackall, Herbert Xu, Rob Herring, Conor Dooley,
Alexander Clouter, linux-crypto, devicetree, linux-kernel,
Jad Keskes
In-Reply-To: <20260617114436.1909659-1-inasj268@gmail.com>
The TODO for supporting read sizes other than 32 bits and masking has
been sitting in this driver since 2009. Implement it.
Add reg-io-width (1, 2, or 4 bytes) and mask support. The read loop
dispatches on width using readb/readw/readl so a configured 1-byte
access doesn't trigger a bus error on hardware that rejects 32-bit
reads to that address. The mask is ANDed with the value before storing.
These are platform properties, not runtime policy -- width depends on
SoC integration, mask reflects which output bits carry entropy.
The alignment check in probe is updated to verify the resource is
aligned to the configured width instead of hardcoding 4-byte alignment.
Signed-off-by: Jad Keskes <inasj268@gmail.com>
---
drivers/char/hw_random/timeriomem-rng.c | 77 ++++++++++++++++++++-----
include/linux/timeriomem-rng.h | 12 ++++
2 files changed, 76 insertions(+), 13 deletions(-)
diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c
index e61f06393209..42393409f22a 100644
--- a/drivers/char/hw_random/timeriomem-rng.c
+++ b/drivers/char/hw_random/timeriomem-rng.c
@@ -14,7 +14,9 @@
* has to do is provide the address and 'wait time' that new data becomes
* available.
*
- * TODO: add support for reading sizes other than 32bits and masking
+ * The read width (8, 16, or 32 bits) and an optional data mask can be
+ * configured through platform data or device tree properties. Default is
+ * 32-bit reads with no mask.
*/
#include <linux/completion.h>
@@ -34,6 +36,8 @@ struct timeriomem_rng_private {
void __iomem *io_base;
ktime_t period;
unsigned int present:1;
+ unsigned int reg_io_width;
+ u32 mask;
struct hrtimer timer;
struct completion completion;
@@ -48,6 +52,7 @@ static int timeriomem_rng_read(struct hwrng *hwrng, void *data,
container_of(hwrng, struct timeriomem_rng_private, rng_ops);
int retval = 0;
int period_us = ktime_to_us(priv->period);
+ int chunk = priv->reg_io_width;
/*
* There may not have been enough time for new data to be generated
@@ -71,11 +76,28 @@ static int timeriomem_rng_read(struct hwrng *hwrng, void *data,
usleep_range(period_us,
period_us + max(1, period_us / 100));
- *(u32 *)data = readl(priv->io_base);
- retval += sizeof(u32);
- data += sizeof(u32);
- max -= sizeof(u32);
- } while (wait && max > sizeof(u32));
+ switch (priv->reg_io_width) {
+ case 1: {
+ u8 val = readb(priv->io_base) & priv->mask;
+ *(u8 *)data = val;
+ break;
+ }
+ case 2: {
+ u16 val = readw(priv->io_base) & priv->mask;
+ *(u16 *)data = val;
+ break;
+ }
+ case 4: {
+ u32 val = readl(priv->io_base) & priv->mask;
+ *(u32 *)data = val;
+ break;
+ }
+ }
+
+ retval += chunk;
+ data += chunk;
+ max -= chunk;
+ } while (wait && max > chunk);
/*
* Block any new callers until the RNG has had time to generate new
@@ -125,11 +147,8 @@ static int timeriomem_rng_probe(struct platform_device *pdev)
if (IS_ERR(priv->io_base))
return PTR_ERR(priv->io_base);
- if (res->start % 4 != 0 || resource_size(res) < 4) {
- dev_err(&pdev->dev,
- "address must be at least four bytes wide and 32-bit aligned\n");
- return -EINVAL;
- }
+ priv->reg_io_width = 4;
+ priv->mask = 0xFFFFFFFF;
if (pdev->dev.of_node) {
int i;
@@ -145,9 +164,41 @@ static int timeriomem_rng_probe(struct platform_device *pdev)
if (!of_property_read_u32(pdev->dev.of_node,
"quality", &i))
priv->rng_ops.quality = i;
+
+ of_property_read_u32(pdev->dev.of_node,
+ "reg-io-width", &priv->reg_io_width);
+ of_property_read_u32(pdev->dev.of_node,
+ "mask", &priv->mask);
} else {
period = pdata->period;
priv->rng_ops.quality = pdata->quality;
+
+ if (pdata->reg_io_width_set)
+ priv->reg_io_width = pdata->reg_io_width;
+ if (pdata->mask_set)
+ priv->mask = pdata->mask;
+ }
+
+ if (priv->reg_io_width == 0)
+ priv->reg_io_width = 4;
+
+ switch (priv->reg_io_width) {
+ case 1:
+ case 2:
+ case 4:
+ break;
+ default:
+ dev_err(&pdev->dev, "invalid reg-io-width %u, must be 1, 2, or 4\n",
+ priv->reg_io_width);
+ return -EINVAL;
+ }
+
+ if (!IS_ALIGNED(res->start, priv->reg_io_width) ||
+ resource_size(res) < priv->reg_io_width) {
+ dev_err(&pdev->dev,
+ "address must be %u-byte aligned\n",
+ priv->reg_io_width);
+ return -EINVAL;
}
priv->period = us_to_ktime(period);
@@ -167,8 +218,8 @@ static int timeriomem_rng_probe(struct platform_device *pdev)
return err;
}
- dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n",
- priv->io_base, period);
+ dev_info(&pdev->dev, "%u-byte from %p @ %dus\n",
+ priv->reg_io_width, priv->io_base, period);
return 0;
}
diff --git a/include/linux/timeriomem-rng.h b/include/linux/timeriomem-rng.h
index 672df7fbf6c1..5732489a17a1 100644
--- a/include/linux/timeriomem-rng.h
+++ b/include/linux/timeriomem-rng.h
@@ -16,6 +16,18 @@ struct timeriomem_rng_data {
/* bits of entropy per 1024 bits read */
unsigned int quality;
+
+ /* read width (1, 2, or 4 bytes), 0 means 4 */
+ unsigned int reg_io_width;
+
+ /* set to true if reg-io-width is explicitly provided */
+ bool reg_io_width_set;
+
+ /* mask applied to raw read value */
+ u32 mask;
+
+ /* set to true if mask is explicitly provided */
+ bool mask_set;
};
#endif /* _LINUX_TIMERIOMEM_RNG_H */
--
2.54.0
^ permalink raw reply related
* [RFC PATCH 0/2] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA
From: Mike Lothian @ 2026-06-17 15:01 UTC (permalink / raw)
To: rust-for-linux
Cc: Mike Lothian, linux-crypto, Eric Biggers, Herbert Xu,
David S. Miller, Ard Biesheuvel, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, linux-kernel
This RFC series adds a small, reusable kernel::crypto module so in-kernel
Rust code can hash, encrypt a single AES block, and do RSA public-key
encryption:
1/2 sha256(), hmac_sha256(), Aes128 (single-block ECB)
2/2 Akcipher + rsa_pubkey_encrypt() over crypto_akcipher
Patch 1 binds the library crypto (lib/crypto) functions directly
(SHA-256 / HMAC-SHA256) and uses one rust_helper_ shim for aes_encrypt()
(its transparent union is unbindable). It runs synchronously in the
calling context with no allocation and is the independently-mergeable,
self-contained contribution.
Patch 2 adds crypto::Akcipher, a thin wrapper over the asynchronous
public-key API (crypto_akcipher) driven synchronously, and a
crypto::rsa_pubkey_encrypt() convenience built on it: it DER-encodes the
RSAPublicKey the "rsa" transform expects, runs one encrypt, and leaves
padding to the caller. The request/scatterlist/completion plumbing (all
static-inline or on-stack) plus a kmalloc bounce for the DMA data path
live in one rust_helper_ shim; crypto_free_akcipher() and
crypto_akcipher_set_pub_key() are exposed through 1:1 shims. Going
through crypto_akcipher rather than the MPI math library means it
composes with any registered RSA implementation, including hardware
offload. It is kept a separate patch so the public-key surface can be
reviewed (or deferred) on its own without touching patch 1.
Both were factored out of an out-of-tree in-kernel Rust DisplayLink DL3
dock driver (which needs SHA/HMAC/AES for HDCP 2.2 and RSA for the AKE),
but the module is generic. Compile-tested in-tree against drm-next.
Mike Lothian (2):
rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings
rust: crypto: add RSA public-key encryption via crypto_akcipher
rust/bindings/bindings_helper.h | 3 +
rust/helpers/crypto.c | 95 +++++++++++++++++++++++++++
rust/helpers/helpers.c | 1 +
rust/kernel/crypto.rs | 255 ++++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 1 +
5 files changed, 355 insertions(+)
--
2.54.0
^ permalink raw reply
* [RFC PATCH 1/2] rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings
From: Mike Lothian @ 2026-06-17 15:01 UTC (permalink / raw)
To: rust-for-linux
Cc: Mike Lothian, linux-crypto, Eric Biggers, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Daniel Almeida, Greg Kroah-Hartman, Yury Norov (NVIDIA),
Asahi Lina, Lorenzo Stoakes, Joel Fernandes, Alexandre Courbot,
FUJITA Tomonori, Krishna Ketan Rai, linux-kernel
In-Reply-To: <20260617150143.2152-1-mike@fireburn.co.uk>
Add a small `kernel::crypto` module exposing the kernel's synchronous
library crypto (lib/crypto) to Rust: SHA-256, HMAC-SHA256 and single-block
AES-128 ECB. These are one-shot, allocation-free and run in the calling
context, suitable for in-kernel Rust users that need a hash or a block
cipher without the full asynchronous crypto API.
SHA-256 (`sha256()`) and HMAC-SHA256 (`hmac_sha256_usingrawkey()`) are
plain exported functions and are bound directly via bindgen, so their
header `<crypto/sha2.h>` is added to bindings_helper.h. AES single-block
encryption goes through a `rust_helper_` shim because `aes_encrypt()` takes
a transparent union (`aes_encrypt_arg`) that bindgen cannot express; the
shim also zeroes the expanded key schedule before returning.
The Rust API: `crypto::sha256(&[u8]) -> [u8; 32]`,
`crypto::hmac_sha256(key, data) -> [u8; 32]`, and the `crypto::Aes128`
type, created with `Aes128::new(key)` and used via
`encrypt_block(&[u8; 16]) -> Result<[u8; 16]>`.
Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
rust/bindings/bindings_helper.h | 2 +
rust/helpers/crypto.c | 25 +++++++++++
rust/helpers/helpers.c | 1 +
rust/kernel/crypto.rs | 77 +++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 1 +
5 files changed, 106 insertions(+)
create mode 100644 rust/helpers/crypto.c
create mode 100644 rust/kernel/crypto.rs
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 1124785e210b..14671e1825bb 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -28,6 +28,8 @@
*/
#include <linux/hrtimer_types.h>
+#include <crypto/sha2.h>
+
#include <linux/acpi.h>
#include <linux/gpu_buddy.h>
#include <drm/drm_device.h>
diff --git a/rust/helpers/crypto.c b/rust/helpers/crypto.c
new file mode 100644
index 000000000000..dc9614f6fc8e
--- /dev/null
+++ b/rust/helpers/crypto.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <crypto/aes.h>
+#include <linux/string.h>
+
+/*
+ * AES-128 single-block ECB encryption: out = AES(key, in).
+ *
+ * A helper because aes_encrypt() takes a transparent union (aes_encrypt_arg)
+ * that bindgen cannot express. SHA-256 and HMAC-SHA256 are plain extern
+ * functions and are bound directly.
+ */
+__rust_helper int
+rust_helper_aes128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
+{
+ struct aes_enckey enckey;
+ int ret;
+
+ ret = aes_prepareenckey(&enckey, key, AES_KEYSIZE_128);
+ if (ret)
+ return ret;
+ aes_encrypt(&enckey, out, in);
+ memzero_explicit(&enckey, sizeof(enckey));
+ return 0;
+}
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 4488a87223b9..45e67929251e 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -55,6 +55,7 @@
#include "cpufreq.c"
#include "cpumask.c"
#include "cred.c"
+#include "crypto.c"
#include "device.c"
#include "dma.c"
#include "dma-resv.c"
diff --git a/rust/kernel/crypto.rs b/rust/kernel/crypto.rs
new file mode 100644
index 000000000000..c8f2cb994cfd
--- /dev/null
+++ b/rust/kernel/crypto.rs
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Safe wrappers over the kernel's synchronous library crypto.
+//!
+//! Exposes the one-shot `lib/crypto` primitives — AES-128 single-block ECB,
+//! SHA-256 and HMAC-SHA256 — for use from Rust. They run synchronously in the
+//! calling context with no allocation; the hashes are infallible.
+//!
+//! C headers: [`include/crypto/aes.h`](srctree/include/crypto/aes.h),
+//! [`include/crypto/sha2.h`](srctree/include/crypto/sha2.h).
+
+use crate::{bindings, error::to_result, prelude::*};
+
+/// Size of a SHA-256 / HMAC-SHA256 digest, in bytes.
+pub const SHA256_DIGEST_SIZE: usize = 32;
+/// AES-128 block and key size, in bytes.
+pub const AES128_BLOCK_SIZE: usize = 16;
+
+/// Returns the SHA-256 digest of `data`.
+pub fn sha256(data: &[u8]) -> [u8; SHA256_DIGEST_SIZE] {
+ let mut out = [0u8; SHA256_DIGEST_SIZE];
+ // SAFETY: `data` is valid for `data.len()` reads and `out` is a valid
+ // `SHA256_DIGEST_SIZE`-byte output buffer, as `sha256()` requires.
+ unsafe { bindings::sha256(data.as_ptr(), data.len(), out.as_mut_ptr()) };
+ out
+}
+
+/// Returns `HMAC-SHA256(key, data)`.
+pub fn hmac_sha256(key: &[u8], data: &[u8]) -> [u8; SHA256_DIGEST_SIZE] {
+ let mut out = [0u8; SHA256_DIGEST_SIZE];
+ // SAFETY: `key` and `data` are valid for their respective lengths and `out`
+ // is a valid `SHA256_DIGEST_SIZE`-byte output buffer, as required.
+ unsafe {
+ bindings::hmac_sha256_usingrawkey(
+ key.as_ptr(),
+ key.len(),
+ data.as_ptr(),
+ data.len(),
+ out.as_mut_ptr(),
+ )
+ };
+ out
+}
+
+/// An AES-128 key usable for single-block ECB encryption.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::crypto::Aes128;
+/// let cipher = Aes128::new([0u8; 16]);
+/// let _ct = cipher.encrypt_block(&[0u8; 16])?;
+/// # Ok::<(), Error>(())
+/// ```
+pub struct Aes128([u8; AES128_BLOCK_SIZE]);
+
+impl Aes128 {
+ /// Creates an AES-128 key from 16 raw key bytes.
+ pub fn new(key: [u8; AES128_BLOCK_SIZE]) -> Self {
+ Self(key)
+ }
+
+ /// Encrypts one 16-byte block: returns `AES-128-ECB(key, block)`.
+ pub fn encrypt_block(
+ &self,
+ block: &[u8; AES128_BLOCK_SIZE],
+ ) -> Result<[u8; AES128_BLOCK_SIZE]> {
+ let mut out = [0u8; AES128_BLOCK_SIZE];
+ // SAFETY: `self.0`, `block` and `out` are all valid 16-byte buffers, as
+ // the helper requires.
+ let ret = unsafe {
+ bindings::aes128_encrypt_block(self.0.as_ptr(), block.as_ptr(), out.as_mut_ptr())
+ };
+ to_result(ret)?;
+ Ok(out)
+ }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index b72b2fbe046d..3448fa3a0e9e 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -58,6 +58,7 @@
pub mod cpufreq;
pub mod cpumask;
pub mod cred;
+pub mod crypto;
pub mod debugfs;
pub mod device;
pub mod device_id;
--
2.54.0
^ permalink raw reply related
* [RFC PATCH 2/2] rust: crypto: add RSA public-key encryption via crypto_akcipher
From: Mike Lothian @ 2026-06-17 15:01 UTC (permalink / raw)
To: rust-for-linux
Cc: Mike Lothian, linux-crypto, Eric Biggers, Miguel Ojeda,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
Daniel Almeida, Greg Kroah-Hartman, Yury Norov (NVIDIA),
Asahi Lina, Lorenzo Stoakes, Joel Fernandes, Alexandre Courbot,
FUJITA Tomonori, Krishna Ketan Rai, linux-kernel
In-Reply-To: <20260617150143.2152-1-mike@fireburn.co.uk>
Add a Rust binding for the asynchronous public-key cipher API
(crypto_akcipher), driven synchronously, and a crypto::rsa_pubkey_encrypt()
convenience built on it.
crypto::Akcipher wraps a tfm allocated with crypto_alloc_akcipher(): new()
selects the algorithm by name, set_pub_key() installs the key in the
algorithm's wire format, and encrypt() runs one public-key operation and
blocks until it completes. crypto::rsa_pubkey_encrypt() DER-encodes the
RSAPublicKey { modulus, publicExponent } that the "rsa" transform's
set_pub_key expects and computes out = (input ^ e) mod n; the caller
applies any padding (PKCS#1 v1.5, EME-OAEP, ...) to the input first, and
out is zeroed on any error so it never retains data from a partial
computation.
The request object, scatterlists and the completion wait are static-inline
or on-stack state that cannot be expressed from Rust, and the akcipher
data path needs DMA-capable (kmalloc, not vmap-stack) buffers, so the
canonical synchronous encrypt sequence and a kmalloc bounce live in a
single rust_helper_ shim; crypto_free_akcipher() and
crypto_akcipher_set_pub_key() are exposed through 1:1 shims as they too
are static inlines.
This goes through the crypto_akcipher subsystem rather than the raw MPI
math library, so it composes with any registered akcipher implementation
(including hardware offload) and does not open-code modular exponentiation.
Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
rust/bindings/bindings_helper.h | 1 +
rust/helpers/crypto.c | 70 +++++++++++++
rust/kernel/crypto.rs | 180 +++++++++++++++++++++++++++++++-
3 files changed, 250 insertions(+), 1 deletion(-)
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 14671e1825bb..e6add9754acd 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -28,6 +28,7 @@
*/
#include <linux/hrtimer_types.h>
+#include <crypto/akcipher.h>
#include <crypto/sha2.h>
#include <linux/acpi.h>
diff --git a/rust/helpers/crypto.c b/rust/helpers/crypto.c
index dc9614f6fc8e..6681e9155c84 100644
--- a/rust/helpers/crypto.c
+++ b/rust/helpers/crypto.c
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
#include <crypto/aes.h>
+#include <crypto/akcipher.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <linux/string.h>
/*
@@ -23,3 +27,69 @@ rust_helper_aes128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
memzero_explicit(&enckey, sizeof(enckey));
return 0;
}
+
+/*
+ * crypto_free_akcipher() and crypto_akcipher_set_pub_key() are static inlines,
+ * so they are exposed to Rust through these 1:1 shims (the same convention as
+ * the other rust_helper_ wrappers).
+ */
+__rust_helper void
+rust_helper_crypto_free_akcipher(struct crypto_akcipher *tfm)
+{
+ crypto_free_akcipher(tfm);
+}
+
+__rust_helper int
+rust_helper_crypto_akcipher_set_pub_key(struct crypto_akcipher *tfm,
+ const void *key, unsigned int keylen)
+{
+ return crypto_akcipher_set_pub_key(tfm, key, keylen);
+}
+
+/*
+ * One-shot synchronous public-key encrypt over the akcipher API: encrypts
+ * @src_len bytes at @src into @dst (capacity @dst_len) and returns the
+ * ciphertext length or -errno.
+ *
+ * The request object, the scatterlists and the completion wait are all
+ * static-inline or on-stack state that cannot be expressed from Rust, so the
+ * canonical synchronous akcipher sequence lives here. The akcipher data path
+ * also needs DMA-capable (kmalloc, not vmap-stack) buffers, so @src/@dst are
+ * bounced through kmalloc'd copies; @dst is overwritten only on success and
+ * the bounce buffers are wiped on free.
+ */
+__rust_helper int
+rust_helper_akcipher_encrypt_oneshot(struct crypto_akcipher *tfm,
+ const u8 *src, unsigned int src_len,
+ u8 *dst, unsigned int dst_len)
+{
+ struct akcipher_request *req;
+ struct scatterlist src_sg, dst_sg;
+ DECLARE_CRYPTO_WAIT(wait);
+ u8 *in, *out;
+ int ret = -ENOMEM;
+
+ req = akcipher_request_alloc(tfm, GFP_KERNEL);
+ in = kmemdup(src, src_len, GFP_KERNEL);
+ out = kzalloc(dst_len, GFP_KERNEL);
+ if (!req || !in || !out)
+ goto out;
+
+ sg_init_one(&src_sg, in, src_len);
+ sg_init_one(&dst_sg, out, dst_len);
+ akcipher_request_set_crypt(req, &src_sg, &dst_sg, src_len, dst_len);
+ akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+
+ ret = crypto_wait_req(crypto_akcipher_encrypt(req), &wait);
+ if (ret == 0) {
+ memcpy(dst, out, dst_len);
+ ret = req->dst_len;
+ }
+out:
+ kfree_sensitive(in);
+ kfree_sensitive(out);
+ if (req)
+ akcipher_request_free(req);
+ return ret;
+}
diff --git a/rust/kernel/crypto.rs b/rust/kernel/crypto.rs
index c8f2cb994cfd..0824f6ec81df 100644
--- a/rust/kernel/crypto.rs
+++ b/rust/kernel/crypto.rs
@@ -6,10 +6,21 @@
//! SHA-256 and HMAC-SHA256 — for use from Rust. They run synchronously in the
//! calling context with no allocation; the hashes are infallible.
//!
+//! Also exposes the asynchronous public-key API ([`Akcipher`]) over
+//! `crypto_akcipher`, driven synchronously, and a convenience RSA public-key
+//! primitive built on it for callers that do their own padding (see
+//! [`rsa_pubkey_encrypt`]).
+//!
//! C headers: [`include/crypto/aes.h`](srctree/include/crypto/aes.h),
+//! [`include/crypto/akcipher.h`](srctree/include/crypto/akcipher.h),
//! [`include/crypto/sha2.h`](srctree/include/crypto/sha2.h).
-use crate::{bindings, error::to_result, prelude::*};
+use crate::{
+ bindings,
+ error::{from_err_ptr, to_result},
+ prelude::*,
+};
+use core::ptr::NonNull;
/// Size of a SHA-256 / HMAC-SHA256 digest, in bytes.
pub const SHA256_DIGEST_SIZE: usize = 32;
@@ -75,3 +86,170 @@ pub fn encrypt_block(
Ok(out)
}
}
+
+/// An asynchronous public-key cipher transform (`struct crypto_akcipher`),
+/// driven synchronously.
+///
+/// Wraps a tfm allocated with `crypto_alloc_akcipher()`; the underlying
+/// algorithm (e.g. `"rsa"`) is selected by name in [`Akcipher::new`]. After a
+/// key is installed with [`set_pub_key`](Akcipher::set_pub_key), [`encrypt`]
+/// performs one public-key operation, blocking until the request completes.
+///
+/// [`encrypt`]: Akcipher::encrypt
+///
+/// # Examples
+///
+/// ```
+/// use kernel::crypto::Akcipher;
+/// // `der` is a DER-encoded RSAPublicKey; `pt` is already padded to key size.
+/// # fn f(der: &[u8], pt: &[u8]) -> Result {
+/// let mut tfm = Akcipher::new(c"rsa")?;
+/// tfm.set_pub_key(der)?;
+/// let mut ct = [0u8; 256];
+/// let n = tfm.encrypt(pt, &mut ct)?;
+/// # let _ = n; Ok(())
+/// # }
+/// ```
+pub struct Akcipher(NonNull<bindings::crypto_akcipher>);
+
+// SAFETY: a tfm is a self-contained kernel object with no thread affinity; the
+// synchronous request path takes its own per-call state, so the handle may be
+// moved and used from any thread.
+unsafe impl Send for Akcipher {}
+
+impl Akcipher {
+ /// Allocates a transform for the named akcipher algorithm (e.g. `c"rsa"`).
+ pub fn new(alg_name: &core::ffi::CStr) -> Result<Self> {
+ // SAFETY: `alg_name` is a valid NUL-terminated C string; the call
+ // returns a valid tfm pointer or an `ERR_PTR`.
+ let tfm = from_err_ptr(unsafe {
+ bindings::crypto_alloc_akcipher(alg_name.as_ptr().cast(), 0, 0)
+ })?;
+ Ok(Self(NonNull::new(tfm).ok_or(ENOMEM)?))
+ }
+
+ /// Installs the public key, encoded in the algorithm's expected wire format
+ /// (for `"rsa"`, a DER-encoded `RSAPublicKey`).
+ pub fn set_pub_key(&mut self, key: &[u8]) -> Result {
+ // SAFETY: `self.0` is a live tfm; `key` is valid for `key.len()` reads.
+ to_result(unsafe {
+ bindings::crypto_akcipher_set_pub_key(
+ self.0.as_ptr(),
+ key.as_ptr().cast(),
+ key.len() as u32,
+ )
+ })
+ }
+
+ /// Encrypts `src` into `dst`, returning the ciphertext length. Blocks until
+ /// the operation completes. `dst` must be at least the algorithm's maximum
+ /// output size (the key/modulus size for RSA).
+ pub fn encrypt(&self, src: &[u8], dst: &mut [u8]) -> Result<usize> {
+ // SAFETY: `self.0` is a live tfm; `src`/`dst` are valid for their
+ // lengths. The helper bounces them through kmalloc'd buffers, runs one
+ // synchronous akcipher encrypt, and returns the length or a negative
+ // errno.
+ let ret = unsafe {
+ bindings::akcipher_encrypt_oneshot(
+ self.0.as_ptr(),
+ src.as_ptr(),
+ src.len() as u32,
+ dst.as_mut_ptr(),
+ dst.len() as u32,
+ )
+ };
+ to_result(ret)?;
+ Ok(ret as usize)
+ }
+}
+
+impl Drop for Akcipher {
+ fn drop(&mut self) {
+ // SAFETY: `self.0` was allocated by `crypto_alloc_akcipher()` and is
+ // freed exactly once here.
+ unsafe { bindings::crypto_free_akcipher(self.0.as_ptr()) };
+ }
+}
+
+/// Appends a DER definite length to `out`.
+fn der_len(out: &mut KVec<u8>, len: usize) -> Result {
+ if len < 0x80 {
+ out.push(len as u8, GFP_KERNEL)?;
+ return Ok(());
+ }
+ let mut bytes = [0u8; core::mem::size_of::<usize>()];
+ let mut n = 0;
+ let mut rest = len;
+ while rest > 0 {
+ bytes[n] = rest as u8;
+ rest >>= 8;
+ n += 1;
+ }
+ out.push(0x80 | n as u8, GFP_KERNEL)?;
+ for i in (0..n).rev() {
+ out.push(bytes[i], GFP_KERNEL)?;
+ }
+ Ok(())
+}
+
+/// Appends a DER `INTEGER` carrying the unsigned big-endian magnitude `bytes`.
+fn der_integer(out: &mut KVec<u8>, bytes: &[u8]) -> Result {
+ // Canonicalise: drop leading zero octets, keeping at least one.
+ let mut mag = bytes;
+ while mag.len() > 1 && mag[0] == 0 {
+ mag = &mag[1..];
+ }
+ // A leading zero is needed to keep the value positive if the top bit is set
+ // (or to represent zero when the magnitude is empty).
+ let pad = mag.is_empty() || mag[0] & 0x80 != 0;
+ out.push(0x02, GFP_KERNEL)?;
+ der_len(out, mag.len() + pad as usize)?;
+ if pad {
+ out.push(0x00, GFP_KERNEL)?;
+ }
+ out.extend_from_slice(mag, GFP_KERNEL)?;
+ Ok(())
+}
+
+/// DER-encodes `RSAPublicKey ::= SEQUENCE { modulus INTEGER, publicExponent
+/// INTEGER }` from big-endian `modulus`/`exponent`, as `rsa`'s `set_pub_key`
+/// expects.
+fn der_rsa_pubkey(modulus: &[u8], exponent: &[u8]) -> Result<KVec<u8>> {
+ let mut body = KVec::new();
+ der_integer(&mut body, modulus)?;
+ der_integer(&mut body, exponent)?;
+ let mut der = KVec::new();
+ der.push(0x30, GFP_KERNEL)?;
+ der_len(&mut der, body.len())?;
+ der.extend_from_slice(&body, GFP_KERNEL)?;
+ Ok(der)
+}
+
+/// Computes the RSA public-key operation `out = (input ^ exponent) mod modulus`
+/// through the `crypto_akcipher` `"rsa"` transform.
+///
+/// All buffers are unsigned big-endian. `out` is written fixed-width to exactly
+/// `out.len()` bytes (left zero-padded by the cipher); pass `out.len()` equal to
+/// the modulus size (e.g. 128 for RSA-1024). This is the bare primitive: the
+/// caller applies any padding (PKCS#1 v1.5, EME-OAEP, …) to `input` first.
+///
+/// `input` interpreted as an integer must be less than `modulus`, as RSA
+/// requires; otherwise an error is returned. On any error `out` is zeroed, so
+/// it never retains data from a partial computation.
+pub fn rsa_pubkey_encrypt(
+ modulus: &[u8],
+ exponent: &[u8],
+ input: &[u8],
+ out: &mut [u8],
+) -> Result {
+ let der = der_rsa_pubkey(modulus, exponent)?;
+ let mut tfm = Akcipher::new(c"rsa")?;
+ tfm.set_pub_key(&der)?;
+ match tfm.encrypt(input, out) {
+ Ok(_) => Ok(()),
+ Err(e) => {
+ out.fill(0);
+ Err(e)
+ }
+ }
+}
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v4 2/2] hw_random: timeriomem-rng: add configurable read width and data mask
From: Thomas Gleixner @ 2026-06-17 15:11 UTC (permalink / raw)
To: Jad Keskes, Krzysztof Kozlowski
Cc: Olivia Mackall, Herbert Xu, Rob Herring, Conor Dooley,
Alexander Clouter, linux-crypto, devicetree, linux-kernel,
Jad Keskes
In-Reply-To: <20260617114642.1911191-1-inasj268@gmail.com>
Jad!
On Wed, Jun 17 2026 at 12:46, Jad Keskes wrote:
> The TODO for supporting read sizes other than 32 bits and masking has
> been sitting in this driver since 2009. Implement it.
>
> Add reg-io-width (1, 2, or 4 bytes) and mask support. The read loop
> dispatches on width using readb/readw/readl so a configured 1-byte
> access doesn't trigger a bus error on hardware that rejects 32-bit
> reads to that address. The mask is ANDed with the value before storing.
>
> These are platform properties, not runtime policy -- width depends on
> SoC integration, mask reflects which output bits carry entropy.
>
> The alignment check in probe is updated to verify the resource is
> aligned to the configured width instead of hardcoding 4-byte alignment.
So this is the 4th version of the same thing within 24 hours and without
any explanation what the difference between v1/2/3/4 is.
Please stop this frenzy and send out new versions only if there is a
good and documented reason. Otherwise give people the time to review
your patch. All of this is documented in Documentation/process.
^ permalink raw reply
* Re: [RFC PATCH 0/2] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA
From: Miguel Ojeda @ 2026-06-17 15:13 UTC (permalink / raw)
To: Mike Lothian
Cc: rust-for-linux, linux-crypto, Eric Biggers, Herbert Xu,
David S. Miller, Ard Biesheuvel, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, linux-kernel
In-Reply-To: <20260617150143.2152-1-mike@fireburn.co.uk>
On Wed, Jun 17, 2026 at 5:01 PM Mike Lothian <mike@fireburn.co.uk> wrote:
>
> Both were factored out of an out-of-tree in-kernel Rust DisplayLink DL3
> dock driver (which needs SHA/HMAC/AES for HDCP 2.2 and RSA for the AKE),
> but the module is generic. Compile-tested in-tree against drm-next.
Same question as in the other patch series you just sent: is this only
expected for an out-of-tree driver? Maintainers generally cannot take
dead code, unless there is a good justification behind it -- is the
driver expected to land upstream? Do you have a link to the code?
Thanks!
Cheers,
Miguel
^ permalink raw reply
* Re: [RFC PATCH 0/2] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA
From: Mike Lothian @ 2026-06-17 15:19 UTC (permalink / raw)
To: Miguel Ojeda
Cc: rust-for-linux, linux-crypto, Eric Biggers, Herbert Xu,
David S. Miller, Ard Biesheuvel, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, linux-kernel
In-Reply-To: <CANiq72=me+GZCOW1H5FtfE-b1OY5FXN0yT2S3661vP+S0EDVwQ@mail.gmail.com>
On Wed, 17 Jun 2026 at 16:13, Miguel Ojeda
<miguel.ojeda.sandonis@gmail.com> wrote:
>
> On Wed, Jun 17, 2026 at 5:01 PM Mike Lothian <mike@fireburn.co.uk> wrote:
> >
> > Both were factored out of an out-of-tree in-kernel Rust DisplayLink DL3
> > dock driver (which needs SHA/HMAC/AES for HDCP 2.2 and RSA for the AKE),
> > but the module is generic. Compile-tested in-tree against drm-next.
>
> Same question as in the other patch series you just sent: is this only
> expected for an out-of-tree driver? Maintainers generally cannot take
> dead code, unless there is a good justification behind it -- is the
> driver expected to land upstream? Do you have a link to the code?
>
> Thanks!
>
> Cheers,
> Miguel
Hi
I've just posted it
https://lore.kernel.org/r/20260617151249.2937-1-mike@fireburn.co.uk
I'd like it upstream if I get it working
Chers
Mike
^ permalink raw reply
* Re: [PATCH RESEND 1/6] sock: add sock_kzalloc helper
From: Thorsten Blum @ 2026-06-17 15:36 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Herbert Xu, David S. Miller, Eric Dumazet, Kuniyuki Iwashima,
Paolo Abeni, Willem de Bruijn, Simon Horman, linux-crypto,
linux-kernel, netdev
In-Reply-To: <20260615091555.4af017aa@kernel.org>
On Mon, Jun 15, 2026 at 09:15:55AM -0700, Jakub Kicinski wrote:
> On Sun, 14 Jun 2026 17:32:12 +0200 Thorsten Blum wrote:
> > Gentle ping? Patch 1/6 still needs an ack from netdev maintainers.
>
> Perhaps other maintainers shared my feeling that this is a waste of
> time.
Could you elaborate on why sock_kzfree_s() is okay, but sock_kzalloc()
is not? Both are small, socket-specific zeroing helpers.
sock_kzalloc() has the same number of call sites as sock_kzfree_s(), and
it could also be used in net/ipv6/exthdrs.c in ipv6_renew_options().
Thanks,
Thorsten
^ permalink raw reply
* Re: [PATCH v3] lib/raid/xor: x86: Add AVX-512 optimized xor_gen()
From: Eric Biggers @ 2026-06-17 15:44 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Andrew Morton, linux-kernel, linux-crypto, x86, David Laight,
linux-raid
In-Reply-To: <20260617055653.GB19218@lst.de>
On Wed, Jun 17, 2026 at 07:56:53AM +0200, Christoph Hellwig wrote:
> Can use the xor: prefix used for all other commits to lib/raid/xor?
>
> > Benchmark on AMD Ryzen 9 9950X (Zen 5):
> >
> > src_cnt avx avx512 Improvement
> > ======= ========== ========== ===========
> > 1 56353 MB/s 75388 MB/s 33%
> > 2 54274 MB/s 68409 MB/s 26%
> > 3 44649 MB/s 64042 MB/s 43%
> > 4 41315 MB/s 55002 MB/s 33%
>
> On my Zen 5 mobile (AMD Ryzen AI 7 PRO 350) both the existing
> AVX2 and this AVX512 code give numbers in the 200+ GB/s range. Not
> sure if is just the different benchmarking or something else going on.
I used lib/raid/xor/xor-core.c which measures the throughput of parity
data generated, whereas your proposed xor_benchmark() in xor_kunit
measures the throughput of source data consumed. I don't know which
makes more sense, but we should make them consistent with each other.
> FYI, one or 2 sources are basically useless as they RAID5 configs
> that have no benefits over simple mirroring and thus the numbers
> aren't too interesting.
>
> > +DO_XOR_BLOCKS(avx512_inner, xor_avx512_2, xor_avx512_3, xor_avx512_4,
> > + xor_avx512_5);
>
> Is there really much of a benefit of doing the historic DO_XOR_BLOCKS
> vs doing the loop manually? Especially as the common cases for a
> modern RAID will usually loop over more disks than this was built
> for. I.e., in practice one or two source buffers only happen at the
> end of a loop over more disks.
There's not really a way out of unrolling by source buffer count, as
otherwise the pointers would continuously have to be reloaded into
registers. That's why your proposal was so slow (see the numbers I gave
in https://lore.kernel.org/linux-crypto/20260612055933.GA6675@sol/ ).
It could be something different from 2-5 specifically, or open-coded
instead of using the macro if that's all you're asking for, but at a
high level the unrolling by source buffer count does seem to be needed.
- Eric
^ permalink raw reply
* [PATCH v3 0/8] crypto: qce - Fix crypto self-test failures
From: Bartosz Golaszewski @ 2026-06-17 15:49 UTC (permalink / raw)
To: Thara Gopinath, Herbert Xu, David S. Miller, Stanimir Varbanov,
Eneas U de Queiroz, Kuldeep Singh, Eric Biggers
Cc: linux-crypto, linux-arm-msm, linux-kernel, brgl,
Bartosz Golaszewski, stable
This extends the initial submission from Kuldeep.
The QCE hardware crypto engine has several limitations that cause it to
produce incorrect results or stall on certain inputs. This series fixes
several bugs and adds workaround allowing the deiver to pass crypto
self-tests.
The failures addressed are:
- HMAC self-test failures for empty messages
- AES-XTS returning success on zero-length input (should be -EINVAL)
- AES-CTR: partial final block causes the engine to stall, output IV
derivation was incorrect
- AES-XTS with key1 == key2 is not supported by the CE
- AES-CCM: partial final block and fragmented payload both stall the
engine
All fixes were tested on an SM8650 QRD board with
CONFIG_CRYPTO_SELFTESTS=y and CONFIG_CRYPTO_SELFTESTS_FULL=y.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
Changes in v3:
- Remove even more algorithms and dead code in patch 1/8
- Link to v2: https://patch.msgid.link/20260615-qce-fix-self-tests-v2-0-dc911f1aad42@oss.qualcomm.com
Changes in v2:
- Add fixes for the full suite of crypto self-tests
- Add Fixes and Cc tags
- Link to v1: https://patch.msgid.link/20260610-qce_selftest_fix-v1-0-1b0504783a46@oss.qualcomm.com/
---
Bartosz Golaszewski (6):
crypto: qce - Remove unsafe/deprecated algorithms
crypto: qce - Fix HMAC self-test failures for empty messages
crypto: qce - Reject empty messages for AES-XTS
crypto: qce - Use a fallback for AES-CTR with a partial final block
crypto: qce - Use a fallback for CCM with a partial final block
crypto: qce - Use fallback for CCM with a fragmented payload
Kuldeep Singh (2):
crypto: qce - Fix CTR-AES for partial block requests
crypto: qce - Fix xts-aes-qce for weak keys
drivers/crypto/qce/aead.c | 88 ++++++++++----------------
drivers/crypto/qce/cipher.h | 1 +
drivers/crypto/qce/common.c | 40 +++---------
drivers/crypto/qce/common.h | 13 +---
drivers/crypto/qce/regs-v5.h | 4 --
drivers/crypto/qce/sha.c | 114 +++++++++++++++++++++++++---------
drivers/crypto/qce/sha.h | 2 +-
drivers/crypto/qce/skcipher.c | 141 ++++++++++++------------------------------
8 files changed, 166 insertions(+), 237 deletions(-)
---
base-commit: 7f5e2941e7dccc9dfaaa23d0548a40039772a284
change-id: 20260610-qce-fix-self-tests-492ffd2ef955
Best regards,
--
Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
^ permalink raw reply
* [PATCH v3 1/8] crypto: qce - Remove unsafe/deprecated algorithms
From: Bartosz Golaszewski @ 2026-06-17 15:49 UTC (permalink / raw)
To: Thara Gopinath, Herbert Xu, David S. Miller, Stanimir Varbanov,
Eneas U de Queiroz, Kuldeep Singh, Eric Biggers
Cc: linux-crypto, linux-arm-msm, linux-kernel, brgl,
Bartosz Golaszewski, stable
In-Reply-To: <20260617-qce-fix-self-tests-v3-0-ecc2b4dedcfd@oss.qualcomm.com>
Remove algorithms that are either unsafe or deprecated and have no
in-kernel users that cannot be served by the ARM CE implementations.
AES-ECB reveals plaintext patterns (identical plaintext blocks produce
identical ciphertext blocks) and should not be exposed as a hardware-
accelerated primitive. DES, Triple DES and HMAC-SHA1 have been
deprecated for years.
Remove sha1, ecb(aes), ecb(des), cbc(des), ecb(des3_ede), cbc(des3_ede),
hmac(sha1) and all AEAD variants built on these primitives as well as
authenc(hmac(sha256),cbc(des)). Also clean up the - now dead - code,
flags and constants.
Cc: stable@vger.kernel.org
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
drivers/crypto/qce/aead.c | 56 +------------------------
drivers/crypto/qce/common.c | 40 ++++--------------
drivers/crypto/qce/common.h | 13 +-----
drivers/crypto/qce/regs-v5.h | 4 --
drivers/crypto/qce/sha.c | 30 +-------------
drivers/crypto/qce/sha.h | 1 -
drivers/crypto/qce/skcipher.c | 95 +------------------------------------------
7 files changed, 13 insertions(+), 226 deletions(-)
diff --git a/drivers/crypto/qce/aead.c b/drivers/crypto/qce/aead.c
index 03b8042da9a1b4aebdc775ad8ab912abc7b2383d..336614a11377e0be246817da584296124f4de5d8 100644
--- a/drivers/crypto/qce/aead.c
+++ b/drivers/crypto/qce/aead.c
@@ -9,8 +9,6 @@
#include <crypto/gcm.h>
#include <crypto/authenc.h>
#include <crypto/internal/aead.h>
-#include <crypto/internal/des.h>
-#include <crypto/sha1.h>
#include <crypto/sha2.h>
#include <crypto/scatterwalk.h>
#include "aead.h"
@@ -592,7 +590,6 @@ static int qce_aead_setkey(struct crypto_aead *tfm, const u8 *key, unsigned int
struct qce_aead_ctx *ctx = crypto_aead_ctx(tfm);
struct crypto_authenc_keys authenc_keys;
unsigned long flags = to_aead_tmpl(tfm)->alg_flags;
- u32 _key[6];
int err;
err = crypto_authenc_extractkeys(&authenc_keys, key, keylen);
@@ -603,26 +600,7 @@ static int qce_aead_setkey(struct crypto_aead *tfm, const u8 *key, unsigned int
authenc_keys.authkeylen > QCE_MAX_KEY_SIZE)
return -EINVAL;
- if (IS_DES(flags)) {
- err = verify_aead_des_key(tfm, authenc_keys.enckey, authenc_keys.enckeylen);
- if (err)
- return err;
- } else if (IS_3DES(flags)) {
- err = verify_aead_des3_key(tfm, authenc_keys.enckey, authenc_keys.enckeylen);
- if (err)
- return err;
- /*
- * The crypto engine does not support any two keys
- * being the same for triple des algorithms. The
- * verify_skcipher_des3_key does not check for all the
- * below conditions. Schedule fallback in this case.
- */
- memcpy(_key, authenc_keys.enckey, DES3_EDE_KEY_SIZE);
- if (!((_key[0] ^ _key[2]) | (_key[1] ^ _key[3])) ||
- !((_key[2] ^ _key[4]) | (_key[3] ^ _key[5])) ||
- !((_key[0] ^ _key[4]) | (_key[1] ^ _key[5])))
- ctx->need_fallback = true;
- } else if (IS_AES(flags)) {
+ if (IS_AES(flags)) {
/* No random key sizes */
if (authenc_keys.enckeylen != AES_KEYSIZE_128 &&
authenc_keys.enckeylen != AES_KEYSIZE_192 &&
@@ -693,38 +671,6 @@ struct qce_aead_def {
};
static const struct qce_aead_def aead_def[] = {
- {
- .flags = QCE_ALG_DES | QCE_MODE_CBC | QCE_HASH_SHA1_HMAC,
- .name = "authenc(hmac(sha1),cbc(des))",
- .drv_name = "authenc-hmac-sha1-cbc-des-qce",
- .blocksize = DES_BLOCK_SIZE,
- .ivsize = DES_BLOCK_SIZE,
- .maxauthsize = SHA1_DIGEST_SIZE,
- },
- {
- .flags = QCE_ALG_3DES | QCE_MODE_CBC | QCE_HASH_SHA1_HMAC,
- .name = "authenc(hmac(sha1),cbc(des3_ede))",
- .drv_name = "authenc-hmac-sha1-cbc-3des-qce",
- .blocksize = DES3_EDE_BLOCK_SIZE,
- .ivsize = DES3_EDE_BLOCK_SIZE,
- .maxauthsize = SHA1_DIGEST_SIZE,
- },
- {
- .flags = QCE_ALG_DES | QCE_MODE_CBC | QCE_HASH_SHA256_HMAC,
- .name = "authenc(hmac(sha256),cbc(des))",
- .drv_name = "authenc-hmac-sha256-cbc-des-qce",
- .blocksize = DES_BLOCK_SIZE,
- .ivsize = DES_BLOCK_SIZE,
- .maxauthsize = SHA256_DIGEST_SIZE,
- },
- {
- .flags = QCE_ALG_3DES | QCE_MODE_CBC | QCE_HASH_SHA256_HMAC,
- .name = "authenc(hmac(sha256),cbc(des3_ede))",
- .drv_name = "authenc-hmac-sha256-cbc-3des-qce",
- .blocksize = DES3_EDE_BLOCK_SIZE,
- .ivsize = DES3_EDE_BLOCK_SIZE,
- .maxauthsize = SHA256_DIGEST_SIZE,
- },
{
.flags = QCE_ALG_AES | QCE_MODE_CBC | QCE_HASH_SHA256_HMAC,
.name = "authenc(hmac(sha256),cbc(aes))",
diff --git a/drivers/crypto/qce/common.c b/drivers/crypto/qce/common.c
index 54a78a57f63028f01870a3edeb8e390f523bb190..a1c972115c700448cd17713b7615a5e5f0c377bf 100644
--- a/drivers/crypto/qce/common.c
+++ b/drivers/crypto/qce/common.c
@@ -8,7 +8,6 @@
#include <linux/interrupt.h>
#include <linux/types.h>
#include <crypto/scatterwalk.h>
-#include <crypto/sha1.h>
#include <crypto/sha2.h>
#include "cipher.h"
@@ -115,18 +114,16 @@ static u32 qce_auth_cfg(unsigned long flags, u32 key_size, u32 auth_size)
cfg |= AUTH_KEY_SZ_AES256 << AUTH_KEY_SIZE_SHIFT;
}
- if (IS_SHA1(flags) || IS_SHA1_HMAC(flags))
- cfg |= AUTH_SIZE_SHA1 << AUTH_SIZE_SHIFT;
- else if (IS_SHA256(flags) || IS_SHA256_HMAC(flags))
+ if (IS_SHA256(flags) || IS_SHA256_HMAC(flags))
cfg |= AUTH_SIZE_SHA256 << AUTH_SIZE_SHIFT;
else if (IS_CMAC(flags))
cfg |= AUTH_SIZE_ENUM_16_BYTES << AUTH_SIZE_SHIFT;
else if (IS_CCM(flags))
cfg |= (auth_size - 1) << AUTH_SIZE_SHIFT;
- if (IS_SHA1(flags) || IS_SHA256(flags))
+ if (IS_SHA256(flags))
cfg |= AUTH_MODE_HASH << AUTH_MODE_SHIFT;
- else if (IS_SHA1_HMAC(flags) || IS_SHA256_HMAC(flags))
+ else if (IS_SHA256_HMAC(flags))
cfg |= AUTH_MODE_HMAC << AUTH_MODE_SHIFT;
else if (IS_CCM(flags))
cfg |= AUTH_MODE_CCM << AUTH_MODE_SHIFT;
@@ -191,7 +188,7 @@ static int qce_setup_regs_ahash(struct crypto_async_request *async_req)
else
qce_cpu_to_be32p_array(auth, rctx->digest, digestsize);
- iv_words = (IS_SHA1(rctx->flags) || IS_SHA1_HMAC(rctx->flags)) ? 5 : 8;
+ iv_words = 8;
qce_write_array(qce, REG_AUTH_IV0, (u32 *)auth, iv_words);
if (rctx->first_blk)
@@ -243,14 +240,6 @@ static u32 qce_encr_cfg(unsigned long flags, u32 aes_key_size)
if (IS_AES(flags))
cfg |= ENCR_ALG_AES << ENCR_ALG_SHIFT;
- else if (IS_DES(flags) || IS_3DES(flags))
- cfg |= ENCR_ALG_DES << ENCR_ALG_SHIFT;
-
- if (IS_DES(flags))
- cfg |= ENCR_KEY_SZ_DES << ENCR_KEY_SZ_SHIFT;
-
- if (IS_3DES(flags))
- cfg |= ENCR_KEY_SZ_3DES << ENCR_KEY_SZ_SHIFT;
switch (flags & QCE_MODE_MASK) {
case QCE_MODE_ECB:
@@ -340,13 +329,7 @@ static int qce_setup_regs_skcipher(struct crypto_async_request *async_req)
encr_cfg = qce_encr_cfg(flags, keylen);
- if (IS_DES(flags)) {
- enciv_words = 2;
- enckey_words = 2;
- } else if (IS_3DES(flags)) {
- enciv_words = 2;
- enckey_words = 6;
- } else if (IS_AES(flags)) {
+ if (IS_AES(flags)) {
if (IS_XTS(flags))
qce_xtskey(qce, ctx->enc_key, ctx->enc_keylen,
rctx->cryptlen);
@@ -393,10 +376,6 @@ static int qce_setup_regs_skcipher(struct crypto_async_request *async_req)
#endif
#ifdef CONFIG_CRYPTO_DEV_QCE_AEAD
-static const u32 std_iv_sha1[SHA256_DIGEST_SIZE / sizeof(u32)] = {
- SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4, 0, 0, 0
-};
-
static const u32 std_iv_sha256[SHA256_DIGEST_SIZE / sizeof(u32)] = {
SHA256_H0, SHA256_H1, SHA256_H2, SHA256_H3,
SHA256_H4, SHA256_H5, SHA256_H6, SHA256_H7
@@ -473,13 +452,8 @@ static int qce_setup_regs_aead(struct crypto_async_request *async_req)
/* Write initial authentication IV only for HMAC algorithms */
if (IS_SHA_HMAC(rctx->flags)) {
/* Write default authentication iv */
- if (IS_SHA1_HMAC(rctx->flags)) {
- auth_ivsize = SHA1_DIGEST_SIZE;
- memcpy(authiv, std_iv_sha1, auth_ivsize);
- } else if (IS_SHA256_HMAC(rctx->flags)) {
- auth_ivsize = SHA256_DIGEST_SIZE;
- memcpy(authiv, std_iv_sha256, auth_ivsize);
- }
+ auth_ivsize = SHA256_DIGEST_SIZE;
+ memcpy(authiv, std_iv_sha256, auth_ivsize);
authiv_words = auth_ivsize / sizeof(u32);
qce_write_array(qce, REG_AUTH_IV0, (u32 *)authiv, authiv_words);
} else if (IS_CCM(rctx->flags)) {
diff --git a/drivers/crypto/qce/common.h b/drivers/crypto/qce/common.h
index 02e63ad9f24557c2238caa70b0ec521d49da4f13..8f0ab4d9fa1e11ab123edb616b43b82541275f4e 100644
--- a/drivers/crypto/qce/common.h
+++ b/drivers/crypto/qce/common.h
@@ -33,14 +33,10 @@
#define QCE_MAX_ALIGN_SIZE 64
/* cipher algorithms */
-#define QCE_ALG_DES BIT(0)
-#define QCE_ALG_3DES BIT(1)
#define QCE_ALG_AES BIT(2)
/* hash and hmac algorithms */
-#define QCE_HASH_SHA1 BIT(3)
#define QCE_HASH_SHA256 BIT(4)
-#define QCE_HASH_SHA1_HMAC BIT(5)
#define QCE_HASH_SHA256_HMAC BIT(6)
#define QCE_HASH_AES_CMAC BIT(7)
@@ -58,18 +54,13 @@
#define QCE_ENCRYPT BIT(30)
#define QCE_DECRYPT BIT(31)
-#define IS_DES(flags) (flags & QCE_ALG_DES)
-#define IS_3DES(flags) (flags & QCE_ALG_3DES)
#define IS_AES(flags) (flags & QCE_ALG_AES)
-#define IS_SHA1(flags) (flags & QCE_HASH_SHA1)
#define IS_SHA256(flags) (flags & QCE_HASH_SHA256)
-#define IS_SHA1_HMAC(flags) (flags & QCE_HASH_SHA1_HMAC)
#define IS_SHA256_HMAC(flags) (flags & QCE_HASH_SHA256_HMAC)
#define IS_CMAC(flags) (flags & QCE_HASH_AES_CMAC)
-#define IS_SHA(flags) (IS_SHA1(flags) || IS_SHA256(flags))
-#define IS_SHA_HMAC(flags) \
- (IS_SHA1_HMAC(flags) || IS_SHA256_HMAC(flags))
+#define IS_SHA(flags) IS_SHA256(flags)
+#define IS_SHA_HMAC(flags) IS_SHA256_HMAC(flags)
#define IS_CBC(mode) (mode & QCE_MODE_CBC)
#define IS_ECB(mode) (mode & QCE_MODE_ECB)
diff --git a/drivers/crypto/qce/regs-v5.h b/drivers/crypto/qce/regs-v5.h
index d59ed279890621a8e2e6f4cdb20692dbf39f1461..431a7db1a4e72188c2ccca094bda18a03f30d3d2 100644
--- a/drivers/crypto/qce/regs-v5.h
+++ b/drivers/crypto/qce/regs-v5.h
@@ -203,7 +203,6 @@
#define AUTH_SIZE_SHIFT 9
#define AUTH_SIZE_MASK GENMASK(13, 9)
-#define AUTH_SIZE_SHA1 0
#define AUTH_SIZE_SHA256 1
#define AUTH_SIZE_ENUM_1_BYTES 0
#define AUTH_SIZE_ENUM_2_BYTES 1
@@ -284,15 +283,12 @@
#define ENCR_KEY_SZ_SHIFT 3
#define ENCR_KEY_SZ_MASK GENMASK(5, 3)
-#define ENCR_KEY_SZ_DES 0
-#define ENCR_KEY_SZ_3DES 1
#define ENCR_KEY_SZ_AES128 0
#define ENCR_KEY_SZ_AES256 2
#define ENCR_ALG_SHIFT 0
#define ENCR_ALG_MASK GENMASK(2, 0)
#define ENCR_ALG_NONE 0
-#define ENCR_ALG_DES 1
#define ENCR_ALG_AES 2
#define ENCR_ALG_KASUMI 4
#define ENCR_ALG_SNOW_3G 5
diff --git a/drivers/crypto/qce/sha.c b/drivers/crypto/qce/sha.c
index a3a1a205aaf8559a04809936e2a3b7d564c16c53..0a3f88aaf5169ea7b47a549bbc10ea87d3ae7a2b 100644
--- a/drivers/crypto/qce/sha.c
+++ b/drivers/crypto/qce/sha.c
@@ -25,10 +25,6 @@ struct qce_sha_saved_state {
static LIST_HEAD(ahash_algs);
-static const u32 std_iv_sha1[SHA256_DIGEST_SIZE / sizeof(u32)] = {
- SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4, 0, 0, 0
-};
-
static const u32 std_iv_sha256[SHA256_DIGEST_SIZE / sizeof(u32)] = {
SHA256_H0, SHA256_H1, SHA256_H2, SHA256_H3,
SHA256_H4, SHA256_H5, SHA256_H6, SHA256_H7
@@ -349,9 +345,7 @@ static int qce_ahash_hmac_setkey(struct crypto_ahash *tfm, const u8 *key,
return 0;
}
- if (digestsize == SHA1_DIGEST_SIZE)
- alg_name = "sha1-qce";
- else if (digestsize == SHA256_DIGEST_SIZE)
+ if (digestsize == SHA256_DIGEST_SIZE)
alg_name = "sha256-qce";
else
return -EINVAL;
@@ -412,15 +406,6 @@ struct qce_ahash_def {
};
static const struct qce_ahash_def ahash_def[] = {
- {
- .flags = QCE_HASH_SHA1,
- .name = "sha1",
- .drv_name = "sha1-qce",
- .digestsize = SHA1_DIGEST_SIZE,
- .blocksize = SHA1_BLOCK_SIZE,
- .statesize = sizeof(struct qce_sha_saved_state),
- .std_iv = std_iv_sha1,
- },
{
.flags = QCE_HASH_SHA256,
.name = "sha256",
@@ -430,15 +415,6 @@ static const struct qce_ahash_def ahash_def[] = {
.statesize = sizeof(struct qce_sha_saved_state),
.std_iv = std_iv_sha256,
},
- {
- .flags = QCE_HASH_SHA1_HMAC,
- .name = "hmac(sha1)",
- .drv_name = "hmac-sha1-qce",
- .digestsize = SHA1_DIGEST_SIZE,
- .blocksize = SHA1_BLOCK_SIZE,
- .statesize = sizeof(struct qce_sha_saved_state),
- .std_iv = std_iv_sha1,
- },
{
.flags = QCE_HASH_SHA256_HMAC,
.name = "hmac(sha256)",
@@ -476,9 +452,7 @@ static int qce_ahash_register_one(const struct qce_ahash_def *def,
alg->halg.digestsize = def->digestsize;
alg->halg.statesize = def->statesize;
- if (IS_SHA1(def->flags))
- tmpl->hash_zero = sha1_zero_message_hash;
- else if (IS_SHA256(def->flags))
+ if (IS_SHA256(def->flags))
tmpl->hash_zero = sha256_zero_message_hash;
base = &alg->halg.base;
diff --git a/drivers/crypto/qce/sha.h b/drivers/crypto/qce/sha.h
index a22695361f1654cc94325ec5d886a158fa4bfb9c..cb822fc334dc187cf1c66e2a332822a596ebcef3 100644
--- a/drivers/crypto/qce/sha.h
+++ b/drivers/crypto/qce/sha.h
@@ -7,7 +7,6 @@
#define _SHA_H_
#include <crypto/scatterwalk.h>
-#include <crypto/sha1.h>
#include <crypto/sha2.h>
#include "common.h"
diff --git a/drivers/crypto/qce/skcipher.c b/drivers/crypto/qce/skcipher.c
index 1fef315a7105c869e7fc6a60719087b721e78bb3..58a6c8e333784af73cd4340814046f04405c69e7 100644
--- a/drivers/crypto/qce/skcipher.c
+++ b/drivers/crypto/qce/skcipher.c
@@ -11,7 +11,6 @@
#include <linux/types.h>
#include <linux/errno.h>
#include <crypto/aes.h>
-#include <crypto/internal/des.h>
#include <crypto/internal/skcipher.h>
#include "cipher.h"
@@ -209,51 +208,6 @@ static int qce_skcipher_setkey(struct crypto_skcipher *ablk, const u8 *key,
return ret;
}
-static int qce_des_setkey(struct crypto_skcipher *ablk, const u8 *key,
- unsigned int keylen)
-{
- struct qce_cipher_ctx *ctx = crypto_skcipher_ctx(ablk);
- int err;
-
- err = verify_skcipher_des_key(ablk, key);
- if (err)
- return err;
-
- ctx->enc_keylen = keylen;
- memcpy(ctx->enc_key, key, keylen);
- return 0;
-}
-
-static int qce_des3_setkey(struct crypto_skcipher *ablk, const u8 *key,
- unsigned int keylen)
-{
- struct qce_cipher_ctx *ctx = crypto_skcipher_ctx(ablk);
- u32 _key[6];
- int err;
-
- err = verify_skcipher_des3_key(ablk, key);
- if (err)
- return err;
-
- /*
- * The crypto engine does not support any two keys
- * being the same for triple des algorithms. The
- * verify_skcipher_des3_key does not check for all the
- * below conditions. Return -ENOKEY in case any two keys
- * are the same. Revisit to see if a fallback cipher
- * is needed to handle this condition.
- */
- memcpy(_key, key, DES3_EDE_KEY_SIZE);
- if (!((_key[0] ^ _key[2]) | (_key[1] ^ _key[3])) ||
- !((_key[2] ^ _key[4]) | (_key[3] ^ _key[5])) ||
- !((_key[0] ^ _key[4]) | (_key[1] ^ _key[5])))
- return -ENOKEY;
-
- ctx->enc_keylen = keylen;
- memcpy(ctx->enc_key, key, keylen);
- return 0;
-}
-
static int qce_skcipher_crypt(struct skcipher_request *req, int encrypt)
{
struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
@@ -359,15 +313,6 @@ struct qce_skcipher_def {
};
static const struct qce_skcipher_def skcipher_def[] = {
- {
- .flags = QCE_ALG_AES | QCE_MODE_ECB,
- .name = "ecb(aes)",
- .drv_name = "ecb-aes-qce",
- .blocksize = AES_BLOCK_SIZE,
- .ivsize = 0,
- .min_keysize = AES_MIN_KEY_SIZE,
- .max_keysize = AES_MAX_KEY_SIZE,
- },
{
.flags = QCE_ALG_AES | QCE_MODE_CBC,
.name = "cbc(aes)",
@@ -396,42 +341,6 @@ static const struct qce_skcipher_def skcipher_def[] = {
.min_keysize = AES_MIN_KEY_SIZE * 2,
.max_keysize = AES_MAX_KEY_SIZE * 2,
},
- {
- .flags = QCE_ALG_DES | QCE_MODE_ECB,
- .name = "ecb(des)",
- .drv_name = "ecb-des-qce",
- .blocksize = DES_BLOCK_SIZE,
- .ivsize = 0,
- .min_keysize = DES_KEY_SIZE,
- .max_keysize = DES_KEY_SIZE,
- },
- {
- .flags = QCE_ALG_DES | QCE_MODE_CBC,
- .name = "cbc(des)",
- .drv_name = "cbc-des-qce",
- .blocksize = DES_BLOCK_SIZE,
- .ivsize = DES_BLOCK_SIZE,
- .min_keysize = DES_KEY_SIZE,
- .max_keysize = DES_KEY_SIZE,
- },
- {
- .flags = QCE_ALG_3DES | QCE_MODE_ECB,
- .name = "ecb(des3_ede)",
- .drv_name = "ecb-3des-qce",
- .blocksize = DES3_EDE_BLOCK_SIZE,
- .ivsize = 0,
- .min_keysize = DES3_EDE_KEY_SIZE,
- .max_keysize = DES3_EDE_KEY_SIZE,
- },
- {
- .flags = QCE_ALG_3DES | QCE_MODE_CBC,
- .name = "cbc(des3_ede)",
- .drv_name = "cbc-3des-qce",
- .blocksize = DES3_EDE_BLOCK_SIZE,
- .ivsize = DES3_EDE_BLOCK_SIZE,
- .min_keysize = DES3_EDE_KEY_SIZE,
- .max_keysize = DES3_EDE_KEY_SIZE,
- },
};
static int qce_skcipher_register_one(const struct qce_skcipher_def *def,
@@ -455,9 +364,7 @@ static int qce_skcipher_register_one(const struct qce_skcipher_def *def,
alg->ivsize = def->ivsize;
alg->min_keysize = def->min_keysize;
alg->max_keysize = def->max_keysize;
- alg->setkey = IS_3DES(def->flags) ? qce_des3_setkey :
- IS_DES(def->flags) ? qce_des_setkey :
- qce_skcipher_setkey;
+ alg->setkey = qce_skcipher_setkey;
alg->encrypt = qce_skcipher_encrypt;
alg->decrypt = qce_skcipher_decrypt;
--
2.47.3
^ permalink raw reply related
* [PATCH v3 2/8] crypto: qce - Fix HMAC self-test failures for empty messages
From: Bartosz Golaszewski @ 2026-06-17 15:49 UTC (permalink / raw)
To: Thara Gopinath, Herbert Xu, David S. Miller, Stanimir Varbanov,
Eneas U de Queiroz, Kuldeep Singh, Eric Biggers
Cc: linux-crypto, linux-arm-msm, linux-kernel, brgl,
Bartosz Golaszewski, stable
In-Reply-To: <20260617-qce-fix-self-tests-v3-0-ecc2b4dedcfd@oss.qualcomm.com>
BAM DMA cannot process zero-length transfers. For plain hashes this is
handled by returning the precomputed hash of the empty message
(tmpl->hash_zero), but for keyed HMAC the result depends on the key and
cannot be a constant. As a result, hmac(sha256) produced an incorrect
digest for an empty message and the crypto self-tests failed.
Allocate a software fallback ahash for the HMAC transforms and use it to
compute the digest whenever the message is empty (in both the .final()
and .digest() paths). The fallback is allocated in a dedicated cra_init
for the HMAC algorithms and is excluded from matching the crypto engine's
own algorithm to avoid recursion. It is kept keyed in sync with the
hardware transform in .setkey().
Cc: stable@vger.kernel.org
Fixes: ec8f5d8f6f76 ("crypto: qce - Qualcomm crypto engine driver")
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
drivers/crypto/qce/sha.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++-
drivers/crypto/qce/sha.h | 1 +
2 files changed, 84 insertions(+), 1 deletion(-)
diff --git a/drivers/crypto/qce/sha.c b/drivers/crypto/qce/sha.c
index 0a3f88aaf5169ea7b47a549bbc10ea87d3ae7a2b..d4d0bf88dea6bf1c58ee103cdccbbbfc266110e1 100644
--- a/drivers/crypto/qce/sha.c
+++ b/drivers/crypto/qce/sha.c
@@ -270,6 +270,36 @@ static int qce_ahash_update(struct ahash_request *req)
return qce->async_req_enqueue(tmpl->qce, &req->base);
}
+/*
+ * BAM DMA cannot handle zero-length transfers. For plain hashes the result of
+ * an empty message is a known constant (hash_zero), for keyed HMAC it depends
+ * on the key, so compute it with the software fallback.
+ */
+static int qce_ahash_hmac_zero(struct ahash_request *req)
+{
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct qce_sha_ctx *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm));
+ struct ahash_request *subreq;
+ struct crypto_wait wait;
+ struct scatterlist sg;
+ int ret;
+
+ subreq = ahash_request_alloc(ctx->fallback, GFP_ATOMIC);
+ if (!subreq)
+ return -ENOMEM;
+
+ crypto_init_wait(&wait);
+ ahash_request_set_callback(subreq, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+ sg_init_one(&sg, NULL, 0);
+ ahash_request_set_crypt(subreq, &sg, req->result, 0);
+
+ ret = crypto_wait_req(crypto_ahash_digest(subreq), &wait);
+
+ ahash_request_free(subreq);
+ return ret;
+}
+
static int qce_ahash_final(struct ahash_request *req)
{
struct qce_sha_reqctx *rctx = ahash_request_ctx_dma(req);
@@ -280,6 +310,8 @@ static int qce_ahash_final(struct ahash_request *req)
if (tmpl->hash_zero)
memcpy(req->result, tmpl->hash_zero,
tmpl->alg.ahash.halg.digestsize);
+ else if (IS_SHA_HMAC(rctx->flags))
+ return qce_ahash_hmac_zero(req);
return 0;
}
@@ -317,6 +349,8 @@ static int qce_ahash_digest(struct ahash_request *req)
if (tmpl->hash_zero)
memcpy(req->result, tmpl->hash_zero,
tmpl->alg.ahash.halg.digestsize);
+ else if (IS_SHA_HMAC(rctx->flags))
+ return qce_ahash_hmac_zero(req);
return 0;
}
@@ -340,6 +374,17 @@ static int qce_ahash_hmac_setkey(struct crypto_ahash *tfm, const u8 *key,
blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm));
memset(ctx->authkey, 0, sizeof(ctx->authkey));
+ /*
+ * Keep the software fallback keyed in sync - it is used for empty
+ * messages, which the DMA engine cannot process.
+ */
+ crypto_ahash_clear_flags(ctx->fallback, CRYPTO_TFM_REQ_MASK);
+ crypto_ahash_set_flags(ctx->fallback,
+ crypto_ahash_get_flags(tfm) & CRYPTO_TFM_REQ_MASK);
+ ret = crypto_ahash_setkey(ctx->fallback, key, keylen);
+ if (ret)
+ return ret;
+
if (keylen <= blocksize) {
memcpy(ctx->authkey, key, keylen);
return 0;
@@ -395,6 +440,36 @@ static int qce_ahash_cra_init(struct crypto_tfm *tfm)
return 0;
}
+static int qce_ahash_hmac_cra_init(struct crypto_tfm *tfm)
+{
+ struct qce_sha_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct crypto_ahash *fallback;
+ int ret;
+
+ ret = qce_ahash_cra_init(tfm);
+ if (ret)
+ return ret;
+
+ /*
+ * The fallback is used to compute HMACs of empty messages, which the
+ * DMA engine cannot process.
+ */
+ fallback = crypto_alloc_ahash(crypto_tfm_alg_name(tfm), 0,
+ CRYPTO_ALG_NEED_FALLBACK);
+ if (IS_ERR(fallback))
+ return PTR_ERR(fallback);
+
+ ctx->fallback = fallback;
+ return 0;
+}
+
+static void qce_ahash_hmac_cra_exit(struct crypto_tfm *tfm)
+{
+ struct qce_sha_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ crypto_free_ahash(ctx->fallback);
+}
+
struct qce_ahash_def {
unsigned long flags;
const char *name;
@@ -462,7 +537,14 @@ static int qce_ahash_register_one(const struct qce_ahash_def *def,
base->cra_ctxsize = sizeof(struct qce_sha_ctx);
base->cra_alignmask = 0;
base->cra_module = THIS_MODULE;
- base->cra_init = qce_ahash_cra_init;
+
+ if (IS_SHA_HMAC(def->flags)) {
+ base->cra_flags |= CRYPTO_ALG_NEED_FALLBACK;
+ base->cra_init = qce_ahash_hmac_cra_init;
+ base->cra_exit = qce_ahash_hmac_cra_exit;
+ } else {
+ base->cra_init = qce_ahash_cra_init;
+ }
strscpy(base->cra_name, def->name);
strscpy(base->cra_driver_name, def->drv_name);
diff --git a/drivers/crypto/qce/sha.h b/drivers/crypto/qce/sha.h
index cb822fc334dc187cf1c66e2a332822a596ebcef3..2fa173ff2b2ec4031710ab6e3b14c28b04e0a746 100644
--- a/drivers/crypto/qce/sha.h
+++ b/drivers/crypto/qce/sha.h
@@ -17,6 +17,7 @@
struct qce_sha_ctx {
u8 authkey[QCE_SHA_MAX_BLOCKSIZE];
+ struct crypto_ahash *fallback;
};
/**
--
2.47.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox