blob: fd42a4a095fc1bb822f1dc033df8a35fa58070b5 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* 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 */
Al Viroa4f89fb2006-11-14 21:20:08 -080021__wsum
22csum_partial_copy_from_user(const void __user *src, void *dst,
23 int len, __wsum isum, int *errp)
Linus Torvalds1da177e2005-04-16 15:20:36 -070024{
25 might_sleep();
26 *errp = 0;
27 if (likely(access_ok(VERIFY_READ,src, len))) {
28 /* Why 6, not 7? To handle odd addresses aligned we
29 would need to do considerable complications to fix the
30 checksum which is defined as an 16bit accumulator. The
31 fix alignment code is primarily for performance
32 compatibility with 32bit and that will handle odd
33 addresses slowly too. */
34 if (unlikely((unsigned long)src & 6)) {
35 while (((unsigned long)src & 6) && len >= 2) {
36 __u16 val16;
Al Viroa4f89fb2006-11-14 21:20:08 -080037 *errp = __get_user(val16, (const __u16 __user *)src);
Linus Torvalds1da177e2005-04-16 15:20:36 -070038 if (*errp)
39 return isum;
40 *(__u16 *)dst = val16;
Al Viroa4f89fb2006-11-14 21:20:08 -080041 isum = (__force __wsum)add32_with_carry(
42 (__force unsigned)isum, val16);
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 src += 2;
44 dst += 2;
45 len -= 2;
46 }
47 }
Al Viroa4f89fb2006-11-14 21:20:08 -080048 isum = csum_partial_copy_generic((__force const void *)src,
49 dst, len, isum, errp, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070050 if (likely(*errp == 0))
51 return isum;
52 }
53 *errp = -EFAULT;
54 memset(dst,0,len);
55 return isum;
56}
57
58EXPORT_SYMBOL(csum_partial_copy_from_user);
59
60/**
61 * csum_partial_copy_to_user - Copy and checksum to user space.
62 * @src: source address
63 * @dst: destination address (user space)
64 * @len: number of bytes to be copied.
65 * @isum: initial sum that is added into the result (32bit unfolded)
66 * @errp: set to -EFAULT for an bad destination address.
67 *
68 * Returns an 32bit unfolded checksum of the buffer.
69 * src and dst are best aligned to 64bits.
70 */
Al Viroa4f89fb2006-11-14 21:20:08 -080071__wsum
72csum_partial_copy_to_user(const void *src, void __user *dst,
73 int len, __wsum isum, int *errp)
Linus Torvalds1da177e2005-04-16 15:20:36 -070074{
75 might_sleep();
76 if (unlikely(!access_ok(VERIFY_WRITE, dst, len))) {
77 *errp = -EFAULT;
78 return 0;
79 }
80
81 if (unlikely((unsigned long)dst & 6)) {
82 while (((unsigned long)dst & 6) && len >= 2) {
83 __u16 val16 = *(__u16 *)src;
Al Viroa4f89fb2006-11-14 21:20:08 -080084 isum = (__force __wsum)add32_with_carry(
85 (__force unsigned)isum, val16);
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 *errp = __put_user(val16, (__u16 __user *)dst);
87 if (*errp)
88 return isum;
89 src += 2;
90 dst += 2;
91 len -= 2;
92 }
93 }
94
95 *errp = 0;
96 return csum_partial_copy_generic(src, (void __force *)dst,len,isum,NULL,errp);
97}
98
99EXPORT_SYMBOL(csum_partial_copy_to_user);
100
101/**
102 * csum_partial_copy_nocheck - Copy and checksum.
103 * @src: source address
104 * @dst: destination address
105 * @len: number of bytes to be copied.
106 * @isum: initial sum that is added into the result (32bit unfolded)
107 *
108 * Returns an 32bit unfolded checksum of the buffer.
109 */
Al Viroa4f89fb2006-11-14 21:20:08 -0800110__wsum
111csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112{
113 return csum_partial_copy_generic(src,dst,len,sum,NULL,NULL);
114}
Andi Kleen2ee60e172006-06-26 13:59:44 +0200115EXPORT_SYMBOL(csum_partial_copy_nocheck);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
Al Viroa4f89fb2006-11-14 21:20:08 -0800117__sum16 csum_ipv6_magic(const struct in6_addr *saddr,
118 const struct in6_addr *daddr,
119 __u32 len, unsigned short proto, __wsum sum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120{
121 __u64 rest, sum64;
122
Al Viroa4f89fb2006-11-14 21:20:08 -0800123 rest = (__force __u64)htonl(len) + (__force __u64)htons(proto) +
124 (__force __u64)sum;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 asm(" addq (%[saddr]),%[sum]\n"
126 " adcq 8(%[saddr]),%[sum]\n"
127 " adcq (%[daddr]),%[sum]\n"
128 " adcq 8(%[daddr]),%[sum]\n"
129 " adcq $0,%[sum]\n"
130 : [sum] "=r" (sum64)
131 : "[sum]" (rest),[saddr] "r" (saddr), [daddr] "r" (daddr));
Al Viroa4f89fb2006-11-14 21:20:08 -0800132 return csum_fold((__force __wsum)add32_with_carry(sum64 & 0xffffffff, sum64>>32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133}
134
135EXPORT_SYMBOL(csum_ipv6_magic);