blob: 95e45dcc5a297625ac6b2d8781773dc011f288db [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
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +01003 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * Wrappers of assembly checksum functions for x86-64.
5 */
6
7#include <asm/checksum.h>
8#include <linux/module.h>
9
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010010/**
11 * csum_partial_copy_from_user - Copy and checksum from user space.
12 * @src: source address (user space)
Linus Torvalds1da177e2005-04-16 15:20:36 -070013 * @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.
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010017 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070018 * Returns an 32bit unfolded checksum of the buffer.
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010019 * 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)
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010024{
Linus Torvalds1da177e2005-04-16 15:20:36 -070025 might_sleep();
26 *errp = 0;
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010027 if (likely(access_ok(VERIFY_READ, src, len))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070028 /* 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. */
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010034 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);
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010043 src += 2;
44 dst += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -070045 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);
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010050 if (likely(*errp == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 return isum;
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010052 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070053 *errp = -EFAULT;
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010054 memset(dst, 0, len);
55 return isum;
56}
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
58EXPORT_SYMBOL(csum_partial_copy_from_user);
59
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010060/**
61 * csum_partial_copy_to_user - Copy and checksum to user space.
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 * @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.
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010067 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 * Returns an 32bit unfolded checksum of the buffer.
69 * src and dst are best aligned to 64bits.
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010070 */
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)
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010074{
Linus Torvalds1da177e2005-04-16 15:20:36 -070075 might_sleep();
76 if (unlikely(!access_ok(VERIFY_WRITE, dst, len))) {
77 *errp = -EFAULT;
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010078 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 }
80
81 if (unlikely((unsigned long)dst & 6)) {
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010082 while (((unsigned long)dst & 6) && len >= 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 __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;
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010089 src += 2;
90 dst += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 len -= 2;
92 }
93 }
94
95 *errp = 0;
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +010096 return csum_partial_copy_generic(src, (void __force *)dst, len, isum, NULL, errp);
97}
Linus Torvalds1da177e2005-04-16 15:20:36 -070098
99EXPORT_SYMBOL(csum_partial_copy_to_user);
100
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +0100101/**
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 * 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)
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +0100107 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 * Returns an 32bit unfolded checksum of the buffer.
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +0100109 */
Al Viroa4f89fb2006-11-14 21:20:08 -0800110__wsum
111csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +0100112{
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;
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +0100122
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"
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +0100127 " adcq (%[daddr]),%[sum]\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 " adcq 8(%[daddr]),%[sum]\n"
129 " adcq $0,%[sum]\n"
Paolo Ciarrocchi0df025b2008-02-17 14:56:50 +0100130 : [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);