2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2006-10-04 18:48:57 +10:00
/* Copyright (C) 2004-2006, Advanced Micro Devices, Inc.
2017-07-06 14:44:56 -04:00
*/
2006-10-04 18:48:57 +10:00
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/pci.h>
# include <linux/pci_ids.h>
# include <linux/crypto.h>
# include <linux/spinlock.h>
# include <crypto/algapi.h>
2007-10-17 23:18:57 +08:00
# include <crypto/aes.h>
2019-10-10 21:51:32 -07:00
# include <crypto/internal/skcipher.h>
2006-10-04 18:48:57 +10:00
2010-04-19 21:02:41 +08:00
# include <linux/io.h>
# include <linux/delay.h>
2006-10-04 18:48:57 +10:00
# include "geode-aes.h"
/* Static structures */
2010-04-19 21:02:41 +08:00
static void __iomem * _iobase ;
2006-10-04 18:48:57 +10:00
static spinlock_t lock ;
/* Write a 128 bit field (either a writable key or IV) */
static inline void
2019-10-10 21:51:32 -07:00
_writefield ( u32 offset , const void * value )
2006-10-04 18:48:57 +10:00
{
int i ;
2017-07-06 14:44:56 -04:00
2010-04-19 21:02:41 +08:00
for ( i = 0 ; i < 4 ; i + + )
2019-10-10 21:51:32 -07:00
iowrite32 ( ( ( const u32 * ) value ) [ i ] , _iobase + offset + ( i * 4 ) ) ;
2006-10-04 18:48:57 +10:00
}
/* Read a 128 bit field (either a writable key or IV) */
static inline void
_readfield ( u32 offset , void * value )
{
int i ;
2017-07-06 14:44:56 -04:00
2010-04-19 21:02:41 +08:00
for ( i = 0 ; i < 4 ; i + + )
2006-10-04 18:48:57 +10:00
( ( u32 * ) value ) [ i ] = ioread32 ( _iobase + offset + ( i * 4 ) ) ;
}
static int
2019-10-10 21:51:32 -07:00
do_crypt ( const void * src , void * dst , u32 len , u32 flags )
2006-10-04 18:48:57 +10:00
{
u32 status ;
u32 counter = AES_OP_TIMEOUT ;
2019-10-10 21:51:32 -07:00
iowrite32 ( virt_to_phys ( ( void * ) src ) , _iobase + AES_SOURCEA_REG ) ;
2006-10-04 18:48:57 +10:00
iowrite32 ( virt_to_phys ( dst ) , _iobase + AES_DSTA_REG ) ;
iowrite32 ( len , _iobase + AES_LENA_REG ) ;
/* Start the operation */
iowrite32 ( AES_CTRL_START | flags , _iobase + AES_CTRLA_REG ) ;
2007-10-21 16:18:12 +08:00
do {
2006-10-04 18:48:57 +10:00
status = ioread32 ( _iobase + AES_INTR_REG ) ;
2007-10-21 16:18:12 +08:00
cpu_relax ( ) ;
2010-04-19 21:02:41 +08:00
} while ( ! ( status & AES_INTRA_PENDING ) & & - - counter ) ;
2006-10-04 18:48:57 +10:00
/* Clear the event */
iowrite32 ( ( status & 0xFF ) | AES_INTRA_PENDING , _iobase + AES_INTR_REG ) ;
return counter ? 0 : 1 ;
}
2019-10-10 21:51:32 -07:00
static void
geode_aes_crypt ( const struct geode_aes_tfm_ctx * tctx , const void * src ,
void * dst , u32 len , u8 * iv , int mode , int dir )
2006-10-04 18:48:57 +10:00
{
u32 flags = 0 ;
2007-03-06 01:42:13 -08:00
unsigned long iflags ;
2007-10-21 16:18:12 +08:00
int ret ;
2006-10-04 18:48:57 +10:00
2007-05-24 21:23:24 +10:00
/* If the source and destination is the same, then
* we need to turn on the coherent flags , otherwise
* we don ' t need to worry
*/
2007-11-10 19:37:49 +08:00
flags | = ( AES_CTRL_DCA | AES_CTRL_SCA ) ;
2006-10-04 18:48:57 +10:00
2019-10-10 21:51:32 -07:00
if ( dir = = AES_DIR_ENCRYPT )
2006-10-04 18:48:57 +10:00
flags | = AES_CTRL_ENCRYPT ;
/* Start the critical section */
spin_lock_irqsave ( & lock , iflags ) ;
2019-10-10 21:51:32 -07:00
if ( mode = = AES_MODE_CBC ) {
2006-10-04 18:48:57 +10:00
flags | = AES_CTRL_CBC ;
2019-10-10 21:51:32 -07:00
_writefield ( AES_WRITEIV0_REG , iv ) ;
2006-10-04 18:48:57 +10:00
}
2019-10-10 21:51:32 -07:00
flags | = AES_CTRL_WRKEY ;
_writefield ( AES_WRITEKEY0_REG , tctx - > key ) ;
2006-10-04 18:48:57 +10:00
2019-10-10 21:51:32 -07:00
ret = do_crypt ( src , dst , len , flags ) ;
2007-10-21 16:18:12 +08:00
BUG_ON ( ret ) ;
2006-10-04 18:48:57 +10:00
2019-10-10 21:51:32 -07:00
if ( mode = = AES_MODE_CBC )
_readfield ( AES_WRITEIV0_REG , iv ) ;
2006-10-04 18:48:57 +10:00
spin_unlock_irqrestore ( & lock , iflags ) ;
}
/* CRYPTO-API Functions */
2007-11-10 19:29:33 +08:00
static int geode_setkey_cip ( struct crypto_tfm * tfm , const u8 * key ,
unsigned int len )
2006-10-04 18:48:57 +10:00
{
2019-10-10 21:51:32 -07:00
struct geode_aes_tfm_ctx * tctx = crypto_tfm_ctx ( tfm ) ;
2007-11-10 19:29:33 +08:00
unsigned int ret ;
2019-10-10 21:51:32 -07:00
tctx - > keylen = len ;
2007-11-10 19:29:33 +08:00
if ( len = = AES_KEYSIZE_128 ) {
2019-10-10 21:51:32 -07:00
memcpy ( tctx - > key , key , len ) ;
2007-11-10 19:29:33 +08:00
return 0 ;
}
2006-10-04 18:48:57 +10:00
2007-11-10 19:29:33 +08:00
if ( len ! = AES_KEYSIZE_192 & & len ! = AES_KEYSIZE_256 ) {
/* not supported at all */
2006-10-04 18:48:57 +10:00
tfm - > crt_flags | = CRYPTO_TFM_RES_BAD_KEY_LEN ;
return - EINVAL ;
}
2007-11-10 19:29:33 +08:00
/*
* The requested key size is not supported by HW , do a fallback
*/
2019-10-10 21:51:32 -07:00
tctx - > fallback . cip - > base . crt_flags & = ~ CRYPTO_TFM_REQ_MASK ;
tctx - > fallback . cip - > base . crt_flags | =
( tfm - > crt_flags & CRYPTO_TFM_REQ_MASK ) ;
2007-11-10 19:29:33 +08:00
2019-10-10 21:51:32 -07:00
ret = crypto_cipher_setkey ( tctx - > fallback . cip , key , len ) ;
2007-11-10 19:29:33 +08:00
if ( ret ) {
tfm - > crt_flags & = ~ CRYPTO_TFM_RES_MASK ;
2019-10-10 21:51:32 -07:00
tfm - > crt_flags | = ( tctx - > fallback . cip - > base . crt_flags &
CRYPTO_TFM_RES_MASK ) ;
2007-11-10 19:29:33 +08:00
}
return ret ;
}
2019-10-10 21:51:32 -07:00
static int geode_setkey_skcipher ( struct crypto_skcipher * tfm , const u8 * key ,
unsigned int len )
2007-11-10 19:29:33 +08:00
{
2019-10-10 21:51:32 -07:00
struct geode_aes_tfm_ctx * tctx = crypto_skcipher_ctx ( tfm ) ;
2007-11-10 19:29:33 +08:00
unsigned int ret ;
2019-10-10 21:51:32 -07:00
tctx - > keylen = len ;
2007-11-10 19:29:33 +08:00
if ( len = = AES_KEYSIZE_128 ) {
2019-10-10 21:51:32 -07:00
memcpy ( tctx - > key , key , len ) ;
2007-11-10 19:29:33 +08:00
return 0 ;
}
if ( len ! = AES_KEYSIZE_192 & & len ! = AES_KEYSIZE_256 ) {
/* not supported at all */
2019-10-10 21:51:32 -07:00
crypto_skcipher_set_flags ( tfm , CRYPTO_TFM_RES_BAD_KEY_LEN ) ;
2007-11-10 19:29:33 +08:00
return - EINVAL ;
}
/*
* The requested key size is not supported by HW , do a fallback
*/
2019-10-10 21:51:32 -07:00
crypto_skcipher_clear_flags ( tctx - > fallback . skcipher ,
CRYPTO_TFM_REQ_MASK ) ;
crypto_skcipher_set_flags ( tctx - > fallback . skcipher ,
crypto_skcipher_get_flags ( tfm ) &
CRYPTO_TFM_REQ_MASK ) ;
ret = crypto_skcipher_setkey ( tctx - > fallback . skcipher , key , len ) ;
crypto_skcipher_set_flags ( tfm ,
crypto_skcipher_get_flags ( tctx - > fallback . skcipher ) &
CRYPTO_TFM_RES_MASK ) ;
2007-11-10 19:29:33 +08:00
return ret ;
}
2006-10-04 18:48:57 +10:00
static void
geode_encrypt ( struct crypto_tfm * tfm , u8 * out , const u8 * in )
{
2019-10-10 21:51:32 -07:00
const struct geode_aes_tfm_ctx * tctx = crypto_tfm_ctx ( tfm ) ;
2006-10-04 18:48:57 +10:00
2019-10-10 21:51:32 -07:00
if ( unlikely ( tctx - > keylen ! = AES_KEYSIZE_128 ) ) {
crypto_cipher_encrypt_one ( tctx - > fallback . cip , out , in ) ;
2006-10-04 18:48:57 +10:00
return ;
2007-11-10 19:29:33 +08:00
}
2006-10-04 18:48:57 +10:00
2019-10-10 21:51:32 -07:00
geode_aes_crypt ( tctx , in , out , AES_BLOCK_SIZE , NULL ,
AES_MODE_ECB , AES_DIR_ENCRYPT ) ;
2006-10-04 18:48:57 +10:00
}
static void
geode_decrypt ( struct crypto_tfm * tfm , u8 * out , const u8 * in )
{
2019-10-10 21:51:32 -07:00
const struct geode_aes_tfm_ctx * tctx = crypto_tfm_ctx ( tfm ) ;
2006-10-04 18:48:57 +10:00
2019-10-10 21:51:32 -07:00
if ( unlikely ( tctx - > keylen ! = AES_KEYSIZE_128 ) ) {
crypto_cipher_decrypt_one ( tctx - > fallback . cip , out , in ) ;
2006-10-04 18:48:57 +10:00
return ;
2007-11-10 19:29:33 +08:00
}
2006-10-04 18:48:57 +10:00
2019-10-10 21:51:32 -07:00
geode_aes_crypt ( tctx , in , out , AES_BLOCK_SIZE , NULL ,
AES_MODE_ECB , AES_DIR_DECRYPT ) ;
2006-10-04 18:48:57 +10:00
}
2007-11-10 19:29:33 +08:00
static int fallback_init_cip ( struct crypto_tfm * tfm )
{
2014-05-14 11:40:57 +02:00
const char * name = crypto_tfm_alg_name ( tfm ) ;
2019-10-10 21:51:32 -07:00
struct geode_aes_tfm_ctx * tctx = crypto_tfm_ctx ( tfm ) ;
2007-11-10 19:29:33 +08:00
2019-10-10 21:51:32 -07:00
tctx - > fallback . cip = crypto_alloc_cipher ( name , 0 ,
CRYPTO_ALG_NEED_FALLBACK ) ;
2007-11-10 19:29:33 +08:00
2019-10-10 21:51:32 -07:00
if ( IS_ERR ( tctx - > fallback . cip ) ) {
2007-11-10 19:29:33 +08:00
printk ( KERN_ERR " Error allocating fallback algo %s \n " , name ) ;
2019-10-10 21:51:32 -07:00
return PTR_ERR ( tctx - > fallback . cip ) ;
2007-11-10 19:29:33 +08:00
}
return 0 ;
}
static void fallback_exit_cip ( struct crypto_tfm * tfm )
{
2019-10-10 21:51:32 -07:00
struct geode_aes_tfm_ctx * tctx = crypto_tfm_ctx ( tfm ) ;
2007-11-10 19:29:33 +08:00
2019-10-10 21:51:32 -07:00
crypto_free_cipher ( tctx - > fallback . cip ) ;
2007-11-10 19:29:33 +08:00
}
2006-10-04 18:48:57 +10:00
static struct crypto_alg geode_alg = {
2007-11-10 19:29:33 +08:00
. cra_name = " aes " ,
. cra_driver_name = " geode-aes " ,
. cra_priority = 300 ,
. cra_alignmask = 15 ,
. cra_flags = CRYPTO_ALG_TYPE_CIPHER |
CRYPTO_ALG_NEED_FALLBACK ,
. cra_init = fallback_init_cip ,
. cra_exit = fallback_exit_cip ,
2014-05-14 11:36:38 +02:00
. cra_blocksize = AES_BLOCK_SIZE ,
2019-10-10 21:51:32 -07:00
. cra_ctxsize = sizeof ( struct geode_aes_tfm_ctx ) ,
2007-11-10 19:29:33 +08:00
. cra_module = THIS_MODULE ,
. cra_u = {
. cipher = {
. cia_min_keysize = AES_MIN_KEY_SIZE ,
. cia_max_keysize = AES_MAX_KEY_SIZE ,
. cia_setkey = geode_setkey_cip ,
. cia_encrypt = geode_encrypt ,
. cia_decrypt = geode_decrypt
2006-10-04 18:48:57 +10:00
}
}
} ;
2019-10-10 21:51:32 -07:00
static int geode_init_skcipher ( struct crypto_skcipher * tfm )
2006-10-04 18:48:57 +10:00
{
2019-10-10 21:51:32 -07:00
const char * name = crypto_tfm_alg_name ( & tfm - > base ) ;
struct geode_aes_tfm_ctx * tctx = crypto_skcipher_ctx ( tfm ) ;
2007-11-10 19:29:33 +08:00
2019-10-10 21:51:32 -07:00
tctx - > fallback . skcipher =
crypto_alloc_skcipher ( name , 0 , CRYPTO_ALG_NEED_FALLBACK |
CRYPTO_ALG_ASYNC ) ;
if ( IS_ERR ( tctx - > fallback . skcipher ) ) {
printk ( KERN_ERR " Error allocating fallback algo %s \n " , name ) ;
return PTR_ERR ( tctx - > fallback . skcipher ) ;
2006-10-04 18:48:57 +10:00
}
2019-10-10 21:51:32 -07:00
crypto_skcipher_set_reqsize ( tfm , sizeof ( struct skcipher_request ) +
crypto_skcipher_reqsize ( tctx - > fallback . skcipher ) ) ;
return 0 ;
2006-10-04 18:48:57 +10:00
}
2019-10-10 21:51:32 -07:00
static void geode_exit_skcipher ( struct crypto_skcipher * tfm )
2006-10-04 18:48:57 +10:00
{
2019-10-10 21:51:32 -07:00
struct geode_aes_tfm_ctx * tctx = crypto_skcipher_ctx ( tfm ) ;
2006-10-04 18:48:57 +10:00
2019-10-10 21:51:32 -07:00
crypto_free_skcipher ( tctx - > fallback . skcipher ) ;
}
2007-11-10 19:29:33 +08:00
2019-10-10 21:51:32 -07:00
static int geode_skcipher_crypt ( struct skcipher_request * req , int mode , int dir )
{
struct crypto_skcipher * tfm = crypto_skcipher_reqtfm ( req ) ;
const struct geode_aes_tfm_ctx * tctx = crypto_skcipher_ctx ( tfm ) ;
struct skcipher_walk walk ;
unsigned int nbytes ;
int err ;
if ( unlikely ( tctx - > keylen ! = AES_KEYSIZE_128 ) ) {
struct skcipher_request * subreq = skcipher_request_ctx ( req ) ;
* subreq = * req ;
skcipher_request_set_tfm ( subreq , tctx - > fallback . skcipher ) ;
if ( dir = = AES_DIR_DECRYPT )
return crypto_skcipher_decrypt ( subreq ) ;
else
return crypto_skcipher_encrypt ( subreq ) ;
}
2006-10-04 18:48:57 +10:00
2019-10-10 21:51:32 -07:00
err = skcipher_walk_virt ( & walk , req , false ) ;
2006-10-04 18:48:57 +10:00
2019-10-10 21:51:32 -07:00
while ( ( nbytes = walk . nbytes ) ! = 0 ) {
geode_aes_crypt ( tctx , walk . src . virt . addr , walk . dst . virt . addr ,
round_down ( nbytes , AES_BLOCK_SIZE ) ,
walk . iv , mode , dir ) ;
err = skcipher_walk_done ( & walk , nbytes % AES_BLOCK_SIZE ) ;
2006-10-04 18:48:57 +10:00
}
return err ;
}
2019-10-10 21:51:32 -07:00
static int geode_cbc_encrypt ( struct skcipher_request * req )
2007-11-10 19:29:33 +08:00
{
2019-10-10 21:51:32 -07:00
return geode_skcipher_crypt ( req , AES_MODE_CBC , AES_DIR_ENCRYPT ) ;
2007-11-10 19:29:33 +08:00
}
2019-10-10 21:51:32 -07:00
static int geode_cbc_decrypt ( struct skcipher_request * req )
2007-11-10 19:29:33 +08:00
{
2019-10-10 21:51:32 -07:00
return geode_skcipher_crypt ( req , AES_MODE_CBC , AES_DIR_DECRYPT ) ;
2007-11-10 19:29:33 +08:00
}
2019-10-10 21:51:32 -07:00
static int geode_ecb_encrypt ( struct skcipher_request * req )
2006-10-04 18:48:57 +10:00
{
2019-10-10 21:51:32 -07:00
return geode_skcipher_crypt ( req , AES_MODE_ECB , AES_DIR_ENCRYPT ) ;
2006-10-04 18:48:57 +10:00
}
2019-10-10 21:51:32 -07:00
static int geode_ecb_decrypt ( struct skcipher_request * req )
2006-10-04 18:48:57 +10:00
{
2019-10-10 21:51:32 -07:00
return geode_skcipher_crypt ( req , AES_MODE_ECB , AES_DIR_DECRYPT ) ;
2006-10-04 18:48:57 +10:00
}
2019-10-10 21:51:32 -07:00
static struct skcipher_alg geode_skcipher_algs [ ] = {
{
. base . cra_name = " cbc(aes) " ,
. base . cra_driver_name = " cbc-aes-geode " ,
. base . cra_priority = 400 ,
. base . cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY |
CRYPTO_ALG_NEED_FALLBACK ,
. base . cra_blocksize = AES_BLOCK_SIZE ,
. base . cra_ctxsize = sizeof ( struct geode_aes_tfm_ctx ) ,
. base . cra_alignmask = 15 ,
. base . cra_module = THIS_MODULE ,
. init = geode_init_skcipher ,
. exit = geode_exit_skcipher ,
. setkey = geode_setkey_skcipher ,
. encrypt = geode_cbc_encrypt ,
. decrypt = geode_cbc_decrypt ,
. min_keysize = AES_MIN_KEY_SIZE ,
. max_keysize = AES_MAX_KEY_SIZE ,
. ivsize = AES_BLOCK_SIZE ,
} , {
. base . cra_name = " ecb(aes) " ,
. base . cra_driver_name = " ecb-aes-geode " ,
. base . cra_priority = 400 ,
. base . cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY |
CRYPTO_ALG_NEED_FALLBACK ,
. base . cra_blocksize = AES_BLOCK_SIZE ,
. base . cra_ctxsize = sizeof ( struct geode_aes_tfm_ctx ) ,
. base . cra_alignmask = 15 ,
. base . cra_module = THIS_MODULE ,
. init = geode_init_skcipher ,
. exit = geode_exit_skcipher ,
. setkey = geode_setkey_skcipher ,
. encrypt = geode_ecb_encrypt ,
. decrypt = geode_ecb_decrypt ,
. min_keysize = AES_MIN_KEY_SIZE ,
. max_keysize = AES_MAX_KEY_SIZE ,
} ,
2006-10-04 18:48:57 +10:00
} ;
2012-12-21 13:14:09 -08:00
static void geode_aes_remove ( struct pci_dev * dev )
2006-10-04 18:48:57 +10:00
{
crypto_unregister_alg ( & geode_alg ) ;
2019-10-10 21:51:32 -07:00
crypto_unregister_skciphers ( geode_skcipher_algs ,
ARRAY_SIZE ( geode_skcipher_algs ) ) ;
2006-10-04 18:48:57 +10:00
pci_iounmap ( dev , _iobase ) ;
_iobase = NULL ;
pci_release_regions ( dev ) ;
pci_disable_device ( dev ) ;
}
2012-12-21 13:14:09 -08:00
static int geode_aes_probe ( struct pci_dev * dev , const struct pci_device_id * id )
2006-10-04 18:48:57 +10:00
{
int ret ;
2017-07-06 14:44:56 -04:00
2010-04-19 21:02:41 +08:00
ret = pci_enable_device ( dev ) ;
if ( ret )
2006-10-04 18:48:57 +10:00
return ret ;
2010-04-19 21:02:41 +08:00
ret = pci_request_regions ( dev , " geode-aes " ) ;
if ( ret )
2006-10-04 18:48:57 +10:00
goto eenable ;
_iobase = pci_iomap ( dev , 0 , 0 ) ;
if ( _iobase = = NULL ) {
ret = - ENOMEM ;
goto erequest ;
}
spin_lock_init ( & lock ) ;
/* Clear any pending activity */
iowrite32 ( AES_INTR_PENDING | AES_INTR_MASK , _iobase + AES_INTR_REG ) ;
2010-04-19 21:02:41 +08:00
ret = crypto_register_alg ( & geode_alg ) ;
if ( ret )
2006-10-04 18:48:57 +10:00
goto eiomap ;
2019-10-10 21:51:32 -07:00
ret = crypto_register_skciphers ( geode_skcipher_algs ,
ARRAY_SIZE ( geode_skcipher_algs ) ) ;
2010-04-19 21:02:41 +08:00
if ( ret )
2006-10-04 18:48:57 +10:00
goto ealg ;
2014-05-14 11:36:41 +02:00
dev_notice ( & dev - > dev , " GEODE AES engine enabled. \n " ) ;
2006-10-04 18:48:57 +10:00
return 0 ;
ealg :
crypto_unregister_alg ( & geode_alg ) ;
eiomap :
pci_iounmap ( dev , _iobase ) ;
erequest :
pci_release_regions ( dev ) ;
eenable :
pci_disable_device ( dev ) ;
2014-05-14 11:36:41 +02:00
dev_err ( & dev - > dev , " GEODE AES initialization failed. \n " ) ;
2006-10-04 18:48:57 +10:00
return ret ;
}
static struct pci_device_id geode_aes_tbl [ ] = {
2017-07-06 14:44:56 -04:00
{ PCI_VDEVICE ( AMD , PCI_DEVICE_ID_AMD_LX_AES ) , } ,
2006-10-04 18:48:57 +10:00
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , geode_aes_tbl ) ;
static struct pci_driver geode_aes_driver = {
. name = " Geode LX AES " ,
. id_table = geode_aes_tbl ,
. probe = geode_aes_probe ,
2012-12-21 13:14:09 -08:00
. remove = geode_aes_remove ,
2006-10-04 18:48:57 +10:00
} ;
2012-08-27 15:45:31 +05:30
module_pci_driver ( geode_aes_driver ) ;
2006-10-04 18:48:57 +10:00
MODULE_AUTHOR ( " Advanced Micro Devices, Inc. " ) ;
MODULE_DESCRIPTION ( " Geode LX Hardware AES driver " ) ;
MODULE_LICENSE ( " GPL " ) ;