diff options
author | Chris Zankel <czankel@tensilica.com> | 2005-06-24 01:01:26 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-24 03:05:22 -0400 |
commit | 9a8fd5589902153a134111ed7a40f9cca1f83254 (patch) | |
tree | 6f7a06de25bdf0b2d94623794c2cbbc66b5a77f6 /include/asm-xtensa/checksum.h | |
parent | 3f65ce4d141e435e54c20ed2379d983d362a2cb5 (diff) |
[PATCH] xtensa: Architecture support for Tensilica Xtensa Part 6
The attached patches provides part 6 of an architecture implementation for the
Tensilica Xtensa CPU series.
Signed-off-by: Chris Zankel <chris@zankel.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'include/asm-xtensa/checksum.h')
-rw-r--r-- | include/asm-xtensa/checksum.h | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/include/asm-xtensa/checksum.h b/include/asm-xtensa/checksum.h new file mode 100644 index 000000000000..1a00fad19929 --- /dev/null +++ b/include/asm-xtensa/checksum.h | |||
@@ -0,0 +1,264 @@ | |||
1 | /* | ||
2 | * include/asm-xtensa/checksum.h | ||
3 | * | ||
4 | * This file is subject to the terms and conditions of the GNU General Public | ||
5 | * License. See the file "COPYING" in the main directory of this archive | ||
6 | * for more details. | ||
7 | * | ||
8 | * Copyright (C) 2001 - 2005 Tensilica Inc. | ||
9 | */ | ||
10 | |||
11 | #ifndef _XTENSA_CHECKSUM_H | ||
12 | #define _XTENSA_CHECKSUM_H | ||
13 | |||
14 | #include <linux/config.h> | ||
15 | #include <linux/in6.h> | ||
16 | #include <xtensa/config/core.h> | ||
17 | |||
18 | /* | ||
19 | * computes the checksum of a memory block at buff, length len, | ||
20 | * and adds in "sum" (32-bit) | ||
21 | * | ||
22 | * returns a 32-bit number suitable for feeding into itself | ||
23 | * or csum_tcpudp_magic | ||
24 | * | ||
25 | * this function must be called with even lengths, except | ||
26 | * for the last fragment, which may be odd | ||
27 | * | ||
28 | * it's best to have buff aligned on a 32-bit boundary | ||
29 | */ | ||
30 | asmlinkage unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum); | ||
31 | |||
32 | /* | ||
33 | * the same as csum_partial, but copies from src while it | ||
34 | * checksums, and handles user-space pointer exceptions correctly, when needed. | ||
35 | * | ||
36 | * here even more important to align src and dst on a 32-bit (or even | ||
37 | * better 64-bit) boundary | ||
38 | */ | ||
39 | |||
40 | asmlinkage unsigned int csum_partial_copy_generic( const char *src, char *dst, int len, int sum, | ||
41 | int *src_err_ptr, int *dst_err_ptr); | ||
42 | |||
43 | /* | ||
44 | * Note: when you get a NULL pointer exception here this means someone | ||
45 | * passed in an incorrect kernel address to one of these functions. | ||
46 | * | ||
47 | * If you use these functions directly please don't forget the | ||
48 | * verify_area(). | ||
49 | */ | ||
50 | extern __inline__ | ||
51 | unsigned int csum_partial_copy_nocheck ( const char *src, char *dst, | ||
52 | int len, int sum) | ||
53 | { | ||
54 | return csum_partial_copy_generic ( src, dst, len, sum, NULL, NULL); | ||
55 | } | ||
56 | |||
57 | extern __inline__ | ||
58 | unsigned int csum_partial_copy_from_user ( const char *src, char *dst, | ||
59 | int len, int sum, int *err_ptr) | ||
60 | { | ||
61 | return csum_partial_copy_generic ( src, dst, len, sum, err_ptr, NULL); | ||
62 | } | ||
63 | |||
64 | /* | ||
65 | * These are the old (and unsafe) way of doing checksums, a warning message will be | ||
66 | * printed if they are used and an exeption occurs. | ||
67 | * | ||
68 | * these functions should go away after some time. | ||
69 | */ | ||
70 | |||
71 | #define csum_partial_copy_fromuser csum_partial_copy | ||
72 | unsigned int csum_partial_copy( const char *src, char *dst, int len, int sum); | ||
73 | |||
74 | /* | ||
75 | * Fold a partial checksum | ||
76 | */ | ||
77 | |||
78 | static __inline__ unsigned int csum_fold(unsigned int sum) | ||
79 | { | ||
80 | unsigned int __dummy; | ||
81 | __asm__("extui %1, %0, 16, 16\n\t" | ||
82 | "extui %0 ,%0, 0, 16\n\t" | ||
83 | "add %0, %0, %1\n\t" | ||
84 | "slli %1, %0, 16\n\t" | ||
85 | "add %0, %0, %1\n\t" | ||
86 | "extui %0, %0, 16, 16\n\t" | ||
87 | "neg %0, %0\n\t" | ||
88 | "addi %0, %0, -1\n\t" | ||
89 | "extui %0, %0, 0, 16\n\t" | ||
90 | : "=r" (sum), "=&r" (__dummy) | ||
91 | : "0" (sum)); | ||
92 | return sum; | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * This is a version of ip_compute_csum() optimized for IP headers, | ||
97 | * which always checksum on 4 octet boundaries. | ||
98 | */ | ||
99 | static __inline__ unsigned short ip_fast_csum(unsigned char * iph, unsigned int ihl) | ||
100 | { | ||
101 | unsigned int sum, tmp, endaddr; | ||
102 | |||
103 | __asm__ __volatile__( | ||
104 | "sub %0, %0, %0\n\t" | ||
105 | #if XCHAL_HAVE_LOOPS | ||
106 | "loopgtz %2, 2f\n\t" | ||
107 | #else | ||
108 | "beqz %2, 2f\n\t" | ||
109 | "slli %4, %2, 2\n\t" | ||
110 | "add %4, %4, %1\n\t" | ||
111 | "0:\t" | ||
112 | #endif | ||
113 | "l32i %3, %1, 0\n\t" | ||
114 | "add %0, %0, %3\n\t" | ||
115 | "bgeu %0, %3, 1f\n\t" | ||
116 | "addi %0, %0, 1\n\t" | ||
117 | "1:\t" | ||
118 | "addi %1, %1, 4\n\t" | ||
119 | #if !XCHAL_HAVE_LOOPS | ||
120 | "blt %1, %4, 0b\n\t" | ||
121 | #endif | ||
122 | "2:\t" | ||
123 | /* Since the input registers which are loaded with iph and ihl | ||
124 | are modified, we must also specify them as outputs, or gcc | ||
125 | will assume they contain their original values. */ | ||
126 | : "=r" (sum), "=r" (iph), "=r" (ihl), "=&r" (tmp), "=&r" (endaddr) | ||
127 | : "1" (iph), "2" (ihl)); | ||
128 | |||
129 | return csum_fold(sum); | ||
130 | } | ||
131 | |||
132 | static __inline__ unsigned long csum_tcpudp_nofold(unsigned long saddr, | ||
133 | unsigned long daddr, | ||
134 | unsigned short len, | ||
135 | unsigned short proto, | ||
136 | unsigned int sum) | ||
137 | { | ||
138 | |||
139 | #ifdef __XTENSA_EL__ | ||
140 | unsigned long len_proto = (ntohs(len)<<16)+proto*256; | ||
141 | #elif defined(__XTENSA_EB__) | ||
142 | unsigned long len_proto = (proto<<16)+len; | ||
143 | #else | ||
144 | # error processor byte order undefined! | ||
145 | #endif | ||
146 | __asm__("add %0, %0, %1\n\t" | ||
147 | "bgeu %0, %1, 1f\n\t" | ||
148 | "addi %0, %0, 1\n\t" | ||
149 | "1:\t" | ||
150 | "add %0, %0, %2\n\t" | ||
151 | "bgeu %0, %2, 1f\n\t" | ||
152 | "addi %0, %0, 1\n\t" | ||
153 | "1:\t" | ||
154 | "add %0, %0, %3\n\t" | ||
155 | "bgeu %0, %3, 1f\n\t" | ||
156 | "addi %0, %0, 1\n\t" | ||
157 | "1:\t" | ||
158 | : "=r" (sum), "=r" (len_proto) | ||
159 | : "r" (daddr), "r" (saddr), "1" (len_proto), "0" (sum)); | ||
160 | return sum; | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * computes the checksum of the TCP/UDP pseudo-header | ||
165 | * returns a 16-bit checksum, already complemented | ||
166 | */ | ||
167 | static __inline__ unsigned short int csum_tcpudp_magic(unsigned long saddr, | ||
168 | unsigned long daddr, | ||
169 | unsigned short len, | ||
170 | unsigned short proto, | ||
171 | unsigned int sum) | ||
172 | { | ||
173 | return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum)); | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * this routine is used for miscellaneous IP-like checksums, mainly | ||
178 | * in icmp.c | ||
179 | */ | ||
180 | |||
181 | static __inline__ unsigned short ip_compute_csum(unsigned char * buff, int len) | ||
182 | { | ||
183 | return csum_fold (csum_partial(buff, len, 0)); | ||
184 | } | ||
185 | |||
186 | #define _HAVE_ARCH_IPV6_CSUM | ||
187 | static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr, | ||
188 | struct in6_addr *daddr, | ||
189 | __u32 len, | ||
190 | unsigned short proto, | ||
191 | unsigned int sum) | ||
192 | { | ||
193 | unsigned int __dummy; | ||
194 | __asm__("l32i %1, %2, 0\n\t" | ||
195 | "add %0, %0, %1\n\t" | ||
196 | "bgeu %0, %1, 1f\n\t" | ||
197 | "addi %0, %0, 1\n\t" | ||
198 | "1:\t" | ||
199 | "l32i %1, %2, 4\n\t" | ||
200 | "add %0, %0, %1\n\t" | ||
201 | "bgeu %0, %1, 1f\n\t" | ||
202 | "addi %0, %0, 1\n\t" | ||
203 | "1:\t" | ||
204 | "l32i %1, %2, 8\n\t" | ||
205 | "add %0, %0, %1\n\t" | ||
206 | "bgeu %0, %1, 1f\n\t" | ||
207 | "addi %0, %0, 1\n\t" | ||
208 | "1:\t" | ||
209 | "l32i %1, %2, 12\n\t" | ||
210 | "add %0, %0, %1\n\t" | ||
211 | "bgeu %0, %1, 1f\n\t" | ||
212 | "addi %0, %0, 1\n\t" | ||
213 | "1:\t" | ||
214 | "l32i %1, %3, 0\n\t" | ||
215 | "add %0, %0, %1\n\t" | ||
216 | "bgeu %0, %1, 1f\n\t" | ||
217 | "addi %0, %0, 1\n\t" | ||
218 | "1:\t" | ||
219 | "l32i %1, %3, 4\n\t" | ||
220 | "add %0, %0, %1\n\t" | ||
221 | "bgeu %0, %1, 1f\n\t" | ||
222 | "addi %0, %0, 1\n\t" | ||
223 | "1:\t" | ||
224 | "l32i %1, %3, 8\n\t" | ||
225 | "add %0, %0, %1\n\t" | ||
226 | "bgeu %0, %1, 1f\n\t" | ||
227 | "addi %0, %0, 1\n\t" | ||
228 | "1:\t" | ||
229 | "l32i %1, %3, 12\n\t" | ||
230 | "add %0, %0, %1\n\t" | ||
231 | "bgeu %0, %1, 1f\n\t" | ||
232 | "addi %0, %0, 1\n\t" | ||
233 | "1:\t" | ||
234 | "add %0, %0, %4\n\t" | ||
235 | "bgeu %0, %4, 1f\n\t" | ||
236 | "addi %0, %0, 1\n\t" | ||
237 | "1:\t" | ||
238 | "add %0, %0, %5\n\t" | ||
239 | "bgeu %0, %5, 1f\n\t" | ||
240 | "addi %0, %0, 1\n\t" | ||
241 | "1:\t" | ||
242 | : "=r" (sum), "=&r" (__dummy) | ||
243 | : "r" (saddr), "r" (daddr), | ||
244 | "r" (htonl(len)), "r" (htonl(proto)), "0" (sum)); | ||
245 | |||
246 | return csum_fold(sum); | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * Copy and checksum to user | ||
251 | */ | ||
252 | #define HAVE_CSUM_COPY_USER | ||
253 | static __inline__ unsigned int csum_and_copy_to_user (const char *src, char *dst, | ||
254 | int len, int sum, int *err_ptr) | ||
255 | { | ||
256 | if (access_ok(VERIFY_WRITE, dst, len)) | ||
257 | return csum_partial_copy_generic(src, dst, len, sum, NULL, err_ptr); | ||
258 | |||
259 | if (len) | ||
260 | *err_ptr = -EFAULT; | ||
261 | |||
262 | return -1; /* invalid checksum */ | ||
263 | } | ||
264 | #endif | ||