2005-04-17 02:20:36 +04:00
/*
* Cryptographic API .
*
* Cipher operations .
*
* Copyright ( c ) 2002 James Morris < jmorris @ intercode . com . au >
* 2002 Adam J . Richter < adam @ yggdrasil . com >
* 2004 Jean - Luc Cooke < jlcooke @ certainkey . com >
*
* 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 ; either version 2 of the License , or ( at your option )
* any later version .
*
*/
2007-12-07 13:52:49 +03:00
# include <crypto/scatterwalk.h>
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
# include <linux/mm.h>
2006-08-12 15:56:17 +04:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <linux/pagemap.h>
# include <linux/highmem.h>
2006-08-12 15:56:17 +04:00
# include <linux/scatterlist.h>
static inline void memcpy_dir ( void * buf , void * sgdata , size_t nbytes , int out )
2005-04-17 02:20:36 +04:00
{
2006-08-12 15:56:17 +04:00
void * src = out ? buf : sgdata ;
void * dst = out ? sgdata : buf ;
memcpy ( dst , src , nbytes ) ;
2005-04-17 02:20:36 +04:00
}
void scatterwalk_start ( struct scatter_walk * walk , struct scatterlist * sg )
{
walk - > sg = sg ;
BUG_ON ( ! sg - > length ) ;
walk - > offset = sg - > offset ;
}
2006-08-12 15:56:17 +04:00
EXPORT_SYMBOL_GPL ( scatterwalk_start ) ;
2005-04-17 02:20:36 +04:00
2011-11-25 19:14:17 +04:00
void * scatterwalk_map ( struct scatter_walk * walk )
2005-04-17 02:20:36 +04:00
{
2011-11-25 19:14:17 +04:00
return kmap_atomic ( scatterwalk_page ( walk ) ) +
2006-08-12 15:56:17 +04:00
offset_in_page ( walk - > offset ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-12 15:56:17 +04:00
EXPORT_SYMBOL_GPL ( scatterwalk_map ) ;
2005-04-17 02:20:36 +04:00
static void scatterwalk_pagedone ( struct scatter_walk * walk , int out ,
unsigned int more )
{
2007-03-31 06:58:20 +04:00
if ( out ) {
struct page * page ;
2007-10-22 21:40:16 +04:00
page = sg_page ( walk - > sg ) + ( ( walk - > offset - 1 ) > > PAGE_SHIFT ) ;
2015-06-01 11:22:03 +03:00
/* Test ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE first as
* PageSlab cannot be optimised away per se due to
* use of volatile pointer .
*/
if ( ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE & & ! PageSlab ( page ) )
2009-02-09 06:22:14 +03:00
flush_dcache_page ( page ) ;
2007-03-31 06:58:20 +04:00
}
2005-04-17 02:20:36 +04:00
if ( more ) {
2006-08-12 15:56:17 +04:00
walk - > offset + = PAGE_SIZE - 1 ;
walk - > offset & = PAGE_MASK ;
if ( walk - > offset > = walk - > sg - > offset + walk - > sg - > length )
2015-01-20 11:06:16 +03:00
scatterwalk_start ( walk , sg_next ( walk - > sg ) ) ;
2005-04-17 02:20:36 +04:00
}
}
void scatterwalk_done ( struct scatter_walk * walk , int out , int more )
{
2010-05-19 08:06:29 +04:00
if ( ! ( scatterwalk_pagelen ( walk ) & ( PAGE_SIZE - 1 ) ) | | ! more )
2005-04-17 02:20:36 +04:00
scatterwalk_pagedone ( walk , out , more ) ;
}
2006-08-12 15:56:17 +04:00
EXPORT_SYMBOL_GPL ( scatterwalk_done ) ;
2005-04-17 02:20:36 +04:00
2006-08-12 15:56:17 +04:00
void scatterwalk_copychunks ( void * buf , struct scatter_walk * walk ,
size_t nbytes , int out )
2005-04-17 02:20:36 +04:00
{
2006-08-12 15:56:17 +04:00
for ( ; ; ) {
unsigned int len_this_page = scatterwalk_pagelen ( walk ) ;
u8 * vaddr ;
if ( len_this_page > nbytes )
len_this_page = nbytes ;
2011-11-25 19:14:17 +04:00
vaddr = scatterwalk_map ( walk ) ;
2006-08-12 15:56:17 +04:00
memcpy_dir ( buf , vaddr , len_this_page , out ) ;
2011-11-25 19:14:17 +04:00
scatterwalk_unmap ( vaddr ) ;
2006-08-12 15:56:17 +04:00
2007-03-31 06:16:20 +04:00
scatterwalk_advance ( walk , len_this_page ) ;
2007-03-21 00:50:12 +03:00
2006-08-12 15:56:17 +04:00
if ( nbytes = = len_this_page )
break ;
buf + = len_this_page ;
nbytes - = len_this_page ;
2005-04-17 02:20:36 +04:00
scatterwalk_pagedone ( walk , out , 1 ) ;
2005-07-07 00:51:31 +04:00
}
2005-04-17 02:20:36 +04:00
}
2006-08-12 15:56:17 +04:00
EXPORT_SYMBOL_GPL ( scatterwalk_copychunks ) ;
2007-08-29 12:31:34 +04:00
void scatterwalk_map_and_copy ( void * buf , struct scatterlist * sg ,
unsigned int start , unsigned int nbytes , int out )
{
struct scatter_walk walk ;
2015-05-21 10:11:12 +03:00
struct scatterlist tmp [ 2 ] ;
2007-08-29 12:31:34 +04:00
2007-12-13 19:44:11 +03:00
if ( ! nbytes )
return ;
2015-05-21 10:11:12 +03:00
sg = scatterwalk_ffwd ( tmp , sg , start ) ;
2007-08-29 12:31:34 +04:00
2015-05-21 10:11:12 +03:00
if ( sg_page ( sg ) = = virt_to_page ( buf ) & &
sg - > offset = = offset_in_page ( buf ) )
return ;
2007-08-29 12:31:34 +04:00
2015-05-21 10:11:12 +03:00
scatterwalk_start ( & walk , sg ) ;
2007-08-29 12:31:34 +04:00
scatterwalk_copychunks ( buf , & walk , nbytes , out ) ;
scatterwalk_done ( & walk , out , 0 ) ;
}
EXPORT_SYMBOL_GPL ( scatterwalk_map_and_copy ) ;
2013-08-18 06:42:22 +04:00
int scatterwalk_bytes_sglen ( struct scatterlist * sg , int num_bytes )
{
int offset = 0 , n = 0 ;
/* num_bytes is too small */
if ( num_bytes < sg - > length )
return - 1 ;
do {
offset + = sg - > length ;
n + + ;
2015-01-20 11:06:16 +03:00
sg = sg_next ( sg ) ;
2013-08-18 06:42:22 +04:00
/* num_bytes is too large */
if ( unlikely ( ! sg & & ( num_bytes < offset ) ) )
return - 1 ;
} while ( sg & & ( num_bytes > offset ) ) ;
return n ;
}
EXPORT_SYMBOL_GPL ( scatterwalk_bytes_sglen ) ;
2015-05-21 10:10:59 +03:00
struct scatterlist * scatterwalk_ffwd ( struct scatterlist dst [ 2 ] ,
struct scatterlist * src ,
unsigned int len )
{
for ( ; ; ) {
if ( ! len )
return src ;
if ( src - > length > len )
break ;
len - = src - > length ;
src = sg_next ( src ) ;
}
2015-05-27 09:37:27 +03:00
sg_init_table ( dst , 2 ) ;
2015-05-21 10:10:59 +03:00
sg_set_page ( dst , sg_page ( src ) , src - > length - len , src - > offset + len ) ;
scatterwalk_crypto_chain ( dst , sg_next ( src ) , 0 , 2 ) ;
return dst ;
}
EXPORT_SYMBOL_GPL ( scatterwalk_ffwd ) ;