patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / arch / x86_64 / lib / csum-wrappers.c
1 /* Copyright 2002,2003 Andi Kleen, SuSE Labs.
2  * Subject to the GNU Public License v.2
3  * 
4  * Wrappers of assembly checksum functions for x86-64.
5  */
6
7 #include <asm/checksum.h>
8 #include <linux/module.h>
9
10 /** 
11  * csum_partial_copy_from_user - Copy and checksum from user space. 
12  * @src: source address (user space) 
13  * @dst: destination address
14  * @len: number of bytes to be copied.
15  * @isum: initial sum that is added into the result (32bit unfolded)
16  * @errp: set to -EFAULT for an bad source address.
17  * 
18  * Returns an 32bit unfolded checksum of the buffer.
19  * src and dst are best aligned to 64bits. 
20  */ 
21 unsigned int 
22 csum_partial_copy_from_user(const char __user *src, char *dst, 
23                             int len, unsigned int isum, int *errp)
24
25         *errp = 0;
26         if (likely(access_ok(VERIFY_READ,src, len))) { 
27                 /* Why 6, not 7? To handle odd addresses aligned we
28                    would need to do considerable complications to fix the
29                    checksum which is defined as an 16bit accumulator. The
30                    fix alignment code is primarily for performance
31                    compatibility with 32bit and that will handle odd
32                    addresses slowly too. */
33                 if (unlikely((unsigned long)src & 6)) {                 
34                         while (((unsigned long)src & 6) && len >= 2) { 
35                                 __u16 val16;                    
36                                 *errp = __get_user(val16, (__u16 __user *)src); 
37                                 if (*errp)
38                                         return isum;
39                                 *(__u16 *)dst = val16;
40                                 isum = add32_with_carry(isum, val16); 
41                                 src += 2; 
42                                 dst += 2; 
43                                 len -= 2;
44                         }
45                 }
46                 isum = csum_partial_copy_generic((void *)src,dst,len,isum,errp,NULL);
47                 if (likely(*errp == 0)) 
48                         return isum;
49         } 
50         *errp = -EFAULT;
51         memset(dst,0,len); 
52         return isum;            
53
54
55 EXPORT_SYMBOL(csum_partial_copy_from_user);
56
57 /** 
58  * csum_partial_copy_to_user - Copy and checksum to user space. 
59  * @src: source address
60  * @dst: destination address (user space)
61  * @len: number of bytes to be copied.
62  * @isum: initial sum that is added into the result (32bit unfolded)
63  * @errp: set to -EFAULT for an bad destination address.
64  * 
65  * Returns an 32bit unfolded checksum of the buffer.
66  * src and dst are best aligned to 64bits.
67  */ 
68 unsigned int 
69 csum_partial_copy_to_user(const char *src, char __user *dst, 
70                           int len, unsigned int isum, int *errp)
71
72         if (unlikely(!access_ok(VERIFY_WRITE, dst, len))) {
73                 *errp = -EFAULT;
74                 return 0; 
75         }
76
77         if (unlikely((unsigned long)dst & 6)) {
78                 while (((unsigned long)dst & 6) && len >= 2) { 
79                         __u16 val16 = *(__u16 *)src;
80                         isum = add32_with_carry(isum, val16);
81                         *errp = __put_user(val16, (__u16 __user *)dst);
82                         if (*errp)
83                                 return isum;
84                         src += 2; 
85                         dst += 2; 
86                         len -= 2;
87                 }
88         }
89
90         *errp = 0;
91         return csum_partial_copy_generic(src, (void *)dst,len,isum,NULL,errp); 
92
93
94 EXPORT_SYMBOL(csum_partial_copy_to_user);
95
96 /** 
97  * csum_partial_copy_nocheck - Copy and checksum.
98  * @src: source address
99  * @dst: destination address
100  * @len: number of bytes to be copied.
101  * @isum: initial sum that is added into the result (32bit unfolded)
102  * 
103  * Returns an 32bit unfolded checksum of the buffer.
104  */ 
105 unsigned int 
106 csum_partial_copy_nocheck(const char *src, char *dst, int len, unsigned int sum)
107
108         return csum_partial_copy_generic(src,dst,len,sum,NULL,NULL);
109
110
111 unsigned short csum_ipv6_magic(struct in6_addr *saddr, struct in6_addr *daddr,
112                                __u32 len, unsigned short proto, unsigned int sum) 
113 {
114         __u64 rest, sum64;
115      
116         rest = (__u64)htonl(len) + (__u64)htons(proto) + (__u64)sum;
117         asm("  addq (%[saddr]),%[sum]\n"
118             "  adcq 8(%[saddr]),%[sum]\n"
119             "  adcq (%[daddr]),%[sum]\n" 
120             "  adcq 8(%[daddr]),%[sum]\n"
121             "  adcq $0,%[sum]\n"
122             : [sum] "=r" (sum64) 
123             : "[sum]" (rest),[saddr] "r" (saddr), [daddr] "r" (daddr));
124         return csum_fold(add32_with_carry(sum64 & 0xffffffff, sum64>>32));
125 }
126
127 EXPORT_SYMBOL(csum_ipv6_magic);