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 .
*
*/
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/pagemap.h>
# include <linux/highmem.h>
# include <asm/bug.h>
# include <asm/scatterlist.h>
# include "internal.h"
# include "scatterwalk.h"
enum km_type crypto_km_types [ ] = {
KM_USER0 ,
KM_USER1 ,
KM_SOFTIRQ0 ,
KM_SOFTIRQ1 ,
} ;
static void memcpy_dir ( void * buf , void * sgdata , size_t nbytes , int out )
{
if ( out )
memcpy ( sgdata , buf , nbytes ) ;
else
memcpy ( buf , sgdata , nbytes ) ;
}
void scatterwalk_start ( struct scatter_walk * walk , struct scatterlist * sg )
{
unsigned int rest_of_page ;
walk - > sg = sg ;
walk - > page = sg - > page ;
walk - > len_this_segment = sg - > length ;
BUG_ON ( ! sg - > length ) ;
rest_of_page = PAGE_CACHE_SIZE - ( sg - > offset & ( PAGE_CACHE_SIZE - 1 ) ) ;
walk - > len_this_page = min ( sg - > length , rest_of_page ) ;
walk - > offset = sg - > offset ;
}
void scatterwalk_map ( struct scatter_walk * walk , int out )
{
walk - > data = crypto_kmap ( walk - > page , out ) + walk - > offset ;
}
static inline void scatterwalk_unmap ( struct scatter_walk * walk , int out )
{
/* walk->data may be pointing the first byte of the next page;
however , we know we transfered at least one byte . So ,
walk - > data - 1 will be a virtual address in the mapped page . */
crypto_kunmap ( walk - > data - 1 , out ) ;
}
static void scatterwalk_pagedone ( struct scatter_walk * walk , int out ,
unsigned int more )
{
if ( out )
flush_dcache_page ( walk - > page ) ;
if ( more ) {
walk - > len_this_segment - = walk - > len_this_page ;
if ( walk - > len_this_segment ) {
walk - > page + + ;
walk - > len_this_page = min ( walk - > len_this_segment ,
( unsigned ) PAGE_CACHE_SIZE ) ;
walk - > offset = 0 ;
}
else
scatterwalk_start ( walk , sg_next ( walk - > sg ) ) ;
}
}
void scatterwalk_done ( struct scatter_walk * walk , int out , int more )
{
scatterwalk_unmap ( walk , out ) ;
if ( walk - > len_this_page = = 0 | | ! more )
scatterwalk_pagedone ( walk , out , more ) ;
}
/*
* Do not call this unless the total length of all of the fragments
* has been verified as multiple of the block size .
*/
int scatterwalk_copychunks ( void * buf , struct scatter_walk * walk ,
size_t nbytes , int out )
{
2005-07-07 00:51:31 +04:00
while ( nbytes > walk - > len_this_page ) {
2005-04-17 02:20:36 +04:00
memcpy_dir ( buf , walk - > data , walk - > len_this_page , out ) ;
buf + = walk - > len_this_page ;
nbytes - = walk - > len_this_page ;
scatterwalk_unmap ( walk , out ) ;
scatterwalk_pagedone ( walk , out , 1 ) ;
scatterwalk_map ( walk , out ) ;
2005-07-07 00:51:31 +04:00
}
2005-04-17 02:20:36 +04:00
memcpy_dir ( buf , walk - > data , nbytes , out ) ;
return nbytes ;
}