Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/ia64/lib/csum_partial_copy.c
10817 views
1
/*
2
* Network Checksum & Copy routine
3
*
4
* Copyright (C) 1999, 2003-2004 Hewlett-Packard Co
5
* Stephane Eranian <[email protected]>
6
*
7
* Most of the code has been imported from Linux/Alpha
8
*/
9
10
#include <linux/module.h>
11
#include <linux/types.h>
12
#include <linux/string.h>
13
14
#include <asm/uaccess.h>
15
16
/*
17
* XXX Fixme: those 2 inlines are meant for debugging and will go away
18
*/
19
static inline unsigned
20
short from64to16(unsigned long x)
21
{
22
/* add up 32-bit words for 33 bits */
23
x = (x & 0xffffffff) + (x >> 32);
24
/* add up 16-bit and 17-bit words for 17+c bits */
25
x = (x & 0xffff) + (x >> 16);
26
/* add up 16-bit and 2-bit for 16+c bit */
27
x = (x & 0xffff) + (x >> 16);
28
/* add up carry.. */
29
x = (x & 0xffff) + (x >> 16);
30
return x;
31
}
32
33
static inline
34
unsigned long do_csum_c(const unsigned char * buff, int len, unsigned int psum)
35
{
36
int odd, count;
37
unsigned long result = (unsigned long)psum;
38
39
if (len <= 0)
40
goto out;
41
odd = 1 & (unsigned long) buff;
42
if (odd) {
43
result = *buff << 8;
44
len--;
45
buff++;
46
}
47
count = len >> 1; /* nr of 16-bit words.. */
48
if (count) {
49
if (2 & (unsigned long) buff) {
50
result += *(unsigned short *) buff;
51
count--;
52
len -= 2;
53
buff += 2;
54
}
55
count >>= 1; /* nr of 32-bit words.. */
56
if (count) {
57
if (4 & (unsigned long) buff) {
58
result += *(unsigned int *) buff;
59
count--;
60
len -= 4;
61
buff += 4;
62
}
63
count >>= 1; /* nr of 64-bit words.. */
64
if (count) {
65
unsigned long carry = 0;
66
do {
67
unsigned long w = *(unsigned long *) buff;
68
count--;
69
buff += 8;
70
result += carry;
71
result += w;
72
carry = (w > result);
73
} while (count);
74
result += carry;
75
result = (result & 0xffffffff) + (result >> 32);
76
}
77
if (len & 4) {
78
result += *(unsigned int *) buff;
79
buff += 4;
80
}
81
}
82
if (len & 2) {
83
result += *(unsigned short *) buff;
84
buff += 2;
85
}
86
}
87
if (len & 1)
88
result += *buff;
89
90
result = from64to16(result);
91
92
if (odd)
93
result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
94
95
out:
96
return result;
97
}
98
99
/*
100
* XXX Fixme
101
*
102
* This is very ugly but temporary. THIS NEEDS SERIOUS ENHANCEMENTS.
103
* But it's very tricky to get right even in C.
104
*/
105
extern unsigned long do_csum(const unsigned char *, long);
106
107
__wsum
108
csum_partial_copy_from_user(const void __user *src, void *dst,
109
int len, __wsum psum, int *errp)
110
{
111
unsigned long result;
112
113
/* XXX Fixme
114
* for now we separate the copy from checksum for obvious
115
* alignment difficulties. Look at the Alpha code and you'll be
116
* scared.
117
*/
118
119
if (__copy_from_user(dst, src, len) != 0 && errp)
120
*errp = -EFAULT;
121
122
result = do_csum(dst, len);
123
124
/* add in old sum, and carry.. */
125
result += (__force u32)psum;
126
/* 32+c bits -> 32 bits */
127
result = (result & 0xffffffff) + (result >> 32);
128
return (__force __wsum)result;
129
}
130
131
EXPORT_SYMBOL(csum_partial_copy_from_user);
132
133
__wsum
134
csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
135
{
136
return csum_partial_copy_from_user((__force const void __user *)src,
137
dst, len, sum, NULL);
138
}
139
140
EXPORT_SYMBOL(csum_partial_copy_nocheck);
141
142