1 /*
2 * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <ipxe/xfer.h>
30 #include <ipxe/iobuf.h>
31 #include <ipxe/umalloc.h>
32 #include <ipxe/profile.h>
33 #include <ipxe/xferbuf.h>
34
35 /** @file
36 *
37 * Data transfer buffer
38 *
39 */
40
41 /** Data delivery profiler */
42 static struct profiler xferbuf_deliver_profiler __profiler =
43 { .name = "xferbuf.deliver" };
44
45 /** Data write profiler */
46 static struct profiler xferbuf_write_profiler __profiler =
47 { .name = "xferbuf.write" };
48
49 /** Data read profiler */
50 static struct profiler xferbuf_read_profiler __profiler =
51 { .name = "xferbuf.read" };
52
53 /**
54 * Free data transfer buffer
55 *
56 * @v xferbuf Data transfer buffer
57 */
xferbuf_free(struct xfer_buffer * xferbuf)58 void xferbuf_free ( struct xfer_buffer *xferbuf ) {
59
60 xferbuf->op->realloc ( xferbuf, 0 );
61 xferbuf->len = 0;
62 xferbuf->pos = 0;
63 }
64
65 /**
66 * Ensure that data transfer buffer is large enough for the specified size
67 *
68 * @v xferbuf Data transfer buffer
69 * @v len Required minimum size
70 * @ret rc Return status code
71 */
xferbuf_ensure_size(struct xfer_buffer * xferbuf,size_t len)72 static int xferbuf_ensure_size ( struct xfer_buffer *xferbuf, size_t len ) {
73 int rc;
74
75 /* If buffer is already large enough, do nothing */
76 if ( len <= xferbuf->len )
77 return 0;
78
79 /* Extend buffer */
80 if ( ( rc = xferbuf->op->realloc ( xferbuf, len ) ) != 0 ) {
81 DBGC ( xferbuf, "XFERBUF %p could not extend buffer to "
82 "%zd bytes: %s\n", xferbuf, len, strerror ( rc ) );
83 return rc;
84 }
85 xferbuf->len = len;
86
87 return 0;
88 }
89
90 /**
91 * Write to data transfer buffer
92 *
93 * @v xferbuf Data transfer buffer
94 * @v offset Starting offset
95 * @v data Data to write
96 * @v len Length of data
97 */
xferbuf_write(struct xfer_buffer * xferbuf,size_t offset,const void * data,size_t len)98 int xferbuf_write ( struct xfer_buffer *xferbuf, size_t offset,
99 const void *data, size_t len ) {
100 size_t max_len;
101 int rc;
102
103 /* Check for overflow */
104 max_len = ( offset + len );
105 if ( max_len < offset )
106 return -EOVERFLOW;
107
108 /* Ensure buffer is large enough to contain this write */
109 if ( ( rc = xferbuf_ensure_size ( xferbuf, max_len ) ) != 0 )
110 return rc;
111
112 /* Copy data to buffer */
113 profile_start ( &xferbuf_write_profiler );
114 xferbuf->op->write ( xferbuf, offset, data, len );
115 profile_stop ( &xferbuf_write_profiler );
116
117 return 0;
118 }
119
120 /**
121 * Read from data transfer buffer
122 *
123 * @v xferbuf Data transfer buffer
124 * @v offset Starting offset
125 * @v data Data to write
126 * @v len Length of data
127 */
xferbuf_read(struct xfer_buffer * xferbuf,size_t offset,void * data,size_t len)128 int xferbuf_read ( struct xfer_buffer *xferbuf, size_t offset,
129 void *data, size_t len ) {
130
131 /* Check that read is within buffer range */
132 if ( ( offset > xferbuf->len ) ||
133 ( len > ( xferbuf->len - offset ) ) )
134 return -ENOENT;
135
136 /* Copy data from buffer */
137 profile_start ( &xferbuf_read_profiler );
138 xferbuf->op->read ( xferbuf, offset, data, len );
139 profile_stop ( &xferbuf_read_profiler );
140
141 return 0;
142 }
143
144 /**
145 * Add received data to data transfer buffer
146 *
147 * @v xferbuf Data transfer buffer
148 * @v iobuf I/O buffer
149 * @v meta Data transfer metadata
150 * @ret rc Return status code
151 */
xferbuf_deliver(struct xfer_buffer * xferbuf,struct io_buffer * iobuf,struct xfer_metadata * meta)152 int xferbuf_deliver ( struct xfer_buffer *xferbuf, struct io_buffer *iobuf,
153 struct xfer_metadata *meta ) {
154 size_t len = iob_len ( iobuf );
155 size_t pos;
156 int rc;
157
158 /* Start profiling */
159 profile_start ( &xferbuf_deliver_profiler );
160
161 /* Calculate new buffer position */
162 pos = xferbuf->pos;
163 if ( meta->flags & XFER_FL_ABS_OFFSET )
164 pos = 0;
165 pos += meta->offset;
166
167 /* Write data to buffer */
168 if ( ( rc = xferbuf_write ( xferbuf, pos, iobuf->data, len ) ) != 0 )
169 goto done;
170
171 /* Update current buffer position */
172 xferbuf->pos = ( pos + len );
173
174 done:
175 free_iob ( iobuf );
176 profile_stop ( &xferbuf_deliver_profiler );
177 return rc;
178 }
179
180 /**
181 * Reallocate malloc()-based data buffer
182 *
183 * @v xferbuf Data transfer buffer
184 * @v len New length (or zero to free buffer)
185 * @ret rc Return status code
186 */
xferbuf_malloc_realloc(struct xfer_buffer * xferbuf,size_t len)187 static int xferbuf_malloc_realloc ( struct xfer_buffer *xferbuf, size_t len ) {
188 void *new_data;
189
190 new_data = realloc ( xferbuf->data, len );
191 if ( ! new_data )
192 return -ENOSPC;
193 xferbuf->data = new_data;
194 return 0;
195 }
196
197 /**
198 * Write data to malloc()-based data buffer
199 *
200 * @v xferbuf Data transfer buffer
201 * @v offset Starting offset
202 * @v data Data to copy
203 * @v len Length of data
204 */
xferbuf_malloc_write(struct xfer_buffer * xferbuf,size_t offset,const void * data,size_t len)205 static void xferbuf_malloc_write ( struct xfer_buffer *xferbuf, size_t offset,
206 const void *data, size_t len ) {
207
208 memcpy ( ( xferbuf->data + offset ), data, len );
209 }
210
211 /**
212 * Read data from malloc()-based data buffer
213 *
214 * @v xferbuf Data transfer buffer
215 * @v offset Starting offset
216 * @v data Data to read
217 * @v len Length of data
218 */
xferbuf_malloc_read(struct xfer_buffer * xferbuf,size_t offset,void * data,size_t len)219 static void xferbuf_malloc_read ( struct xfer_buffer *xferbuf, size_t offset,
220 void *data, size_t len ) {
221
222 memcpy ( data, ( xferbuf->data + offset ), len );
223 }
224
225 /** malloc()-based data buffer operations */
226 struct xfer_buffer_operations xferbuf_malloc_operations = {
227 .realloc = xferbuf_malloc_realloc,
228 .write = xferbuf_malloc_write,
229 .read = xferbuf_malloc_read,
230 };
231
232 /**
233 * Reallocate umalloc()-based data buffer
234 *
235 * @v xferbuf Data transfer buffer
236 * @v len New length (or zero to free buffer)
237 * @ret rc Return status code
238 */
xferbuf_umalloc_realloc(struct xfer_buffer * xferbuf,size_t len)239 static int xferbuf_umalloc_realloc ( struct xfer_buffer *xferbuf, size_t len ) {
240 userptr_t *udata = xferbuf->data;
241 userptr_t new_udata;
242
243 new_udata = urealloc ( *udata, len );
244 if ( ! new_udata )
245 return -ENOSPC;
246 *udata = new_udata;
247 return 0;
248 }
249
250 /**
251 * Write data to umalloc()-based data buffer
252 *
253 * @v xferbuf Data transfer buffer
254 * @v offset Starting offset
255 * @v data Data to copy
256 * @v len Length of data
257 */
xferbuf_umalloc_write(struct xfer_buffer * xferbuf,size_t offset,const void * data,size_t len)258 static void xferbuf_umalloc_write ( struct xfer_buffer *xferbuf, size_t offset,
259 const void *data, size_t len ) {
260 userptr_t *udata = xferbuf->data;
261
262 copy_to_user ( *udata, offset, data, len );
263 }
264
265 /**
266 * Read data from umalloc()-based data buffer
267 *
268 * @v xferbuf Data transfer buffer
269 * @v offset Starting offset
270 * @v data Data to read
271 * @v len Length of data
272 */
xferbuf_umalloc_read(struct xfer_buffer * xferbuf,size_t offset,void * data,size_t len)273 static void xferbuf_umalloc_read ( struct xfer_buffer *xferbuf, size_t offset,
274 void *data, size_t len ) {
275 userptr_t *udata = xferbuf->data;
276
277 copy_from_user ( data, *udata, offset, len );
278 }
279
280 /** umalloc()-based data buffer operations */
281 struct xfer_buffer_operations xferbuf_umalloc_operations = {
282 .realloc = xferbuf_umalloc_realloc,
283 .write = xferbuf_umalloc_write,
284 .read = xferbuf_umalloc_read,
285 };
286
287 /**
288 * Get underlying data transfer buffer
289 *
290 * @v interface Data transfer interface
291 * @ret xferbuf Data transfer buffer, or NULL on error
292 *
293 * This call will check that the xfer_buffer() handler belongs to the
294 * destination interface which also provides xfer_deliver() for this
295 * interface.
296 *
297 * This is done to prevent accidental accesses to a data transfer
298 * buffer which may be located behind a non-transparent datapath via a
299 * series of pass-through interfaces.
300 */
xfer_buffer(struct interface * intf)301 struct xfer_buffer * xfer_buffer ( struct interface *intf ) {
302 struct interface *dest;
303 xfer_buffer_TYPE ( void * ) *op =
304 intf_get_dest_op ( intf, xfer_buffer, &dest );
305 void *object = intf_object ( dest );
306 struct interface *xfer_deliver_dest;
307 struct xfer_buffer *xferbuf;
308
309 /* Check that this operation is provided by the same interface
310 * which handles xfer_deliver().
311 */
312 ( void ) intf_get_dest_op ( intf, xfer_deliver, &xfer_deliver_dest );
313
314 if ( op && ( dest == xfer_deliver_dest ) ) {
315 xferbuf = op ( object );
316 } else {
317 /* Default is to not have a data transfer buffer */
318 xferbuf = NULL;
319 }
320
321 intf_put ( xfer_deliver_dest );
322 intf_put ( dest );
323 return xferbuf;
324 }
325