| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- /* SPDX-License-Identifier: GPL-2.0-only */
- /*
- * Copyright (c) 2015 Markus Stockhausen <stockhausen@collogia.de>
- * Copyright (C) 2015 International Business Machines Inc.
- * Copyright 2026 Google LLC
- */
- #include <asm/simd.h>
- #include <asm/switch_to.h>
- #include <linux/cpufeature.h>
- #include <linux/jump_label.h>
- #include <linux/preempt.h>
- #include <linux/uaccess.h>
- #ifdef CONFIG_SPE
- EXPORT_SYMBOL_GPL(ppc_expand_key_128);
- EXPORT_SYMBOL_GPL(ppc_expand_key_192);
- EXPORT_SYMBOL_GPL(ppc_expand_key_256);
- EXPORT_SYMBOL_GPL(ppc_generate_decrypt_key);
- EXPORT_SYMBOL_GPL(ppc_encrypt_ecb);
- EXPORT_SYMBOL_GPL(ppc_decrypt_ecb);
- EXPORT_SYMBOL_GPL(ppc_encrypt_cbc);
- EXPORT_SYMBOL_GPL(ppc_decrypt_cbc);
- EXPORT_SYMBOL_GPL(ppc_crypt_ctr);
- EXPORT_SYMBOL_GPL(ppc_encrypt_xts);
- EXPORT_SYMBOL_GPL(ppc_decrypt_xts);
- void ppc_encrypt_aes(u8 *out, const u8 *in, const u32 *key_enc, u32 rounds);
- void ppc_decrypt_aes(u8 *out, const u8 *in, const u32 *key_dec, u32 rounds);
- static void spe_begin(void)
- {
- /* disable preemption and save users SPE registers if required */
- preempt_disable();
- enable_kernel_spe();
- }
- static void spe_end(void)
- {
- disable_kernel_spe();
- /* reenable preemption */
- preempt_enable();
- }
- static void aes_preparekey_arch(union aes_enckey_arch *k,
- union aes_invkey_arch *inv_k,
- const u8 *in_key, int key_len, int nrounds)
- {
- if (key_len == AES_KEYSIZE_128)
- ppc_expand_key_128(k->spe_enc_key, in_key);
- else if (key_len == AES_KEYSIZE_192)
- ppc_expand_key_192(k->spe_enc_key, in_key);
- else
- ppc_expand_key_256(k->spe_enc_key, in_key);
- if (inv_k)
- ppc_generate_decrypt_key(inv_k->spe_dec_key, k->spe_enc_key,
- key_len);
- }
- static void aes_encrypt_arch(const struct aes_enckey *key,
- u8 out[AES_BLOCK_SIZE],
- const u8 in[AES_BLOCK_SIZE])
- {
- spe_begin();
- ppc_encrypt_aes(out, in, key->k.spe_enc_key, key->nrounds / 2 - 1);
- spe_end();
- }
- static void aes_decrypt_arch(const struct aes_key *key,
- u8 out[AES_BLOCK_SIZE],
- const u8 in[AES_BLOCK_SIZE])
- {
- spe_begin();
- ppc_decrypt_aes(out, in, key->inv_k.spe_dec_key, key->nrounds / 2 - 1);
- spe_end();
- }
- #else /* CONFIG_SPE */
- static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_vec_crypto);
- EXPORT_SYMBOL_GPL(aes_p8_set_encrypt_key);
- EXPORT_SYMBOL_GPL(aes_p8_set_decrypt_key);
- EXPORT_SYMBOL_GPL(aes_p8_encrypt);
- EXPORT_SYMBOL_GPL(aes_p8_decrypt);
- EXPORT_SYMBOL_GPL(aes_p8_cbc_encrypt);
- EXPORT_SYMBOL_GPL(aes_p8_ctr32_encrypt_blocks);
- EXPORT_SYMBOL_GPL(aes_p8_xts_encrypt);
- EXPORT_SYMBOL_GPL(aes_p8_xts_decrypt);
- static inline bool is_vsx_format(const struct p8_aes_key *key)
- {
- return key->nrounds != 0;
- }
- /*
- * Convert a round key from VSX to generic format by reflecting all 16 bytes (if
- * little endian) or reflecting the bytes in each 4-byte word (if big endian),
- * and (if apply_inv_mix=true) applying InvMixColumn to each column.
- *
- * It would be nice if the VSX and generic key formats would be compatible. But
- * that's very difficult to do, with the assembly code having been borrowed from
- * OpenSSL and also targeted to POWER8 rather than POWER9.
- *
- * Fortunately, this conversion should only be needed in extremely rare cases,
- * possibly not at all in practice. It's just included for full correctness.
- */
- static void rndkey_from_vsx(u32 out[4], const u32 in[4], bool apply_inv_mix)
- {
- const bool be = IS_ENABLED(CONFIG_CPU_BIG_ENDIAN);
- u32 k0 = swab32(in[0]);
- u32 k1 = swab32(in[1]);
- u32 k2 = swab32(in[2]);
- u32 k3 = swab32(in[3]);
- if (apply_inv_mix) {
- k0 = inv_mix_columns(k0);
- k1 = inv_mix_columns(k1);
- k2 = inv_mix_columns(k2);
- k3 = inv_mix_columns(k3);
- }
- out[0] = be ? k0 : k3;
- out[1] = be ? k1 : k2;
- out[2] = be ? k2 : k1;
- out[3] = be ? k3 : k0;
- }
- static void aes_preparekey_arch(union aes_enckey_arch *k,
- union aes_invkey_arch *inv_k,
- const u8 *in_key, int key_len, int nrounds)
- {
- const int keybits = 8 * key_len;
- int ret;
- if (static_branch_likely(&have_vec_crypto) && likely(may_use_simd())) {
- preempt_disable();
- pagefault_disable();
- enable_kernel_vsx();
- ret = aes_p8_set_encrypt_key(in_key, keybits, &k->p8);
- /*
- * aes_p8_set_encrypt_key() should never fail here, since the
- * key length was already validated.
- */
- WARN_ON_ONCE(ret);
- if (inv_k) {
- ret = aes_p8_set_decrypt_key(in_key, keybits,
- &inv_k->p8);
- /* ... and likewise for aes_p8_set_decrypt_key(). */
- WARN_ON_ONCE(ret);
- }
- disable_kernel_vsx();
- pagefault_enable();
- preempt_enable();
- } else {
- aes_expandkey_generic(k->rndkeys,
- inv_k ? inv_k->inv_rndkeys : NULL,
- in_key, key_len);
- /* Mark the key as using the generic format. */
- k->p8.nrounds = 0;
- if (inv_k)
- inv_k->p8.nrounds = 0;
- }
- }
- static void aes_encrypt_arch(const struct aes_enckey *key,
- u8 out[AES_BLOCK_SIZE],
- const u8 in[AES_BLOCK_SIZE])
- {
- if (static_branch_likely(&have_vec_crypto) &&
- likely(is_vsx_format(&key->k.p8) && may_use_simd())) {
- preempt_disable();
- pagefault_disable();
- enable_kernel_vsx();
- aes_p8_encrypt(in, out, &key->k.p8);
- disable_kernel_vsx();
- pagefault_enable();
- preempt_enable();
- } else if (unlikely(is_vsx_format(&key->k.p8))) {
- /*
- * This handles (the hopefully extremely rare) case where a key
- * was prepared using the VSX optimized format, then encryption
- * is done in a context that cannot use VSX instructions.
- */
- u32 rndkeys[AES_MAX_KEYLENGTH_U32];
- for (int i = 0; i < 4 * (key->nrounds + 1); i += 4)
- rndkey_from_vsx(&rndkeys[i],
- &key->k.p8.rndkeys[i], false);
- aes_encrypt_generic(rndkeys, key->nrounds, out, in);
- } else {
- aes_encrypt_generic(key->k.rndkeys, key->nrounds, out, in);
- }
- }
- static void aes_decrypt_arch(const struct aes_key *key, u8 out[AES_BLOCK_SIZE],
- const u8 in[AES_BLOCK_SIZE])
- {
- if (static_branch_likely(&have_vec_crypto) &&
- likely(is_vsx_format(&key->inv_k.p8) && may_use_simd())) {
- preempt_disable();
- pagefault_disable();
- enable_kernel_vsx();
- aes_p8_decrypt(in, out, &key->inv_k.p8);
- disable_kernel_vsx();
- pagefault_enable();
- preempt_enable();
- } else if (unlikely(is_vsx_format(&key->inv_k.p8))) {
- /*
- * This handles (the hopefully extremely rare) case where a key
- * was prepared using the VSX optimized format, then decryption
- * is done in a context that cannot use VSX instructions.
- */
- u32 inv_rndkeys[AES_MAX_KEYLENGTH_U32];
- int i;
- rndkey_from_vsx(&inv_rndkeys[0],
- &key->inv_k.p8.rndkeys[0], false);
- for (i = 4; i < 4 * key->nrounds; i += 4) {
- rndkey_from_vsx(&inv_rndkeys[i],
- &key->inv_k.p8.rndkeys[i], true);
- }
- rndkey_from_vsx(&inv_rndkeys[i],
- &key->inv_k.p8.rndkeys[i], false);
- aes_decrypt_generic(inv_rndkeys, key->nrounds, out, in);
- } else {
- aes_decrypt_generic(key->inv_k.inv_rndkeys, key->nrounds,
- out, in);
- }
- }
- #define aes_mod_init_arch aes_mod_init_arch
- static void aes_mod_init_arch(void)
- {
- if (cpu_has_feature(CPU_FTR_ARCH_207S) &&
- (cur_cpu_spec->cpu_user_features2 & PPC_FEATURE2_VEC_CRYPTO))
- static_branch_enable(&have_vec_crypto);
- }
- #endif /* !CONFIG_SPE */
|