こちらをベースに、各種crypt実装が呼び出されるまでを簡単にまとめておく。
大前提
ARM archで、SHA1のNEON実装が有効になり、呼び出せる準備ができるまでをまとめる。
なお、 ** NEON/SIMDの実装の中身の議論はしない** です。
KConfig
arch/arm/crypy/Kconfig に、各種カーネルコンフィグレーションがある。
- ARMとなっているものは、基本コンパイルできるはず…だけどNEONも使っているものもまざってて、うんわかりにくいな。
- NEONとついているものは、NEONが有効になっている場合にだけ使える。
- CEとついているものは、ARM v8 Crypto Extensionsを有効になっている場合にだけ使える
menuconfig ARM_CRYPTO
bool "ARM Accelerated Cryptographic Algorithms"
depends on ARM
help
Say Y here to choose from a selection of cryptographic algorithms
implemented using ARM specific CPU features or instructions.
if ARM_CRYPTO
<略>
config CRYPTO_SHA1_ARM_NEON
tristate "SHA1 digest algorithm (ARM NEON)"
depends on KERNEL_MODE_NEON
select CRYPTO_SHA1_ARM
select CRYPTO_SHA1
select CRYPTO_HASH
help
SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2) implemented
using optimized ARM NEON assembly, when NEON instructions are
available.
Makefile
KConfigで有効にした設定に応じて、必要なファイルを取り込むいつもの記載ですね
obj-$(CONFIG_CRYPTO_AES_ARM) += aes-arm.o
obj-$(CONFIG_CRYPTO_AES_ARM_BS) += aes-arm-bs.o
obj-$(CONFIG_CRYPTO_SHA1_ARM) += sha1-arm.o
obj-$(CONFIG_CRYPTO_SHA1_ARM_NEON) += sha1-arm-neon.o
obj-$(CONFIG_CRYPTO_SHA256_ARM) += sha256-arm.o
<略>
aes-arm-y := aes-cipher-core.o aes-cipher-glue.o
aes-arm-bs-y := aes-neonbs-core.o aes-neonbs-glue.o
sha1-arm-y := sha1-armv4-large.o sha1_glue.o
sha1-arm-neon-y := sha1-armv7-neon.o sha1_neon_glue.o
sha256-arm-neon-$(CONFIG_KERNEL_MODE_NEON) := sha256_neon_glue.o
sha1-arm-neon.o
これは、 sha1-arm-neon-y := sha1-armv7-neon.o sha1_neon_glue.o
の記載から、2つのファイルを結合したものになる。
sha1_neon_glue.c
モジュール組み込み時の、_init/_exit
モジュールを組み込んだ時点で、sha1_neon_mode_[init|fini] が登録される。
module_init(sha1_neon_mod_init);
module_exit(sha1_neon_mod_fini);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SHA1 Secure Hash Algorithm, NEON accelerated");
MODULE_ALIAS_CRYPTO("sha1");
sha1_neon_mod_init()
sha1_neon_mod_init()では、neon用の実装を登録している。
ここで、 .init
はsha1_base_init
となって共通実装を使い、その他は、sha1_neon_*
と独自実装となっている点に注目されたい。
static struct shash_alg alg = {
.digestsize = SHA1_DIGEST_SIZE,
.init = sha1_base_init,
.update = sha1_neon_update,
.final = sha1_neon_final,
.finup = sha1_neon_finup,
.descsize = sizeof(struct sha1_state),
.base = {
.cra_name = "sha1",
.cra_driver_name = "sha1-neon",
.cra_priority = 250,
.cra_blocksize = SHA1_BLOCK_SIZE,
.cra_module = THIS_MODULE,
}
};
static int __init sha1_neon_mod_init(void)
{
if (!cpu_has_neon())
return -ENODEV;
return crypto_register_shash(&alg);
}
sha1_neon_update()
static int sha1_neon_update(struct shash_desc *desc, const u8 *data,
unsigned int len)
{
struct sha1_state *sctx = shash_desc_ctx(desc);
if (!crypto_simd_usable() ||
(sctx->count % SHA1_BLOCK_SIZE) + len < SHA1_BLOCK_SIZE)
return sha1_update_arm(desc, data, len);
kernel_neon_begin();
sha1_base_do_update(desc, data, len,
(sha1_block_fn *)sha1_transform_neon);
kernel_neon_end();
return 0;
}
int sha1_update_arm(struct shash_desc *desc, const u8 *data,
unsigned int len)
{
/* make sure casting to sha1_block_fn() is safe */
BUILD_BUG_ON(offsetof(struct sha1_state, state) != 0);
return sha1_base_do_update(desc, data, len,
(sha1_block_fn *)sha1_block_data_order);
}
EXPORT_SYMBOL_GPL(sha1_update_arm);
ここで、
- crypt_simd_usable() は、SIMD実装をcryptで利用できるかどうかを判定する関数。 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/crypto/internal/simd.h#n64 あたりに実装がある。
- SIMD実装をcryptで使えない場合は、利用できない場合には、普通の
sha1_update_arm()
からsha1_base_do_update()
を呼び出すフォールバック処理がなされる。 -
kernel_neon_begin()
と、kernel_neon_end()
は、NEON Registerの保護のために行われる関数。 - 処理の実体は、
sha1_base_do_update()
の引数で渡された、sha1_transform_neon()
で行われる。
sha1_neon_finup() / sha1_neon_final()
終了処理をまとめて説明する。
- simdがcryptで使えなかったら、通常処理に戻す。
- kernel_neon_begin() でneon使えるようにする
- sha1_base_do_update(sha1_trasnfrom_neon) で、処理本体を呼び出す
- kernel_neon_end() でneon使用状態を戻す
static int sha1_neon_finup(struct shash_desc *desc, const u8 *data,
unsigned int len, u8 *out)
{
if (!crypto_simd_usable())
return sha1_finup_arm(desc, data, len, out);
kernel_neon_begin();
if (len)
sha1_base_do_update(desc, data, len,
(sha1_block_fn *)sha1_transform_neon);
sha1_base_do_finalize(desc, (sha1_block_fn *)sha1_transform_neon);
kernel_neon_end();
return sha1_base_finish(desc, out);
}
static int sha1_neon_final(struct shash_desc *desc, u8 *out)
{
return sha1_neon_finup(desc, NULL, 0, out);
}
sha1-armv7-neon.S
sha1_transform_neon()
/*
* Transform nblks*64 bytes (nblks*16 32-bit words) at DATA.
*
* unsigned int
* sha1_transform_neon (void *ctx, const unsigned char *data,
* unsigned int nblks)
*/
.align 3
ENTRY(sha1_transform_neon)
/* input:
* r0: ctx, CTX
* r1: data (64*nblks bytes)
* r2: nblks
*/
cmp RNBLKS, #0;
beq .Ldo_nothing;
<略>
まとめ
-
XXXX-glue.c
というファイルを追加して、module_init()内でtransform関数を登録しよう。 - transform関数は、callbackとして引き渡す形で使うので、そこを機にしよう。
- 前提条件を満たさず使えなかった場合のフォールバック処理もちゃんと入れよう。
以上となります。
おまけ:現在(2020/5/30) でLinux Kernelのcrypt実装では、どんなものがサポートされているのか。
ざっくり、ファイル名から見ると下記がサポートされているっポイ。
ARMの場合
(☆ = x86にはなくて、ARMにはある)
- aes
- chacha
- crc32
- crct10dif
- curve25519
- ghash
- nhpoly1305
- poly1305
- sha1
- sha2 ☆
- sha256
- sha512
ARM64の場合
(☆ = x86にはなくて、ARM64にはある)
- aes
- chacha
- crct10dif
- ghash
- ngpoly1305
- poly1305
- sha1
- sha2
- sha3
- sha256
- sha512
- sm3 ☆
x86の場合
(★= ARMにはなくて、x86にある)
特徴:さすがにみんなが使っていることだけあって、サポートが熱く厚いなあ……
- aegis128 ★
- aes
- blake2s ★
- blowfish ★
- camellia ★
- cast5 ★
- cast6 ★
- chacha
- crc32
- crct10dif
- curve25519 ★
- des3 ★
- ghash
- nhpoly1305
- poly1305
- serpent ★
- sha1
- sha256
- sha512
- twofish ★
powerpcの場合
特徴: う、うーん、、、これ、特徴が無いように見える。
- aes
- crc32
- crct10dif
- md5
- sha1
- sha256
sparcの場合
特徴: MD5とかAES,DES,CAMELLIAとか、SHA1,SHA256,SHA512とか、専用命令あるっぽい!
- camellia
- crc32c
- des
- md5
- sha1
- sha256
- sha512
MIPS
- chacha
- crc32
- poly1305
S390
- aes
- RNG
- crc32
- des
- ghash
- paes
- sha1
- sha3
- sha256
- sha512
RISC-V
未実装!!ここにcommitすれば、あなたもLinux Kernel Developerとして名前が未来永劫記録されますよ!!やったね!!!