xref: /kvmtool/util/iovec.c (revision c492534f3ac930107a628c0fadd4c152e8972b35)
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