2005-04-16 15:20:36 -07:00
/* Copyright 2002,2003 Andi Kleen, SuSE Labs.
* Subject to the GNU Public License v .2
*
* Wrappers of assembly checksum functions for x86 - 64.
*/
# include <asm/checksum.h>
# include <linux/module.h>
/**
* csum_partial_copy_from_user - Copy and checksum from user space .
* @ src : source address ( user space )
* @ dst : destination address
* @ len : number of bytes to be copied .
* @ isum : initial sum that is added into the result ( 32 bit unfolded )
* @ errp : set to - EFAULT for an bad source address .
*
* Returns an 32 bit unfolded checksum of the buffer .
* src and dst are best aligned to 64 bits .
*/
2006-11-14 21:20:08 -08:00
__wsum
csum_partial_copy_from_user ( const void __user * src , void * dst ,
int len , __wsum isum , int * errp )
2005-04-16 15:20:36 -07:00
{
might_sleep ( ) ;
* errp = 0 ;
if ( likely ( access_ok ( VERIFY_READ , src , len ) ) ) {
/* Why 6, not 7? To handle odd addresses aligned we
would need to do considerable complications to fix the
checksum which is defined as an 16 bit accumulator . The
fix alignment code is primarily for performance
compatibility with 32 bit and that will handle odd
addresses slowly too . */
if ( unlikely ( ( unsigned long ) src & 6 ) ) {
while ( ( ( unsigned long ) src & 6 ) & & len > = 2 ) {
__u16 val16 ;
2006-11-14 21:20:08 -08:00
* errp = __get_user ( val16 , ( const __u16 __user * ) src ) ;
2005-04-16 15:20:36 -07:00
if ( * errp )
return isum ;
* ( __u16 * ) dst = val16 ;
2006-11-14 21:20:08 -08:00
isum = ( __force __wsum ) add32_with_carry (
( __force unsigned ) isum , val16 ) ;
2005-04-16 15:20:36 -07:00
src + = 2 ;
dst + = 2 ;
len - = 2 ;
}
}
2006-11-14 21:20:08 -08:00
isum = csum_partial_copy_generic ( ( __force const void * ) src ,
dst , len , isum , errp , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( likely ( * errp = = 0 ) )
return isum ;
}
* errp = - EFAULT ;
memset ( dst , 0 , len ) ;
return isum ;
}
EXPORT_SYMBOL ( csum_partial_copy_from_user ) ;
/**
* csum_partial_copy_to_user - Copy and checksum to user space .
* @ src : source address
* @ dst : destination address ( user space )
* @ len : number of bytes to be copied .
* @ isum : initial sum that is added into the result ( 32 bit unfolded )
* @ errp : set to - EFAULT for an bad destination address .
*
* Returns an 32 bit unfolded checksum of the buffer .
* src and dst are best aligned to 64 bits .
*/
2006-11-14 21:20:08 -08:00
__wsum
csum_partial_copy_to_user ( const void * src , void __user * dst ,
int len , __wsum isum , int * errp )
2005-04-16 15:20:36 -07:00
{
might_sleep ( ) ;
if ( unlikely ( ! access_ok ( VERIFY_WRITE , dst , len ) ) ) {
* errp = - EFAULT ;
return 0 ;
}
if ( unlikely ( ( unsigned long ) dst & 6 ) ) {
while ( ( ( unsigned long ) dst & 6 ) & & len > = 2 ) {
__u16 val16 = * ( __u16 * ) src ;
2006-11-14 21:20:08 -08:00
isum = ( __force __wsum ) add32_with_carry (
( __force unsigned ) isum , val16 ) ;
2005-04-16 15:20:36 -07:00
* errp = __put_user ( val16 , ( __u16 __user * ) dst ) ;
if ( * errp )
return isum ;
src + = 2 ;
dst + = 2 ;
len - = 2 ;
}
}
* errp = 0 ;
return csum_partial_copy_generic ( src , ( void __force * ) dst , len , isum , NULL , errp ) ;
}
EXPORT_SYMBOL ( csum_partial_copy_to_user ) ;
/**
* csum_partial_copy_nocheck - Copy and checksum .
* @ src : source address
* @ dst : destination address
* @ len : number of bytes to be copied .
* @ isum : initial sum that is added into the result ( 32 bit unfolded )
*
* Returns an 32 bit unfolded checksum of the buffer .
*/
2006-11-14 21:20:08 -08:00
__wsum
csum_partial_copy_nocheck ( const void * src , void * dst , int len , __wsum sum )
2005-04-16 15:20:36 -07:00
{
return csum_partial_copy_generic ( src , dst , len , sum , NULL , NULL ) ;
}
2006-06-26 13:59:44 +02:00
EXPORT_SYMBOL ( csum_partial_copy_nocheck ) ;
2005-04-16 15:20:36 -07:00
2006-11-14 21:20:08 -08:00
__sum16 csum_ipv6_magic ( const struct in6_addr * saddr ,
const struct in6_addr * daddr ,
__u32 len , unsigned short proto , __wsum sum )
2005-04-16 15:20:36 -07:00
{
__u64 rest , sum64 ;
2006-11-14 21:20:08 -08:00
rest = ( __force __u64 ) htonl ( len ) + ( __force __u64 ) htons ( proto ) +
( __force __u64 ) sum ;
2005-04-16 15:20:36 -07:00
asm ( " addq (%[saddr]),%[sum] \n "
" adcq 8(%[saddr]),%[sum] \n "
" adcq (%[daddr]),%[sum] \n "
" adcq 8(%[daddr]),%[sum] \n "
" adcq $0,%[sum] \n "
: [ sum ] " =r " ( sum64 )
: " [sum] " ( rest ) , [ saddr ] " r " ( saddr ) , [ daddr ] " r " ( daddr ) ) ;
2006-11-14 21:20:08 -08:00
return csum_fold ( ( __force __wsum ) add32_with_carry ( sum64 & 0xffffffff , sum64 > > 32 ) ) ;
2005-04-16 15:20:36 -07:00
}
EXPORT_SYMBOL ( csum_ipv6_magic ) ;