Browse Source

Start restructuring pxe_tftp.c to cope with multicast (MTFTP) receives.

tags/v0.9.3
Michael Brown 17 years ago
parent
commit
5e4e267177
1 changed files with 231 additions and 147 deletions
  1. 231
    147
      src/interface/pxe/pxe_tftp.c

+ 231
- 147
src/interface/pxe/pxe_tftp.c View File

@@ -29,84 +29,179 @@
29 29
 #include <gpxe/uaccess.h>
30 30
 #include <gpxe/in.h>
31 31
 #include <gpxe/tftp.h>
32
-#include <gpxe/posix_io.h>
32
+#include <gpxe/xfer.h>
33
+#include <gpxe/open.h>
34
+#include <gpxe/process.h>
33 35
 #include <pxe.h>
34 36
 
35
-/** File descriptor for "single-file-only" PXE TFTP transfer */
36
-static int pxe_single_fd = -1;
37
+/** A PXE TFTP connection */
38
+struct pxe_tftp_connection {
39
+	/** Data transfer interface */
40
+	struct xfer_interface xfer;
41
+	/** Data buffer */
42
+	userptr_t buffer;
43
+	/** Size of data buffer */
44
+	size_t size;
45
+	/** Starting offset of data buffer */
46
+	size_t start;
47
+	/** File position */
48
+	size_t offset;
49
+	/** Maximum file position */
50
+	size_t max_offset;
51
+	/** Block size */
52
+	size_t blksize;
53
+	/** Block index */
54
+	unsigned int blkidx;
55
+	/** Overall return status code */
56
+	int rc;
57
+};
58
+
59
+/** The PXE TFTP connection */
60
+static struct pxe_tftp_connection pxe_tftp = {
61
+	.xfer = XFER_INIT ( &null_xfer_ops ),
62
+};
63
+
64
+/**
65
+ * Close PXE TFTP connection
66
+ *
67
+ * @v rc		Final status code
68
+ */
69
+static void pxe_tftp_close ( int rc ) {
70
+	xfer_nullify ( &pxe_tftp.xfer );
71
+	xfer_close ( &pxe_tftp.xfer, rc );
72
+	pxe_tftp.rc = rc;
73
+}
74
+
75
+/**
76
+ * Receive new data
77
+ *
78
+ * @v xfer		Data transfer interface
79
+ * @v iobuf		I/O buffer
80
+ * @v meta		Transfer metadata
81
+ * @ret rc		Return status code
82
+ */
83
+static int pxe_tftp_xfer_deliver_iob ( struct xfer_interface *xfer __unused,
84
+				       struct io_buffer *iobuf,
85
+				       struct xfer_metadata *meta ) {
86
+	size_t len = iob_len ( iobuf );
87
+	int rc = 0;
88
+
89
+	/* Calculate new buffer position */
90
+	if ( meta->whence != SEEK_CUR )
91
+		pxe_tftp.offset = 0;
92
+	pxe_tftp.offset += meta->offset;
93
+
94
+	/* Copy data block to buffer */
95
+	if ( len == 0 ) {
96
+		/* No data (pure seek); treat as success */
97
+	} else if ( pxe_tftp.offset < pxe_tftp.start ) {
98
+		DBG ( " buffer underrun at %zx (min %zx)",
99
+		      pxe_tftp.offset, pxe_tftp.start );
100
+		rc = -ENOBUFS;
101
+	} else if ( ( pxe_tftp.offset + len ) >
102
+		    ( pxe_tftp.start + pxe_tftp.size ) ) {
103
+		DBG ( " buffer overrun at %zx (max %zx)",
104
+		      ( pxe_tftp.offset + len ),
105
+		      ( pxe_tftp.start + pxe_tftp.size ) );
106
+		rc = -ENOBUFS;
107
+	} else {
108
+		copy_to_user ( pxe_tftp.buffer,
109
+			       ( pxe_tftp.offset - pxe_tftp.start ),
110
+			       iobuf->data, len );
111
+	}
112
+
113
+	/* Calculate new buffer position */
114
+	pxe_tftp.offset += len;
115
+
116
+	/* Mildly ugly hack; assume that the first non-zero seek
117
+	 * indicates the block size.
118
+	 */
119
+	if ( pxe_tftp.blksize == 0 )
120
+		pxe_tftp.blksize = pxe_tftp.offset;
37 121
 
38
-/** Block size for "single-file-only" PXE TFTP transfer */
39
-static size_t pxe_single_blksize;
122
+	/* Record maximum offset as the file size */
123
+	if ( pxe_tftp.max_offset < pxe_tftp.offset )
124
+		pxe_tftp.max_offset = pxe_tftp.offset;
40 125
 
41
-/** Current block index for "single-file-only" PXE TFTP transfer */
42
-static unsigned int pxe_single_blkidx;
126
+	/* Terminate transfer on error */
127
+	if ( rc != 0 )
128
+		pxe_tftp_close ( rc );
43 129
 
44
-/** Length of a PXE-derived URI
130
+	free_iob ( iobuf );
131
+	return rc;
132
+}
133
+
134
+/**
135
+ * Handle close() event
45 136
  *
46
- * The "single-file-only" API calls use a filename field of 128 bytes.
47
- * 256 bytes provides plenty of space for constructing the (temporary)
48
- * full URI.
137
+ * @v xfer		Data transfer interface
138
+ * @v rc		Reason for close
49 139
  */
50
-#define PXE_URI_LEN 256
140
+static void pxe_tftp_xfer_close ( struct xfer_interface *xfer __unused,
141
+				  int rc ) {
142
+	pxe_tftp_close ( rc );
143
+}
144
+
145
+static struct xfer_interface_operations pxe_tftp_xfer_ops = {
146
+	.close		= pxe_tftp_xfer_close,
147
+	.vredirect	= xfer_vopen,
148
+	.window		= unlimited_xfer_window,
149
+	.alloc_iob	= default_xfer_alloc_iob,
150
+	.deliver_iob	= pxe_tftp_xfer_deliver_iob,
151
+	.deliver_raw	= xfer_deliver_as_iob,
152
+};
51 153
 
52 154
 /**
53
- * Build PXE URI string
155
+ * Maximum length of a PXE TFTP URI
54 156
  *
55
- * @v uri_string	URI string to fill in
56
- * @v ipaddress		Server IP address (in network byte order)
57
- * @v port		Server port (in network byte order)
58
- * @v filename		File name
59
- * @v blksize		Requested block size, or 0
157
+ * The PXE TFTP API provides 128 characters for the filename; the
158
+ * extra 128 bytes allow for the remainder of the URI.
159
+ */
160
+#define PXE_TFTP_URI_LEN 256
161
+
162
+/**
163
+ * Open PXE TFTP connection
60 164
  *
61
- * The URI string buffer must be at least @c PXE_URI_LEN bytes long.
165
+ * @v ipaddress		IP address
166
+ * @v port		TFTP server port
167
+ * @v filename		File name
168
+ * @v blksize		Requested block size
169
+ * @ret rc		Return status code
62 170
  */
63
-static void pxe_tftp_build_uri ( char *uri_string,
64
-				 uint32_t ipaddress, unsigned int port,
65
-				 const unsigned char *filename,
66
-				 int blksize ) {
171
+static int pxe_tftp_open ( uint32_t ipaddress, unsigned int port,
172
+			   const unsigned char *filename, size_t blksize ) {
173
+	char uri_string[PXE_TFTP_URI_LEN];
67 174
 	struct in_addr address;
175
+	int rc;
176
+
177
+	/* Intel bug-for-bug hack */
178
+	pxe_set_cached_filename ( filename );
179
+
180
+	/* Reset PXE TFTP connection structure */
181
+	memset ( &pxe_tftp, 0, sizeof ( pxe_tftp ) );
182
+	xfer_init ( &pxe_tftp.xfer, &pxe_tftp_xfer_ops, NULL );
183
+	pxe_tftp.rc = -EINPROGRESS;
68 184
 
185
+	/* Construct URI string */
69 186
 	address.s_addr = ipaddress;
70 187
 	if ( ! port )
71 188
 		port = htons ( TFTP_PORT );
72
-	if ( ! blksize )
73
-		blksize = TFTP_MAX_BLKSIZE;
74
-	tftp_set_request_blksize ( blksize );
75
-
76
-	snprintf ( uri_string, PXE_URI_LEN, "tftp://%s:%d%s%s",
189
+	if ( blksize < TFTP_DEFAULT_BLKSIZE )
190
+		blksize = TFTP_DEFAULT_BLKSIZE;
191
+	snprintf ( uri_string, sizeof ( uri_string ),
192
+		   "tftp://%s:%d%s%s?blksize=%d",
77 193
 		   inet_ntoa ( address ), ntohs ( port ),
78
-		   ( ( filename[0] == '/' ) ? "" : "/" ), filename );
79
-}
194
+		   ( ( filename[0] == '/' ) ? "" : "/" ), filename, blksize );
195
+	DBG ( " %s", uri_string );
80 196
 
81
-/**
82
- * Read as much as possible from file
83
- *
84
- * @v fd				File descriptor
85
- * @v buffer				Data buffer
86
- * @v max_len				Maximum length to read
87
- * @ret len				Actual length read, or negative error
88
- */
89
-static ssize_t pxe_tftp_read_all ( int fd, userptr_t buffer,
90
-				   size_t max_len ) {
91
-	fd_set fdset;
92
-	off_t offset = 0;
93
-	int ready;
94
-	ssize_t len;
95
-
96
-	do {
97
-		FD_ZERO ( &fdset );
98
-		FD_SET ( fd, &fdset );
99
-		ready = select ( &fdset, 1 );
100
-		if ( ready < 0 )
101
-			return ready;
102
-		len = read_user ( fd, buffer, offset, max_len );
103
-		if ( len < 0 )
104
-			return len;
105
-		offset += len;
106
-	        max_len -= len;
107
-	} while ( max_len && len );
108
-
109
-	return offset;
197
+	/* Open PXE TFTP connection */
198
+	if ( ( rc = xfer_open_uri_string ( &pxe_tftp.xfer,
199
+					   uri_string ) ) != 0 ) {
200
+		DBG ( " could not open (%s)\n", strerror ( rc ) );
201
+		return rc;
202
+	}
203
+
204
+	return 0;
110 205
 }
111 206
 
112 207
 /**
@@ -131,11 +226,6 @@ static ssize_t pxe_tftp_read_all ( int fd, userptr_t buffer,
131 226
  * routing will take place.  See the relevant
132 227
  * @ref pxe_routing "implementation note" for more details.
133 228
  *
134
- * Because we support arbitrary protocols, most of which have no
135
- * notion of "block size" and will return data in arbitrary-sized
136
- * chunks, we cheat and pretend to the caller that the blocksize is
137
- * always accepted as-is.
138
- *
139 229
  * On x86, you must set the s_PXE::StatusCallout field to a nonzero
140 230
  * value before calling this function in protected mode.  You cannot
141 231
  * call this function with a 32-bit stack segment.  (See the relevant
@@ -157,33 +247,35 @@ static ssize_t pxe_tftp_read_all ( int fd, userptr_t buffer,
157 247
  * other PXE API call "if an MTFTP connection is active".
158 248
  */
159 249
 PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) {
160
-	char uri_string[PXE_URI_LEN];
250
+	int rc;
161 251
 
162 252
 	DBG ( "PXENV_TFTP_OPEN" );
163 253
 
164 254
 	/* Guard against callers that fail to close before re-opening */
165
-	close ( pxe_single_fd );
166
-	pxe_single_fd = -1;
167
-
168
-	/* Construct URI */
169
-	pxe_tftp_build_uri ( uri_string, tftp_open->ServerIPAddress,
170
-			     tftp_open->TFTPPort, tftp_open->FileName,
171
-			     tftp_open->PacketSize );
172
-	DBG ( " %s", uri_string );
173
-
174
-	/* Open URI */
175
-	pxe_single_fd = open ( uri_string );
176
-	if ( pxe_single_fd < 0 ) {
177
-		tftp_open->Status = PXENV_STATUS ( pxe_single_fd );
255
+	pxe_tftp_close ( 0 );
256
+
257
+	/* Open connection */
258
+	if ( ( rc = pxe_tftp_open ( tftp_open->ServerIPAddress,
259
+				    tftp_open->TFTPPort,
260
+				    tftp_open->FileName,
261
+				    tftp_open->PacketSize ) ) != 0 ) {
262
+		tftp_open->Status = PXENV_STATUS ( rc );
178 263
 		return PXENV_EXIT_FAILURE;
179 264
 	}
180 265
 
181
-	/* Record parameters for later use */
182
-	pxe_single_blksize = tftp_open->PacketSize;
183
-	pxe_single_blkidx = 0;
266
+	/* Wait for OACK to arrive so that we have the block size */
267
+	while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
268
+		( pxe_tftp.blksize == 0 ) ) {
269
+		step();
270
+	}
271
+	tftp_open->PacketSize = pxe_tftp.blksize;
272
+
273
+	/* EINPROGRESS is normal; we don't wait for the whole transfer */
274
+	if ( rc == -EINPROGRESS )
275
+		rc = 0;
184 276
 
185
-	tftp_open->Status = PXENV_STATUS_SUCCESS;
186
-	return PXENV_EXIT_SUCCESS;
277
+	tftp_open->Status = PXENV_STATUS ( rc );
278
+	return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
187 279
 }
188 280
 
189 281
 /**
@@ -206,8 +298,7 @@ PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) {
206 298
 PXENV_EXIT_t pxenv_tftp_close ( struct s_PXENV_TFTP_CLOSE *tftp_close ) {
207 299
 	DBG ( "PXENV_TFTP_CLOSE" );
208 300
 
209
-	close ( pxe_single_fd );
210
-	pxe_single_fd = -1;
301
+	pxe_tftp_close ( 0 );
211 302
 	tftp_close->Status = PXENV_STATUS_SUCCESS;
212 303
 	return PXENV_EXIT_SUCCESS;
213 304
 }
@@ -274,25 +365,29 @@ PXENV_EXIT_t pxenv_tftp_close ( struct s_PXENV_TFTP_CLOSE *tftp_close ) {
274 365
  * @ref pxe_x86_pmode16 "implementation note" for more details.)
275 366
  */
276 367
 PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read ) {
277
-	userptr_t buffer;
278
-	ssize_t len;
368
+	int rc;
279 369
 
280 370
 	DBG ( "PXENV_TFTP_READ to %04x:%04x",
281 371
 	      tftp_read->Buffer.segment, tftp_read->Buffer.offset );
282 372
 
283
-	buffer = real_to_user ( tftp_read->Buffer.segment,
284
-				tftp_read->Buffer.offset );
285
-	len = pxe_tftp_read_all ( pxe_single_fd, buffer, pxe_single_blksize );
286
-	if ( len < 0 ) {
287
-		tftp_read->Status = PXENV_STATUS ( len );
288
-		return PXENV_EXIT_FAILURE;
289
-	}
290
-
291
-	tftp_read->BufferSize = len;
292
-	tftp_read->PacketNumber = ++pxe_single_blkidx;
293
-
294
-	tftp_read->Status = PXENV_STATUS_SUCCESS;
295
-	return PXENV_EXIT_SUCCESS;
373
+	/* Read single block into buffer */
374
+	pxe_tftp.buffer = real_to_user ( tftp_read->Buffer.segment,
375
+					 tftp_read->Buffer.offset );
376
+	pxe_tftp.size = pxe_tftp.blksize;
377
+	pxe_tftp.start = pxe_tftp.offset;
378
+	while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
379
+		( pxe_tftp.offset == pxe_tftp.start ) )
380
+		step();
381
+	pxe_tftp.buffer = UNULL;
382
+	tftp_read->BufferSize = ( pxe_tftp.offset - pxe_tftp.start );
383
+	tftp_read->PacketNumber = ++pxe_tftp.blkidx;
384
+
385
+	/* EINPROGRESS is normal if we haven't reached EOF yet */
386
+	if ( rc == -EINPROGRESS )
387
+		rc = 0;
388
+
389
+	tftp_read->Status = PXENV_STATUS ( rc );
390
+	return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
296 391
 }
297 392
 
298 393
 /**
@@ -388,40 +483,31 @@ PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read ) {
388 483
  */
389 484
 PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
390 485
 				    *tftp_read_file ) {
391
-	char uri_string[PXE_URI_LEN];
392
-	int fd;
393
-	userptr_t buffer;
394
-	ssize_t len;
395
-	int rc = 0;
396
-
397
-	DBG ( "PXENV_TFTP_READ_FILE" );
398
-
399
-	/* Construct URI */
400
-	pxe_tftp_build_uri ( uri_string, tftp_read_file->ServerIPAddress,
401
-			     tftp_read_file->TFTPSrvPort,
402
-			     tftp_read_file->FileName, 0 );
403
-	DBG ( " %s", uri_string );
486
+	int rc;
404 487
 
405
-	DBG ( " to %08lx+%lx", tftp_read_file->Buffer,
488
+	DBG ( "PXENV_TFTP_READ_FILE to %08lx+%lx", tftp_read_file->Buffer,
406 489
 	      tftp_read_file->BufferSize );
407 490
 
408
-	/* Open URI */
409
-	fd = open ( uri_string );
410
-	if ( fd < 0 ) {
411
-		tftp_read_file->Status = PXENV_STATUS ( fd );
491
+	/* Open TFTP file */
492
+	if ( ( rc = pxe_tftp_open ( tftp_read_file->ServerIPAddress, 0,
493
+				    tftp_read_file->FileName, 0 ) ) != 0 ) {
494
+		tftp_read_file->Status = PXENV_STATUS ( rc );
412 495
 		return PXENV_EXIT_FAILURE;
413 496
 	}
414 497
 
415
-	/* Read file */
416
-	buffer = phys_to_user ( tftp_read_file->Buffer );
417
-	len = pxe_tftp_read_all ( fd, buffer, tftp_read_file->BufferSize );
418
-	if ( len < 0 )
419
-		rc = len;
498
+	/* Read entire file */
499
+	pxe_tftp.buffer = phys_to_user ( tftp_read_file->Buffer );
500
+	pxe_tftp.size = tftp_read_file->BufferSize;
501
+	while ( ( rc = pxe_tftp.rc ) == -EINPROGRESS )
502
+		step();
503
+	pxe_tftp.buffer = UNULL;
504
+	tftp_read_file->BufferSize = pxe_tftp.max_offset;
505
+
506
+	/* Close TFTP file */
507
+	pxe_tftp_close ( rc );
420 508
 
421
-	close ( fd );
422
-	tftp_read_file->BufferSize = len;
423 509
 	tftp_read_file->Status = PXENV_STATUS ( rc );
424
-	return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );	
510
+	return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
425 511
 }
426 512
 
427 513
 /**
@@ -468,33 +554,31 @@ PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE
468 554
  */
469 555
 PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE
470 556
 				    *tftp_get_fsize ) {
471
-	char uri_string[PXE_URI_LEN];
472
-	int fd;
473
-	ssize_t size;
557
+	int rc;
474 558
 
475 559
 	DBG ( "PXENV_TFTP_GET_FSIZE" );
476 560
 
477
-	/* Construct URI */
478
-	pxe_tftp_build_uri ( uri_string, tftp_get_fsize->ServerIPAddress,
479
-			     0, tftp_get_fsize->FileName, 0 );
480
-	DBG ( " %s", uri_string );
481
-
482
-	/* Open URI */
483
-	fd = open ( uri_string );
484
-	if ( fd < 0 ) {
485
-		tftp_get_fsize->Status = PXENV_STATUS ( fd );
561
+	/* Open TFTP file */
562
+	if ( ( rc = pxe_tftp_open ( tftp_get_fsize->ServerIPAddress, 0,
563
+				    tftp_get_fsize->FileName, 0 ) ) != 0 ) {
564
+		tftp_get_fsize->Status = PXENV_STATUS ( rc );
486 565
 		return PXENV_EXIT_FAILURE;
487 566
 	}
488 567
 
489
-	/* Determine size */
490
-	size = fsize ( fd );
491
-	close ( fd );
492
-	if ( size < 0 ) {
493
-		tftp_get_fsize->Status = PXENV_STATUS ( size );
494
-		return PXENV_EXIT_FAILURE;
568
+	/* Wait for initial seek to arrive, and record size */
569
+	while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) &&
570
+		( pxe_tftp.max_offset == 0 ) ) {
571
+		step();
495 572
 	}
573
+	tftp_get_fsize->FileSize = pxe_tftp.max_offset;
496 574
 
497
-	tftp_get_fsize->FileSize = size;
498
-	tftp_get_fsize->Status = PXENV_STATUS_SUCCESS;
499
-	return PXENV_EXIT_SUCCESS;
575
+	/* EINPROGRESS is normal; we don't wait for the whole transfer */
576
+	if ( rc == -EINPROGRESS )
577
+		rc = 0;
578
+
579
+	/* Close TFTP file */
580
+	pxe_tftp_close ( rc );
581
+
582
+	tftp_get_fsize->Status = PXENV_STATUS ( rc );
583
+	return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS );
500 584
 }

Loading…
Cancel
Save