2015-02-06 19:59:05 +03:00
/**
* GHASH routines supporting VMX instructions on the Power 8
*
* Copyright ( C ) 2015 International Business Machines Inc .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 only .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
* Author : Marcelo Henrique Cerri < mhcerri @ br . ibm . com >
*/
# include <linux/types.h>
# include <linux/err.h>
# include <linux/crypto.h>
# include <linux/delay.h>
# include <linux/hardirq.h>
# include <asm/switch_to.h>
# include <crypto/aes.h>
2016-09-28 19:42:10 +03:00
# include <crypto/ghash.h>
2015-02-06 19:59:05 +03:00
# include <crypto/scatterwalk.h>
# include <crypto/internal/hash.h>
# include <crypto/b128ops.h>
# define IN_INTERRUPT in_interrupt()
void gcm_init_p8 ( u128 htable [ 16 ] , const u64 Xi [ 2 ] ) ;
void gcm_gmult_p8 ( u64 Xi [ 2 ] , const u128 htable [ 16 ] ) ;
void gcm_ghash_p8 ( u64 Xi [ 2 ] , const u128 htable [ 16 ] ,
2015-06-15 11:55:46 +03:00
const u8 * in , size_t len ) ;
2015-02-06 19:59:05 +03:00
struct p8_ghash_ctx {
2015-06-15 11:55:46 +03:00
u128 htable [ 16 ] ;
struct crypto_shash * fallback ;
2015-02-06 19:59:05 +03:00
} ;
struct p8_ghash_desc_ctx {
2015-06-15 11:55:46 +03:00
u64 shash [ 2 ] ;
u8 buffer [ GHASH_DIGEST_SIZE ] ;
int bytes ;
struct shash_desc fallback_desc ;
2015-02-06 19:59:05 +03:00
} ;
static int p8_ghash_init_tfm ( struct crypto_tfm * tfm )
{
2016-09-28 19:42:10 +03:00
const char * alg = " ghash-generic " ;
2015-06-15 11:55:46 +03:00
struct crypto_shash * fallback ;
struct crypto_shash * shash_tfm = __crypto_shash_cast ( tfm ) ;
struct p8_ghash_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
fallback = crypto_alloc_shash ( alg , 0 , CRYPTO_ALG_NEED_FALLBACK ) ;
if ( IS_ERR ( fallback ) ) {
printk ( KERN_ERR
" Failed to allocate transformation for '%s': %ld \n " ,
alg , PTR_ERR ( fallback ) ) ;
return PTR_ERR ( fallback ) ;
}
printk ( KERN_INFO " Using '%s' as fallback implementation. \n " ,
crypto_tfm_alg_driver_name ( crypto_shash_tfm ( fallback ) ) ) ;
crypto_shash_set_flags ( fallback ,
crypto_shash_get_flags ( ( struct crypto_shash
* ) tfm ) ) ;
2016-09-28 19:42:10 +03:00
/* Check if the descsize defined in the algorithm is still enough. */
if ( shash_tfm - > descsize < sizeof ( struct p8_ghash_desc_ctx )
+ crypto_shash_descsize ( fallback ) ) {
printk ( KERN_ERR
" Desc size of the fallback implementation (%s) does not match the expected value: %lu vs %u \n " ,
alg ,
shash_tfm - > descsize - sizeof ( struct p8_ghash_desc_ctx ) ,
crypto_shash_descsize ( fallback ) ) ;
return - EINVAL ;
}
ctx - > fallback = fallback ;
2015-06-15 11:55:46 +03:00
return 0 ;
2015-02-06 19:59:05 +03:00
}
static void p8_ghash_exit_tfm ( struct crypto_tfm * tfm )
{
2015-06-15 11:55:46 +03:00
struct p8_ghash_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
2015-02-06 19:59:05 +03:00
2015-06-15 11:55:46 +03:00
if ( ctx - > fallback ) {
crypto_free_shash ( ctx - > fallback ) ;
ctx - > fallback = NULL ;
}
2015-02-06 19:59:05 +03:00
}
static int p8_ghash_init ( struct shash_desc * desc )
{
2015-06-15 11:55:46 +03:00
struct p8_ghash_ctx * ctx = crypto_tfm_ctx ( crypto_shash_tfm ( desc - > tfm ) ) ;
struct p8_ghash_desc_ctx * dctx = shash_desc_ctx ( desc ) ;
dctx - > bytes = 0 ;
memset ( dctx - > shash , 0 , GHASH_DIGEST_SIZE ) ;
dctx - > fallback_desc . tfm = ctx - > fallback ;
dctx - > fallback_desc . flags = desc - > flags ;
return crypto_shash_init ( & dctx - > fallback_desc ) ;
2015-02-06 19:59:05 +03:00
}
static int p8_ghash_setkey ( struct crypto_shash * tfm , const u8 * key ,
2015-06-15 11:55:46 +03:00
unsigned int keylen )
2015-02-06 19:59:05 +03:00
{
2015-06-15 11:55:46 +03:00
struct p8_ghash_ctx * ctx = crypto_tfm_ctx ( crypto_shash_tfm ( tfm ) ) ;
2015-02-06 19:59:05 +03:00
2016-09-28 19:42:10 +03:00
if ( keylen ! = GHASH_BLOCK_SIZE )
2015-06-15 11:55:46 +03:00
return - EINVAL ;
2015-02-06 19:59:05 +03:00
2015-06-23 07:04:48 +03:00
preempt_disable ( ) ;
2015-06-15 11:55:46 +03:00
pagefault_disable ( ) ;
2015-07-13 19:51:39 +03:00
enable_kernel_vsx ( ) ;
2015-06-15 11:55:46 +03:00
gcm_init_p8 ( ctx - > htable , ( const u64 * ) key ) ;
2015-10-29 03:44:05 +03:00
disable_kernel_vsx ( ) ;
2015-06-15 11:55:46 +03:00
pagefault_enable ( ) ;
2015-06-23 07:04:48 +03:00
preempt_enable ( ) ;
2015-06-15 11:55:46 +03:00
return crypto_shash_setkey ( ctx - > fallback , key , keylen ) ;
2015-02-06 19:59:05 +03:00
}
static int p8_ghash_update ( struct shash_desc * desc ,
2015-06-15 11:55:46 +03:00
const u8 * src , unsigned int srclen )
2015-02-06 19:59:05 +03:00
{
2015-06-15 11:55:46 +03:00
unsigned int len ;
struct p8_ghash_ctx * ctx = crypto_tfm_ctx ( crypto_shash_tfm ( desc - > tfm ) ) ;
struct p8_ghash_desc_ctx * dctx = shash_desc_ctx ( desc ) ;
if ( IN_INTERRUPT ) {
return crypto_shash_update ( & dctx - > fallback_desc , src ,
srclen ) ;
} else {
if ( dctx - > bytes ) {
if ( dctx - > bytes + srclen < GHASH_DIGEST_SIZE ) {
memcpy ( dctx - > buffer + dctx - > bytes , src ,
srclen ) ;
dctx - > bytes + = srclen ;
return 0 ;
}
memcpy ( dctx - > buffer + dctx - > bytes , src ,
GHASH_DIGEST_SIZE - dctx - > bytes ) ;
2015-06-23 07:04:48 +03:00
preempt_disable ( ) ;
2015-06-15 11:55:46 +03:00
pagefault_disable ( ) ;
2015-07-13 19:51:39 +03:00
enable_kernel_vsx ( ) ;
2015-06-15 11:55:46 +03:00
gcm_ghash_p8 ( dctx - > shash , ctx - > htable ,
dctx - > buffer , GHASH_DIGEST_SIZE ) ;
2015-10-29 03:44:05 +03:00
disable_kernel_vsx ( ) ;
2015-06-15 11:55:46 +03:00
pagefault_enable ( ) ;
2015-06-23 07:04:48 +03:00
preempt_enable ( ) ;
2015-06-15 11:55:46 +03:00
src + = GHASH_DIGEST_SIZE - dctx - > bytes ;
srclen - = GHASH_DIGEST_SIZE - dctx - > bytes ;
dctx - > bytes = 0 ;
}
len = srclen & ~ ( GHASH_DIGEST_SIZE - 1 ) ;
if ( len ) {
2015-06-23 07:04:48 +03:00
preempt_disable ( ) ;
2015-06-15 11:55:46 +03:00
pagefault_disable ( ) ;
2015-07-13 19:51:39 +03:00
enable_kernel_vsx ( ) ;
2015-06-15 11:55:46 +03:00
gcm_ghash_p8 ( dctx - > shash , ctx - > htable , src , len ) ;
2015-10-29 03:44:05 +03:00
disable_kernel_vsx ( ) ;
2015-06-15 11:55:46 +03:00
pagefault_enable ( ) ;
2015-06-23 07:04:48 +03:00
preempt_enable ( ) ;
2015-06-15 11:55:46 +03:00
src + = len ;
srclen - = len ;
}
if ( srclen ) {
memcpy ( dctx - > buffer , src , srclen ) ;
dctx - > bytes = srclen ;
}
return 0 ;
}
2015-02-06 19:59:05 +03:00
}
static int p8_ghash_final ( struct shash_desc * desc , u8 * out )
{
2015-06-15 11:55:46 +03:00
int i ;
struct p8_ghash_ctx * ctx = crypto_tfm_ctx ( crypto_shash_tfm ( desc - > tfm ) ) ;
struct p8_ghash_desc_ctx * dctx = shash_desc_ctx ( desc ) ;
if ( IN_INTERRUPT ) {
return crypto_shash_final ( & dctx - > fallback_desc , out ) ;
} else {
if ( dctx - > bytes ) {
for ( i = dctx - > bytes ; i < GHASH_DIGEST_SIZE ; i + + )
dctx - > buffer [ i ] = 0 ;
2015-06-23 07:04:48 +03:00
preempt_disable ( ) ;
2015-06-15 11:55:46 +03:00
pagefault_disable ( ) ;
2015-07-13 19:51:39 +03:00
enable_kernel_vsx ( ) ;
2015-06-15 11:55:46 +03:00
gcm_ghash_p8 ( dctx - > shash , ctx - > htable ,
dctx - > buffer , GHASH_DIGEST_SIZE ) ;
2015-10-29 03:44:05 +03:00
disable_kernel_vsx ( ) ;
2015-06-15 11:55:46 +03:00
pagefault_enable ( ) ;
2015-06-23 07:04:48 +03:00
preempt_enable ( ) ;
2015-06-15 11:55:46 +03:00
dctx - > bytes = 0 ;
}
memcpy ( out , dctx - > shash , GHASH_DIGEST_SIZE ) ;
return 0 ;
}
2015-02-06 19:59:05 +03:00
}
struct shash_alg p8_ghash_alg = {
2015-06-15 11:55:46 +03:00
. digestsize = GHASH_DIGEST_SIZE ,
. init = p8_ghash_init ,
. update = p8_ghash_update ,
. final = p8_ghash_final ,
. setkey = p8_ghash_setkey ,
2016-09-28 19:42:10 +03:00
. descsize = sizeof ( struct p8_ghash_desc_ctx )
+ sizeof ( struct ghash_desc_ctx ) ,
2015-06-15 11:55:46 +03:00
. base = {
. cra_name = " ghash " ,
. cra_driver_name = " p8_ghash " ,
. cra_priority = 1000 ,
. cra_flags = CRYPTO_ALG_TYPE_SHASH | CRYPTO_ALG_NEED_FALLBACK ,
. cra_blocksize = GHASH_BLOCK_SIZE ,
. cra_ctxsize = sizeof ( struct p8_ghash_ctx ) ,
. cra_module = THIS_MODULE ,
. cra_init = p8_ghash_init_tfm ,
. cra_exit = p8_ghash_exit_tfm ,
} ,
2015-02-06 19:59:05 +03:00
} ;