1 /*
2  * Copyright (C) 2009 Fen Systems Ltd <mbrown@fensystems.co.uk>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  *   Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  *   Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in
14  *   the documentation and/or other materials provided with the
15  *   distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28  * OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 FILE_LICENCE ( BSD2 );
32 
33 #include <stdlib.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <gpxe/scsi.h>
37 #include <gpxe/xfer.h>
38 #include <gpxe/features.h>
39 #include <gpxe/ib_srp.h>
40 #include <gpxe/srp.h>
41 
42 /**
43  * @file
44  *
45  * SCSI RDMA Protocol
46  *
47  */
48 
49 FEATURE ( FEATURE_PROTOCOL, "SRP", DHCP_EB_FEATURE_SRP, 1 );
50 
51 /** Tag to be used for next SRP IU */
52 static unsigned int srp_tag = 0;
53 
54 static void srp_login ( struct srp_device *srp );
55 static void srp_cmd ( struct srp_device *srp );
56 
57 /**
58  * Mark SRP SCSI command as complete
59  *
60  * @v srp		SRP device
61  * @v rc		Status code
62  */
srp_scsi_done(struct srp_device * srp,int rc)63 static void srp_scsi_done ( struct srp_device *srp, int rc ) {
64 	if ( srp->command )
65 		srp->command->rc = rc;
66 	srp->command = NULL;
67 }
68 
69 /**
70  * Handle SRP session failure
71  *
72  * @v srp		SRP device
73  * @v rc 		Reason for failure
74  */
srp_fail(struct srp_device * srp,int rc)75 static void srp_fail ( struct srp_device *srp, int rc ) {
76 
77 	/* Close underlying socket */
78 	xfer_close ( &srp->socket, rc );
79 
80 	/* Clear session state */
81 	srp->state = 0;
82 
83 	/* If we have reached the retry limit, report the failure */
84 	if ( srp->retry_count >= SRP_MAX_RETRIES ) {
85 		srp_scsi_done ( srp, rc );
86 		return;
87 	}
88 
89 	/* Otherwise, increment the retry count and try to reopen the
90 	 * connection
91 	 */
92 	srp->retry_count++;
93 	srp_login ( srp );
94 }
95 
96 /**
97  * Initiate SRP login
98  *
99  * @v srp		SRP device
100  */
srp_login(struct srp_device * srp)101 static void srp_login ( struct srp_device *srp ) {
102 	struct io_buffer *iobuf;
103 	struct srp_login_req *login_req;
104 	int rc;
105 
106 	assert ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) );
107 
108 	/* Open underlying socket */
109 	if ( ( rc = srp->transport->connect ( srp ) ) != 0 ) {
110 		DBGC ( srp, "SRP %p could not open socket: %s\n",
111 		       srp, strerror ( rc ) );
112 		goto err;
113 	}
114 	srp->state |= SRP_STATE_SOCKET_OPEN;
115 
116 	/* Allocate I/O buffer */
117 	iobuf = xfer_alloc_iob ( &srp->socket, sizeof ( *login_req ) );
118 	if ( ! iobuf ) {
119 		rc = -ENOMEM;
120 		goto err;
121 	}
122 
123 	/* Construct login request IU */
124 	login_req = iob_put ( iobuf, sizeof ( *login_req ) );
125 	memset ( login_req, 0, sizeof ( *login_req ) );
126 	login_req->type = SRP_LOGIN_REQ;
127 	login_req->tag.dwords[1] = htonl ( ++srp_tag );
128 	login_req->max_i_t_iu_len = htonl ( SRP_MAX_I_T_IU_LEN );
129 	login_req->required_buffer_formats = SRP_LOGIN_REQ_FMT_DDBD;
130 	memcpy ( &login_req->port_ids, &srp->port_ids,
131 		 sizeof ( login_req->port_ids ) );
132 
133 	DBGC2 ( srp, "SRP %p TX login request tag %08x%08x\n",
134 		srp, ntohl ( login_req->tag.dwords[0] ),
135 		ntohl ( login_req->tag.dwords[1] ) );
136 	DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) );
137 
138 	/* Send login request IU */
139 	if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) {
140 		DBGC ( srp, "SRP %p could not send login request: %s\n",
141 		       srp, strerror ( rc ) );
142 		goto err;
143 	}
144 
145 	return;
146 
147  err:
148 	srp_fail ( srp, rc );
149 }
150 
151 /**
152  * Handle SRP login response
153  *
154  * @v srp		SRP device
155  * @v iobuf		I/O buffer
156  * @ret rc		Return status code
157  */
srp_login_rsp(struct srp_device * srp,struct io_buffer * iobuf)158 static int srp_login_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) {
159 	struct srp_login_rsp *login_rsp = iobuf->data;
160 	int rc;
161 
162 	DBGC2 ( srp, "SRP %p RX login response tag %08x%08x\n",
163 		srp, ntohl ( login_rsp->tag.dwords[0] ),
164 		ntohl ( login_rsp->tag.dwords[1] ) );
165 
166 	/* Sanity check */
167 	if ( iob_len ( iobuf ) < sizeof ( *login_rsp ) ) {
168 		DBGC ( srp, "SRP %p RX login response too short (%zd bytes)\n",
169 		       srp, iob_len ( iobuf ) );
170 		rc = -EINVAL;
171 		goto out;
172 	}
173 
174 	DBGC ( srp, "SRP %p logged in\n", srp );
175 
176 	/* Mark as logged in */
177 	srp->state |= SRP_STATE_LOGGED_IN;
178 
179 	/* Reset error counter */
180 	srp->retry_count = 0;
181 
182 	/* Issue pending command */
183 	srp_cmd ( srp );
184 
185 	rc = 0;
186  out:
187 	free_iob ( iobuf );
188 	return rc;
189 }
190 
191 /**
192  * Handle SRP login rejection
193  *
194  * @v srp		SRP device
195  * @v iobuf		I/O buffer
196  * @ret rc		Return status code
197  */
srp_login_rej(struct srp_device * srp,struct io_buffer * iobuf)198 static int srp_login_rej ( struct srp_device *srp, struct io_buffer *iobuf ) {
199 	struct srp_login_rej *login_rej = iobuf->data;
200 	int rc;
201 
202 	DBGC2 ( srp, "SRP %p RX login rejection tag %08x%08x\n",
203 		srp, ntohl ( login_rej->tag.dwords[0] ),
204 		ntohl ( login_rej->tag.dwords[1] ) );
205 
206 	/* Sanity check */
207 	if ( iob_len ( iobuf ) < sizeof ( *login_rej ) ) {
208 		DBGC ( srp, "SRP %p RX login rejection too short (%zd "
209 		       "bytes)\n", srp, iob_len ( iobuf ) );
210 		rc = -EINVAL;
211 		goto out;
212 	}
213 
214 	/* Login rejection always indicates an error */
215 	DBGC ( srp, "SRP %p login rejected (reason %08x)\n",
216 	       srp, ntohl ( login_rej->reason ) );
217 	rc = -EPERM;
218 
219  out:
220 	free_iob ( iobuf );
221 	return rc;
222 }
223 
224 /**
225  * Transmit SRP SCSI command
226  *
227  * @v srp		SRP device
228  */
srp_cmd(struct srp_device * srp)229 static void srp_cmd ( struct srp_device *srp ) {
230 	struct io_buffer *iobuf;
231 	struct srp_cmd *cmd;
232 	struct srp_memory_descriptor *data_out;
233 	struct srp_memory_descriptor *data_in;
234 	int rc;
235 
236 	assert ( srp->state & SRP_STATE_LOGGED_IN );
237 
238 	/* Allocate I/O buffer */
239 	iobuf = xfer_alloc_iob ( &srp->socket, SRP_MAX_I_T_IU_LEN );
240 	if ( ! iobuf ) {
241 		rc = -ENOMEM;
242 		goto err;
243 	}
244 
245 	/* Construct base portion */
246 	cmd = iob_put ( iobuf, sizeof ( *cmd ) );
247 	memset ( cmd, 0, sizeof ( *cmd ) );
248 	cmd->type = SRP_CMD;
249 	cmd->tag.dwords[1] = htonl ( ++srp_tag );
250 	cmd->lun = srp->lun;
251 	memcpy ( &cmd->cdb, &srp->command->cdb, sizeof ( cmd->cdb ) );
252 
253 	/* Construct data-out descriptor, if present */
254 	if ( srp->command->data_out ) {
255 		cmd->data_buffer_formats |= SRP_CMD_DO_FMT_DIRECT;
256 		data_out = iob_put ( iobuf, sizeof ( *data_out ) );
257 		data_out->address =
258 		    cpu_to_be64 ( user_to_phys ( srp->command->data_out, 0 ) );
259 		data_out->handle = ntohl ( srp->memory_handle );
260 		data_out->len = ntohl ( srp->command->data_out_len );
261 	}
262 
263 	/* Construct data-in descriptor, if present */
264 	if ( srp->command->data_in ) {
265 		cmd->data_buffer_formats |= SRP_CMD_DI_FMT_DIRECT;
266 		data_in = iob_put ( iobuf, sizeof ( *data_in ) );
267 		data_in->address =
268 		     cpu_to_be64 ( user_to_phys ( srp->command->data_in, 0 ) );
269 		data_in->handle = ntohl ( srp->memory_handle );
270 		data_in->len = ntohl ( srp->command->data_in_len );
271 	}
272 
273 	DBGC2 ( srp, "SRP %p TX SCSI command tag %08x%08x\n", srp,
274 		ntohl ( cmd->tag.dwords[0] ), ntohl ( cmd->tag.dwords[1] ) );
275 	DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) );
276 
277 	/* Send IU */
278 	if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) {
279 		DBGC ( srp, "SRP %p could not send command: %s\n",
280 		       srp, strerror ( rc ) );
281 		goto err;
282 	}
283 
284 	return;
285 
286  err:
287 	srp_fail ( srp, rc );
288 }
289 
290 /**
291  * Handle SRP SCSI response
292  *
293  * @v srp		SRP device
294  * @v iobuf		I/O buffer
295  * @ret rc		Returns status code
296  */
srp_rsp(struct srp_device * srp,struct io_buffer * iobuf)297 static int srp_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) {
298 	struct srp_rsp *rsp = iobuf->data;
299 	int rc;
300 
301 	DBGC2 ( srp, "SRP %p RX SCSI response tag %08x%08x\n", srp,
302 		ntohl ( rsp->tag.dwords[0] ), ntohl ( rsp->tag.dwords[1] ) );
303 
304 	/* Sanity check */
305 	if ( iob_len ( iobuf ) < sizeof ( *rsp ) ) {
306 		DBGC ( srp, "SRP %p RX SCSI response too short (%zd bytes)\n",
307 		       srp, iob_len ( iobuf ) );
308 		rc = -EINVAL;
309 		goto out;
310 	}
311 
312 	/* Report SCSI errors */
313 	if ( rsp->status != 0 ) {
314 		DBGC ( srp, "SRP %p response status %02x\n",
315 		       srp, rsp->status );
316 		if ( srp_rsp_sense_data ( rsp ) ) {
317 			DBGC ( srp, "SRP %p sense data:\n", srp );
318 			DBGC_HDA ( srp, 0, srp_rsp_sense_data ( rsp ),
319 				   srp_rsp_sense_data_len ( rsp ) );
320 		}
321 	}
322 	if ( rsp->valid & ( SRP_RSP_VALID_DOUNDER | SRP_RSP_VALID_DOOVER ) ) {
323 		DBGC ( srp, "SRP %p response data-out %srun by %#x bytes\n",
324 		       srp, ( ( rsp->valid & SRP_RSP_VALID_DOUNDER )
325 			      ? "under" : "over" ),
326 		       ntohl ( rsp->data_out_residual_count ) );
327 	}
328 	if ( rsp->valid & ( SRP_RSP_VALID_DIUNDER | SRP_RSP_VALID_DIOVER ) ) {
329 		DBGC ( srp, "SRP %p response data-in %srun by %#x bytes\n",
330 		       srp, ( ( rsp->valid & SRP_RSP_VALID_DIUNDER )
331 			      ? "under" : "over" ),
332 		       ntohl ( rsp->data_in_residual_count ) );
333 	}
334 	srp->command->status = rsp->status;
335 
336 	/* Mark SCSI command as complete */
337 	srp_scsi_done ( srp, 0 );
338 
339 	rc = 0;
340  out:
341 	free_iob ( iobuf );
342 	return rc;
343 }
344 
345 /**
346  * Handle SRP unrecognised response
347  *
348  * @v srp		SRP device
349  * @v iobuf		I/O buffer
350  * @ret rc		Returns status code
351  */
srp_unrecognised(struct srp_device * srp,struct io_buffer * iobuf)352 static int srp_unrecognised ( struct srp_device *srp,
353 			      struct io_buffer *iobuf ) {
354 	struct srp_common *common = iobuf->data;
355 
356 	DBGC ( srp, "SRP %p RX unrecognised IU tag %08x%08x type %02x\n",
357 	       srp, ntohl ( common->tag.dwords[0] ),
358 	       ntohl ( common->tag.dwords[1] ), common->type );
359 
360 	free_iob ( iobuf );
361 	return -ENOTSUP;
362 }
363 
364 /**
365  * Receive data from underlying socket
366  *
367  * @v xfer		Data transfer interface
368  * @v iobuf		Datagram I/O buffer
369  * @v meta		Data transfer metadata
370  * @ret rc		Return status code
371  */
srp_xfer_deliver_iob(struct xfer_interface * xfer,struct io_buffer * iobuf,struct xfer_metadata * meta __unused)372 static int srp_xfer_deliver_iob ( struct xfer_interface *xfer,
373 				  struct io_buffer *iobuf,
374 				  struct xfer_metadata *meta __unused ) {
375 	struct srp_device *srp =
376 		container_of ( xfer, struct srp_device, socket );
377 	struct srp_common *common = iobuf->data;
378 	int ( * type ) ( struct srp_device *srp, struct io_buffer *iobuf );
379 	int rc;
380 
381 	/* Determine IU type */
382 	switch ( common->type ) {
383 	case SRP_LOGIN_RSP:
384 		type = srp_login_rsp;
385 		break;
386 	case SRP_LOGIN_REJ:
387 		type = srp_login_rej;
388 		break;
389 	case SRP_RSP:
390 		type = srp_rsp;
391 		break;
392 	default:
393 		type = srp_unrecognised;
394 		break;
395 	}
396 
397 	/* Handle IU */
398 	if ( ( rc = type ( srp, iobuf ) ) != 0 )
399 		goto err;
400 
401 	return 0;
402 
403  err:
404 	srp_fail ( srp, rc );
405 	return rc;
406 }
407 
408 /**
409  * Underlying socket closed
410  *
411  * @v xfer		Data transfer interface
412  * @v rc		Reason for close
413  */
srp_xfer_close(struct xfer_interface * xfer,int rc)414 static void srp_xfer_close ( struct xfer_interface *xfer, int rc ) {
415 	struct srp_device *srp =
416 		container_of ( xfer, struct srp_device, socket );
417 
418 	DBGC ( srp, "SRP %p socket closed: %s\n", srp, strerror ( rc ) );
419 
420 	srp_fail ( srp, rc );
421 }
422 
423 /** SRP data transfer interface operations */
424 static struct xfer_interface_operations srp_xfer_operations = {
425 	.close		= srp_xfer_close,
426 	.vredirect	= ignore_xfer_vredirect,
427 	.window		= unlimited_xfer_window,
428 	.alloc_iob	= default_xfer_alloc_iob,
429 	.deliver_iob	= srp_xfer_deliver_iob,
430 	.deliver_raw	= xfer_deliver_as_iob,
431 };
432 
433 /**
434  * Issue SCSI command via SRP
435  *
436  * @v scsi		SCSI device
437  * @v command		SCSI command
438  * @ret rc		Return status code
439  */
srp_command(struct scsi_device * scsi,struct scsi_command * command)440 static int srp_command ( struct scsi_device *scsi,
441 			 struct scsi_command *command ) {
442 	struct srp_device *srp =
443 		container_of ( scsi->backend, struct srp_device, refcnt );
444 
445 	/* Store SCSI command */
446 	if ( srp->command ) {
447 		DBGC ( srp, "SRP %p cannot handle concurrent SCSI commands\n",
448 		       srp );
449 		return -EBUSY;
450 	}
451 	srp->command = command;
452 
453 	/* Log in or issue command as appropriate */
454 	if ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) ) {
455 		srp_login ( srp );
456 	} else if ( srp->state & SRP_STATE_LOGGED_IN ) {
457 		srp_cmd ( srp );
458 	} else {
459 		/* Still waiting for login; do nothing */
460 	}
461 
462 	return 0;
463 }
464 
465 /**
466  * Attach SRP device
467  *
468  * @v scsi		SCSI device
469  * @v root_path		Root path
470  */
srp_attach(struct scsi_device * scsi,const char * root_path)471 int srp_attach ( struct scsi_device *scsi, const char *root_path ) {
472 	struct srp_transport_type *transport;
473 	struct srp_device *srp;
474 	int rc;
475 
476 	/* Hard-code an IB SRP back-end for now */
477 	transport = &ib_srp_transport;
478 
479 	/* Allocate and initialise structure */
480 	srp = zalloc ( sizeof ( *srp ) + transport->priv_len );
481 	if ( ! srp ) {
482 		rc = -ENOMEM;
483 		goto err_alloc;
484 	}
485 	xfer_init ( &srp->socket, &srp_xfer_operations, &srp->refcnt );
486 	srp->transport = transport;
487 	DBGC ( srp, "SRP %p using %s\n", srp, root_path );
488 
489 	/* Parse root path */
490 	if ( ( rc = transport->parse_root_path ( srp, root_path ) ) != 0 ) {
491 		DBGC ( srp, "SRP %p could not parse root path: %s\n",
492 		       srp, strerror ( rc ) );
493 		goto err_parse_root_path;
494 	}
495 
496 	/* Attach parent interface, mortalise self, and return */
497 	scsi->backend = ref_get ( &srp->refcnt );
498 	scsi->command = srp_command;
499 	ref_put ( &srp->refcnt );
500 	return 0;
501 
502  err_parse_root_path:
503 	ref_put ( &srp->refcnt );
504  err_alloc:
505 	return rc;
506 }
507 
508 /**
509  * Detach SRP device
510  *
511  * @v scsi		SCSI device
512  */
srp_detach(struct scsi_device * scsi)513 void srp_detach ( struct scsi_device *scsi ) {
514 	struct srp_device *srp =
515 		container_of ( scsi->backend, struct srp_device, refcnt );
516 
517 	/* Close socket */
518 	xfer_nullify ( &srp->socket );
519 	xfer_close ( &srp->socket, 0 );
520 	scsi->command = scsi_detached_command;
521 	ref_put ( scsi->backend );
522 	scsi->backend = NULL;
523 }
524