1d8f36177SSasha Levin /*
2d8f36177SSasha Levin * iovec manipulation routines.
3d8f36177SSasha Levin *
4d8f36177SSasha Levin *
5d8f36177SSasha Levin * This program is free software; you can redistribute it and/or
6d8f36177SSasha Levin * modify it under the terms of the GNU General Public License
7d8f36177SSasha Levin * as published by the Free Software Foundation; either version
8d8f36177SSasha Levin * 2 of the License, or (at your option) any later version.
9d8f36177SSasha Levin *
10d8f36177SSasha Levin * Fixes:
11d8f36177SSasha Levin * Andrew Lunn : Errors in iovec copying.
12d8f36177SSasha Levin * Pedro Roque : Added memcpy_fromiovecend and
13d8f36177SSasha Levin * csum_..._fromiovecend.
14d8f36177SSasha Levin * Andi Kleen : fixed error handling for 2.1
15d8f36177SSasha Levin * Alexey Kuznetsov: 2.1 optimisations
16d8f36177SSasha Levin * Andi Kleen : Fix csum*fromiovecend for IPv6.
17d8f36177SSasha Levin */
18d8f36177SSasha Levin
19d8f36177SSasha Levin #include <linux/errno.h>
20d8f36177SSasha Levin #include <linux/kernel.h>
21d8f36177SSasha Levin #include <linux/compiler.h>
22d8f36177SSasha Levin #include <sys/uio.h>
23d8f36177SSasha Levin #include <kvm/iovec.h>
24d8f36177SSasha Levin #include <string.h>
25d8f36177SSasha Levin
26d8f36177SSasha Levin /*
27d8f36177SSasha Levin * Copy kernel to iovec. Returns -EFAULT on error.
28d8f36177SSasha Levin *
29d8f36177SSasha Levin * Note: this modifies the original iovec.
30d8f36177SSasha Levin */
31d8f36177SSasha Levin
memcpy_toiovec(struct iovec * iov,unsigned char * kdata,int len)32d8f36177SSasha Levin int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
33d8f36177SSasha Levin {
34d8f36177SSasha Levin while (len > 0) {
35d8f36177SSasha Levin if (iov->iov_len) {
36d8f36177SSasha Levin int copy = min_t(unsigned int, iov->iov_len, len);
37d8f36177SSasha Levin memcpy(iov->iov_base, kdata, copy);
38d8f36177SSasha Levin kdata += copy;
39d8f36177SSasha Levin len -= copy;
40d8f36177SSasha Levin iov->iov_len -= copy;
41d8f36177SSasha Levin iov->iov_base += copy;
42d8f36177SSasha Levin }
43d8f36177SSasha Levin iov++;
44d8f36177SSasha Levin }
45d8f36177SSasha Levin
46d8f36177SSasha Levin return 0;
47d8f36177SSasha Levin }
48d8f36177SSasha Levin
49d8f36177SSasha Levin /*
50d8f36177SSasha Levin * Copy kernel to iovec. Returns -EFAULT on error.
51d8f36177SSasha Levin */
52d8f36177SSasha Levin
memcpy_toiovecend(const struct iovec * iov,unsigned char * kdata,size_t offset,int len)53d8f36177SSasha Levin int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata,
54d8f36177SSasha Levin size_t offset, int len)
55d8f36177SSasha Levin {
56d8f36177SSasha Levin int copy;
57d8f36177SSasha Levin for (; len > 0; ++iov) {
58d8f36177SSasha Levin /* Skip over the finished iovecs */
59d8f36177SSasha Levin if (unlikely(offset >= iov->iov_len)) {
60d8f36177SSasha Levin offset -= iov->iov_len;
61d8f36177SSasha Levin continue;
62d8f36177SSasha Levin }
63d8f36177SSasha Levin copy = min_t(unsigned int, iov->iov_len - offset, len);
64d8f36177SSasha Levin memcpy(iov->iov_base + offset, kdata, copy);
65d8f36177SSasha Levin offset = 0;
66d8f36177SSasha Levin kdata += copy;
67d8f36177SSasha Levin len -= copy;
68d8f36177SSasha Levin }
69d8f36177SSasha Levin
70d8f36177SSasha Levin return 0;
71d8f36177SSasha Levin }
72d8f36177SSasha Levin
73d8f36177SSasha Levin /*
74d8f36177SSasha Levin * Copy iovec to kernel. Returns -EFAULT on error.
75d8f36177SSasha Levin *
76d8f36177SSasha Levin * Note: this modifies the original iovec.
77d8f36177SSasha Levin */
78d8f36177SSasha Levin
memcpy_fromiovec(unsigned char * kdata,struct iovec * iov,int len)79d8f36177SSasha Levin int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
80d8f36177SSasha Levin {
81d8f36177SSasha Levin while (len > 0) {
82d8f36177SSasha Levin if (iov->iov_len) {
83d8f36177SSasha Levin int copy = min_t(unsigned int, len, iov->iov_len);
84d8f36177SSasha Levin memcpy(kdata, iov->iov_base, copy);
85d8f36177SSasha Levin len -= copy;
86d8f36177SSasha Levin kdata += copy;
87d8f36177SSasha Levin iov->iov_base += copy;
88d8f36177SSasha Levin iov->iov_len -= copy;
89d8f36177SSasha Levin }
90d8f36177SSasha Levin iov++;
91d8f36177SSasha Levin }
92d8f36177SSasha Levin
93d8f36177SSasha Levin return 0;
94d8f36177SSasha Levin }
95d8f36177SSasha Levin
96d8f36177SSasha Levin /*
97*c492534fSJean-Philippe Brucker * Copy at most @len bytes from iovec to buffer.
98*c492534fSJean-Philippe Brucker * Returns the remaining len.
99*c492534fSJean-Philippe Brucker *
100*c492534fSJean-Philippe Brucker * Note: this modifies the original iovec, the iov pointer, and the
101*c492534fSJean-Philippe Brucker * iovcount to describe the remaining buffer.
102*c492534fSJean-Philippe Brucker */
memcpy_fromiovec_safe(void * buf,struct iovec ** iov,size_t len,size_t * iovcount)103*c492534fSJean-Philippe Brucker ssize_t memcpy_fromiovec_safe(void *buf, struct iovec **iov, size_t len,
104*c492534fSJean-Philippe Brucker size_t *iovcount)
105*c492534fSJean-Philippe Brucker {
106*c492534fSJean-Philippe Brucker size_t copy;
107*c492534fSJean-Philippe Brucker
108*c492534fSJean-Philippe Brucker while (len && *iovcount) {
109*c492534fSJean-Philippe Brucker copy = min(len, (*iov)->iov_len);
110*c492534fSJean-Philippe Brucker memcpy(buf, (*iov)->iov_base, copy);
111*c492534fSJean-Philippe Brucker buf += copy;
112*c492534fSJean-Philippe Brucker len -= copy;
113*c492534fSJean-Philippe Brucker
114*c492534fSJean-Philippe Brucker /* Move iov cursor */
115*c492534fSJean-Philippe Brucker (*iov)->iov_base += copy;
116*c492534fSJean-Philippe Brucker (*iov)->iov_len -= copy;
117*c492534fSJean-Philippe Brucker
118*c492534fSJean-Philippe Brucker if (!(*iov)->iov_len) {
119*c492534fSJean-Philippe Brucker (*iov)++;
120*c492534fSJean-Philippe Brucker (*iovcount)--;
121*c492534fSJean-Philippe Brucker }
122*c492534fSJean-Philippe Brucker }
123*c492534fSJean-Philippe Brucker
124*c492534fSJean-Philippe Brucker return len;
125*c492534fSJean-Philippe Brucker }
126*c492534fSJean-Philippe Brucker
127*c492534fSJean-Philippe Brucker /*
128d8f36177SSasha Levin * Copy iovec from kernel. Returns -EFAULT on error.
129d8f36177SSasha Levin */
130d8f36177SSasha Levin
memcpy_fromiovecend(unsigned char * kdata,const struct iovec * iov,size_t offset,int len)131d8f36177SSasha Levin int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
132d8f36177SSasha Levin size_t offset, int len)
133d8f36177SSasha Levin {
134d8f36177SSasha Levin /* Skip over the finished iovecs */
135d8f36177SSasha Levin while (offset >= iov->iov_len) {
136d8f36177SSasha Levin offset -= iov->iov_len;
137d8f36177SSasha Levin iov++;
138d8f36177SSasha Levin }
139d8f36177SSasha Levin
140d8f36177SSasha Levin while (len > 0) {
141d8f36177SSasha Levin char *base = iov->iov_base + offset;
142d8f36177SSasha Levin int copy = min_t(unsigned int, len, iov->iov_len - offset);
143d8f36177SSasha Levin
144d8f36177SSasha Levin offset = 0;
145d8f36177SSasha Levin memcpy(kdata, base, copy);
146d8f36177SSasha Levin len -= copy;
147d8f36177SSasha Levin kdata += copy;
148d8f36177SSasha Levin iov++;
149d8f36177SSasha Levin }
150d8f36177SSasha Levin
151d8f36177SSasha Levin return 0;
152d8f36177SSasha Levin }
153