summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2009-07-17 23:11:42 +0200
committerMichael Brown2009-08-10 23:27:33 +0200
commit0c30dc6bc5f26e7011ddfcda34d7adac653464cf (patch)
tree77510bd3e059a3840f8704eae8f2ac05482f6fc4
parent[infiniband] Add last_opened_ibdev(), analogous to last_opened_netdev() (diff)
downloadipxe-0c30dc6bc5f26e7011ddfcda34d7adac653464cf.tar.gz
ipxe-0c30dc6bc5f26e7011ddfcda34d7adac653464cf.tar.xz
ipxe-0c30dc6bc5f26e7011ddfcda34d7adac653464cf.zip
[infiniband] Add support for SRP over Infiniband
SRP is the SCSI RDMA Protocol. It allows for a method of SAN booting whereby the target is responsible for reading and writing data using Remote DMA directly to the initiator's memory. The software initiator merely sends and receives SCSI commands; it never has to touch the actual data.
-rw-r--r--src/arch/i386/interface/pcbios/ib_srpboot.c63
-rw-r--r--src/config/general.h1
-rw-r--r--src/core/config.c3
-rw-r--r--src/drivers/block/srp.c530
-rw-r--r--src/include/gpxe/errfile.h3
-rw-r--r--src/include/gpxe/features.h1
-rw-r--r--src/include/gpxe/ib_srp.h79
-rw-r--r--src/include/gpxe/srp.h874
-rw-r--r--src/net/infiniband/ib_srp.c406
9 files changed, 1960 insertions, 0 deletions
diff --git a/src/arch/i386/interface/pcbios/ib_srpboot.c b/src/arch/i386/interface/pcbios/ib_srpboot.c
new file mode 100644
index 00000000..ab90c44f
--- /dev/null
+++ b/src/arch/i386/interface/pcbios/ib_srpboot.c
@@ -0,0 +1,63 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <gpxe/sanboot.h>
+#include <int13.h>
+#include <gpxe/srp.h>
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+static int ib_srpboot ( const char *root_path ) {
+ struct scsi_device *scsi;
+ struct int13_drive *drive;
+ int rc;
+
+ scsi = zalloc ( sizeof ( *scsi ) );
+ if ( ! scsi ) {
+ rc = -ENOMEM;
+ goto err_alloc_scsi;
+ }
+ drive = zalloc ( sizeof ( *drive ) );
+ if ( ! drive ) {
+ rc = -ENOMEM;
+ goto err_alloc_drive;
+ }
+
+ if ( ( rc = srp_attach ( scsi, root_path ) ) != 0 ) {
+ printf ( "Could not attach IB_SRP device: %s\n",
+ strerror ( rc ) );
+ goto err_attach;
+ }
+ if ( ( rc = init_scsidev ( scsi ) ) != 0 ) {
+ printf ( "Could not initialise IB_SRP device: %s\n",
+ strerror ( rc ) );
+ goto err_init;
+ }
+
+ drive->blockdev = &scsi->blockdev;
+
+ register_int13_drive ( drive );
+ printf ( "Registered as BIOS drive %#02x\n", drive->drive );
+ printf ( "Booting from BIOS drive %#02x\n", drive->drive );
+ rc = int13_boot ( drive->drive );
+ printf ( "Boot failed\n" );
+
+ printf ( "Unregistering BIOS drive %#02x\n", drive->drive );
+ unregister_int13_drive ( drive );
+
+ err_init:
+ srp_detach ( scsi );
+ err_attach:
+ free ( drive );
+ err_alloc_drive:
+ free ( scsi );
+ err_alloc_scsi:
+ return rc;
+}
+
+struct sanboot_protocol ib_srp_sanboot_protocol __sanboot_protocol = {
+ .prefix = "ib_srp:",
+ .boot = ib_srpboot,
+};
diff --git a/src/config/general.h b/src/config/general.h
index 7422a38a..ee07dfc0 100644
--- a/src/config/general.h
+++ b/src/config/general.h
@@ -62,6 +62,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
//#undef SANBOOT_PROTO_ISCSI /* iSCSI protocol */
//#undef SANBOOT_PROTO_AOE /* AoE protocol */
+//#undef SANBOOT_PROTO_IB_SRP /* Infiniband SCSI RDMA protocol */
/*
* Name resolution modules
diff --git a/src/core/config.c b/src/core/config.c
index 4562e3a4..e4c05613 100644
--- a/src/core/config.c
+++ b/src/core/config.c
@@ -109,6 +109,9 @@ REQUIRE_OBJECT ( iscsiboot );
#ifdef SANBOOT_PROTO_AOE
REQUIRE_OBJECT ( aoeboot );
#endif
+#ifdef SANBOOT_PROTO_IB_SRP
+REQUIRE_OBJECT ( ib_srpboot );
+#endif
/*
* Drag in all requested resolvers
diff --git a/src/drivers/block/srp.c b/src/drivers/block/srp.c
new file mode 100644
index 00000000..f50f194b
--- /dev/null
+++ b/src/drivers/block/srp.c
@@ -0,0 +1,530 @@
+/*
+ * Copyright (C) 2009 Fen Systems Ltd <mbrown@fensystems.co.uk>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+FILE_LICENCE ( BSD2 );
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gpxe/scsi.h>
+#include <gpxe/xfer.h>
+#include <gpxe/features.h>
+#include <gpxe/ib_srp.h>
+#include <gpxe/srp.h>
+
+/**
+ * @file
+ *
+ * SCSI RDMA Protocol
+ *
+ */
+
+FEATURE ( FEATURE_PROTOCOL, "SRP", DHCP_EB_FEATURE_SRP, 1 );
+
+/** Tag to be used for next SRP IU */
+static unsigned int srp_tag = 0;
+
+static void srp_login ( struct srp_device *srp );
+static void srp_cmd ( struct srp_device *srp );
+
+/**
+ * Mark SRP SCSI command as complete
+ *
+ * @v srp SRP device
+ * @v rc Status code
+ */
+static void srp_scsi_done ( struct srp_device *srp, int rc ) {
+ if ( srp->command )
+ srp->command->rc = rc;
+ srp->command = NULL;
+}
+
+/**
+ * Handle SRP session failure
+ *
+ * @v srp SRP device
+ * @v rc Reason for failure
+ */
+static void srp_fail ( struct srp_device *srp, int rc ) {
+
+ /* Close underlying socket */
+ xfer_close ( &srp->socket, rc );
+
+ /* Clear session state */
+ srp->state = 0;
+
+ /* Increment retry count */
+ srp->retry_count++;
+
+ /* If we have reached the retry limit, permanently abort the
+ * session.
+ */
+ if ( srp->retry_count >= SRP_MAX_RETRIES ) {
+ srp->instant_rc = rc;
+ srp_scsi_done ( srp, rc );
+ return;
+ }
+
+ /* Otherwise, try to reopen the connection */
+ srp_login ( srp );
+}
+
+/**
+ * Initiate SRP login
+ *
+ * @v srp SRP device
+ */
+static void srp_login ( struct srp_device *srp ) {
+ struct io_buffer *iobuf;
+ struct srp_login_req *login_req;
+ int rc;
+
+ assert ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) );
+
+ /* Open underlying socket */
+ if ( ( rc = srp->transport->connect ( srp ) ) != 0 ) {
+ DBGC ( srp, "SRP %p could not open socket: %s\n",
+ srp, strerror ( rc ) );
+ goto err;
+ }
+ srp->state |= SRP_STATE_SOCKET_OPEN;
+
+ /* Allocate I/O buffer */
+ iobuf = xfer_alloc_iob ( &srp->socket, sizeof ( *login_req ) );
+ if ( ! iobuf ) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ /* Construct login request IU */
+ login_req = iob_put ( iobuf, sizeof ( *login_req ) );
+ memset ( login_req, 0, sizeof ( *login_req ) );
+ login_req->type = SRP_LOGIN_REQ;
+ login_req->tag.dwords[1] = htonl ( ++srp_tag );
+ login_req->max_i_t_iu_len = htonl ( SRP_MAX_I_T_IU_LEN );
+ login_req->required_buffer_formats = SRP_LOGIN_REQ_FMT_DDBD;
+ memcpy ( &login_req->port_ids, &srp->port_ids,
+ sizeof ( login_req->port_ids ) );
+
+ DBGC2 ( srp, "SRP %p TX login request tag %08x%08x\n",
+ srp, ntohl ( login_req->tag.dwords[0] ),
+ ntohl ( login_req->tag.dwords[1] ) );
+ DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) );
+
+ /* Send login request IU */
+ if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) {
+ DBGC ( srp, "SRP %p could not send login request: %s\n",
+ srp, strerror ( rc ) );
+ goto err;
+ }
+
+ return;
+
+ err:
+ srp_fail ( srp, rc );
+}
+
+/**
+ * Handle SRP login response
+ *
+ * @v srp SRP device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int srp_login_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) {
+ struct srp_login_rsp *login_rsp = iobuf->data;
+ int rc;
+
+ DBGC2 ( srp, "SRP %p RX login response tag %08x%08x\n",
+ srp, ntohl ( login_rsp->tag.dwords[0] ),
+ ntohl ( login_rsp->tag.dwords[1] ) );
+
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < sizeof ( *login_rsp ) ) {
+ DBGC ( srp, "SRP %p RX login response too short (%zd bytes)\n",
+ srp, iob_len ( iobuf ) );
+ rc = -EINVAL;
+ goto out;
+ }
+
+ DBGC ( srp, "SRP %p logged in\n", srp );
+
+ /* Mark as logged in */
+ srp->state |= SRP_STATE_LOGGED_IN;
+
+ /* Reset error counter */
+ srp->retry_count = 0;
+
+ /* Issue pending command */
+ srp_cmd ( srp );
+
+ rc = 0;
+ out:
+ free_iob ( iobuf );
+ return rc;
+}
+
+/**
+ * Handle SRP login rejection
+ *
+ * @v srp SRP device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int srp_login_rej ( struct srp_device *srp, struct io_buffer *iobuf ) {
+ struct srp_login_rej *login_rej = iobuf->data;
+ int rc;
+
+ DBGC2 ( srp, "SRP %p RX login rejection tag %08x%08x\n",
+ srp, ntohl ( login_rej->tag.dwords[0] ),
+ ntohl ( login_rej->tag.dwords[1] ) );
+
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < sizeof ( *login_rej ) ) {
+ DBGC ( srp, "SRP %p RX login rejection too short (%zd "
+ "bytes)\n", srp, iob_len ( iobuf ) );
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* Login rejection always indicates an error */
+ DBGC ( srp, "SRP %p login rejected (reason %08x)\n",
+ srp, ntohl ( login_rej->reason ) );
+ rc = -EPERM;
+
+ out:
+ free_iob ( iobuf );
+ return rc;
+}
+
+/**
+ * Transmit SRP SCSI command
+ *
+ * @v srp SRP device
+ */
+static void srp_cmd ( struct srp_device *srp ) {
+ struct io_buffer *iobuf;
+ struct srp_cmd *cmd;
+ struct srp_memory_descriptor *data_out;
+ struct srp_memory_descriptor *data_in;
+ int rc;
+
+ assert ( srp->state & SRP_STATE_LOGGED_IN );
+
+ /* Allocate I/O buffer */
+ iobuf = xfer_alloc_iob ( &srp->socket, SRP_MAX_I_T_IU_LEN );
+ if ( ! iobuf ) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ /* Construct base portion */
+ cmd = iob_put ( iobuf, sizeof ( *cmd ) );
+ memset ( cmd, 0, sizeof ( *cmd ) );
+ cmd->type = SRP_CMD;
+ cmd->tag.dwords[1] = htonl ( ++srp_tag );
+ cmd->lun = srp->lun;
+ memcpy ( &cmd->cdb, &srp->command->cdb, sizeof ( cmd->cdb ) );
+
+ /* Construct data-out descriptor, if present */
+ if ( srp->command->data_out ) {
+ cmd->data_buffer_formats |= SRP_CMD_DO_FMT_DIRECT;
+ data_out = iob_put ( iobuf, sizeof ( *data_out ) );
+ data_out->address =
+ cpu_to_be64 ( user_to_phys ( srp->command->data_out, 0 ) );
+ data_out->handle = ntohl ( srp->memory_handle );
+ data_out->len = ntohl ( srp->command->data_out_len );
+ }
+
+ /* Construct data-in descriptor, if present */
+ if ( srp->command->data_in ) {
+ cmd->data_buffer_formats |= SRP_CMD_DI_FMT_DIRECT;
+ data_in = iob_put ( iobuf, sizeof ( *data_in ) );
+ data_in->address =
+ cpu_to_be64 ( user_to_phys ( srp->command->data_in, 0 ) );
+ data_in->handle = ntohl ( srp->memory_handle );
+ data_in->len = ntohl ( srp->command->data_in_len );
+ }
+
+ DBGC2 ( srp, "SRP %p TX SCSI command tag %08x%08x\n", srp,
+ ntohl ( cmd->tag.dwords[0] ), ntohl ( cmd->tag.dwords[1] ) );
+ DBGC2_HDA ( srp, 0, iobuf->data, iob_len ( iobuf ) );
+
+ /* Send IU */
+ if ( ( rc = xfer_deliver_iob ( &srp->socket, iobuf ) ) != 0 ) {
+ DBGC ( srp, "SRP %p could not send command: %s\n",
+ srp, strerror ( rc ) );
+ goto err;
+ }
+
+ return;
+
+ err:
+ srp_fail ( srp, rc );
+}
+
+/**
+ * Handle SRP SCSI response
+ *
+ * @v srp SRP device
+ * @v iobuf I/O buffer
+ * @ret rc Returns status code
+ */
+static int srp_rsp ( struct srp_device *srp, struct io_buffer *iobuf ) {
+ struct srp_rsp *rsp = iobuf->data;
+ int rc;
+
+ DBGC2 ( srp, "SRP %p RX SCSI response tag %08x%08x\n", srp,
+ ntohl ( rsp->tag.dwords[0] ), ntohl ( rsp->tag.dwords[1] ) );
+
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < sizeof ( *rsp ) ) {
+ DBGC ( srp, "SRP %p RX SCSI response too short (%zd bytes)\n",
+ srp, iob_len ( iobuf ) );
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* Report SCSI errors */
+ if ( rsp->status != 0 ) {
+ DBGC ( srp, "SRP %p response status %02x\n",
+ srp, rsp->status );
+ if ( srp_rsp_sense_data ( rsp ) ) {
+ DBGC ( srp, "SRP %p sense data:\n", srp );
+ DBGC_HDA ( srp, 0, srp_rsp_sense_data ( rsp ),
+ srp_rsp_sense_data_len ( rsp ) );
+ }
+ }
+ if ( rsp->valid & ( SRP_RSP_VALID_DOUNDER | SRP_RSP_VALID_DOOVER ) ) {
+ DBGC ( srp, "SRP %p response data-out %srun by %#x bytes\n",
+ srp, ( ( rsp->valid & SRP_RSP_VALID_DOUNDER )
+ ? "under" : "over" ),
+ ntohl ( rsp->data_out_residual_count ) );
+ }
+ if ( rsp->valid & ( SRP_RSP_VALID_DIUNDER | SRP_RSP_VALID_DIOVER ) ) {
+ DBGC ( srp, "SRP %p response data-in %srun by %#x bytes\n",
+ srp, ( ( rsp->valid & SRP_RSP_VALID_DIUNDER )
+ ? "under" : "over" ),
+ ntohl ( rsp->data_in_residual_count ) );
+ }
+ srp->command->status = rsp->status;
+
+ /* Mark SCSI command as complete */
+ srp_scsi_done ( srp, 0 );
+
+ rc = 0;
+ out:
+ free_iob ( iobuf );
+ return rc;
+}
+
+/**
+ * Handle SRP unrecognised response
+ *
+ * @v srp SRP device
+ * @v iobuf I/O buffer
+ * @ret rc Returns status code
+ */
+static int srp_unrecognised ( struct srp_device *srp,
+ struct io_buffer *iobuf ) {
+ struct srp_common *common = iobuf->data;
+
+ DBGC ( srp, "SRP %p RX unrecognised IU tag %08x%08x type %02x\n",
+ srp, ntohl ( common->tag.dwords[0] ),
+ ntohl ( common->tag.dwords[1] ), common->type );
+
+ free_iob ( iobuf );
+ return -ENOTSUP;
+}
+
+/**
+ * Receive data from underlying socket
+ *
+ * @v xfer Data transfer interface
+ * @v iobuf Datagram I/O buffer
+ * @v meta Data transfer metadata
+ * @ret rc Return status code
+ */
+static int srp_xfer_deliver_iob ( struct xfer_interface *xfer,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta __unused ) {
+ struct srp_device *srp =
+ container_of ( xfer, struct srp_device, socket );
+ struct srp_common *common = iobuf->data;
+ int ( * type ) ( struct srp_device *srp, struct io_buffer *iobuf );
+ int rc;
+
+ /* Determine IU type */
+ switch ( common->type ) {
+ case SRP_LOGIN_RSP:
+ type = srp_login_rsp;
+ break;
+ case SRP_LOGIN_REJ:
+ type = srp_login_rej;
+ break;
+ case SRP_RSP:
+ type = srp_rsp;
+ break;
+ default:
+ type = srp_unrecognised;
+ break;
+ }
+
+ /* Handle IU */
+ if ( ( rc = type ( srp, iobuf ) ) != 0 )
+ goto err;
+
+ return 0;
+
+ err:
+ srp_fail ( srp, rc );
+ return rc;
+}
+
+/**
+ * Underlying socket closed
+ *
+ * @v xfer Data transfer interface
+ * @v rc Reason for close
+ */
+static void srp_xfer_close ( struct xfer_interface *xfer, int rc ) {
+ struct srp_device *srp =
+ container_of ( xfer, struct srp_device, socket );
+
+ DBGC ( srp, "SRP %p socket closed: %s\n", srp, strerror ( rc ) );
+
+ srp_fail ( srp, rc );
+}
+
+/** SRP data transfer interface operations */
+static struct xfer_interface_operations srp_xfer_operations = {
+ .close = srp_xfer_close,
+ .vredirect = ignore_xfer_vredirect,
+ .window = unlimited_xfer_window,
+ .alloc_iob = default_xfer_alloc_iob,
+ .deliver_iob = srp_xfer_deliver_iob,
+ .deliver_raw = xfer_deliver_as_iob,
+};
+
+/**
+ * Issue SCSI command via SRP
+ *
+ * @v scsi SCSI device
+ * @v command SCSI command
+ * @ret rc Return status code
+ */
+static int srp_command ( struct scsi_device *scsi,
+ struct scsi_command *command ) {
+ struct srp_device *srp =
+ container_of ( scsi->backend, struct srp_device, refcnt );
+
+ /* Return instant failure, if we have already aborted the session */
+ if ( srp->instant_rc )
+ return srp->instant_rc;
+
+ /* Store SCSI command */
+ if ( srp->command ) {
+ DBGC ( srp, "SRP %p cannot handle concurrent SCSI commands\n",
+ srp );
+ return -EBUSY;
+ }
+ srp->command = command;
+
+ /* Log in or issue command as appropriate */
+ if ( ! ( srp->state & SRP_STATE_SOCKET_OPEN ) ) {
+ srp_login ( srp );
+ } else if ( srp->state & SRP_STATE_LOGGED_IN ) {
+ srp_cmd ( srp );
+ } else {
+ /* Still waiting for login; do nothing */
+ }
+
+ return 0;
+}
+
+/**
+ * Attach SRP device
+ *
+ * @v scsi SCSI device
+ * @v root_path Root path
+ */
+int srp_attach ( struct scsi_device *scsi, const char *root_path ) {
+ struct srp_transport_type *transport;
+ struct srp_device *srp;
+ int rc;
+
+ /* Hard-code an IB SRP back-end for now */
+ transport = &ib_srp_transport;
+
+ /* Allocate and initialise structure */
+ srp = zalloc ( sizeof ( *srp ) + transport->priv_len );
+ if ( ! srp ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ xfer_init ( &srp->socket, &srp_xfer_operations, &srp->refcnt );
+ srp->transport = transport;
+ DBGC ( srp, "SRP %p using %s\n", srp, root_path );
+
+ /* Parse root path */
+ if ( ( rc = transport->parse_root_path ( srp, root_path ) ) != 0 ) {
+ DBGC ( srp, "SRP %p could not parse root path: %s\n",
+ srp, strerror ( rc ) );
+ goto err_parse_root_path;
+ }
+
+ /* Attach parent interface, mortalise self, and return */
+ scsi->backend = ref_get ( &srp->refcnt );
+ scsi->command = srp_command;
+ ref_put ( &srp->refcnt );
+ return 0;
+
+ err_parse_root_path:
+ ref_put ( &srp->refcnt );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Detach SRP device
+ *
+ * @v scsi SCSI device
+ */
+void srp_detach ( struct scsi_device *scsi ) {
+ struct srp_device *srp =
+ container_of ( scsi->backend, struct srp_device, refcnt );
+
+ /* Close socket */
+ xfer_nullify ( &srp->socket );
+ xfer_close ( &srp->socket, 0 );
+ scsi->command = scsi_detached_command;
+ ref_put ( scsi->backend );
+ scsi->backend = NULL;
+}
diff --git a/src/include/gpxe/errfile.h b/src/include/gpxe/errfile.h
index d2305b32..a7892839 100644
--- a/src/include/gpxe/errfile.h
+++ b/src/include/gpxe/errfile.h
@@ -119,6 +119,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ERRFILE_hermon ( ERRFILE_DRIVER | 0x00720000 )
#define ERRFILE_linda ( ERRFILE_DRIVER | 0x00730000 )
#define ERRFILE_ata ( ERRFILE_DRIVER | 0x00740000 )
+#define ERRFILE_srp ( ERRFILE_DRIVER | 0x00750000 )
#define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 )
#define ERRFILE_arp ( ERRFILE_NET | 0x00010000 )
@@ -154,6 +155,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ERRFILE_net80211 ( ERRFILE_NET | 0x001f0000 )
#define ERRFILE_ib_mi ( ERRFILE_NET | 0x00200000 )
#define ERRFILE_ib_cmrc ( ERRFILE_NET | 0x00210000 )
+#define ERRFILE_ib_srp ( ERRFILE_NET | 0x00220000 )
#define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 )
#define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 )
@@ -186,6 +188,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ERRFILE_pxemenu ( ERRFILE_OTHER | 0x00150000 )
#define ERRFILE_x509 ( ERRFILE_OTHER | 0x00160000 )
#define ERRFILE_login_ui ( ERRFILE_OTHER | 0x00170000 )
+#define ERRFILE_ib_srpboot ( ERRFILE_OTHER | 0x00180000 )
/** @} */
diff --git a/src/include/gpxe/features.h b/src/include/gpxe/features.h
index db485bc2..34431867 100644
--- a/src/include/gpxe/features.h
+++ b/src/include/gpxe/features.h
@@ -44,6 +44,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define DHCP_EB_FEATURE_BZIMAGE 0x18 /**< bzImage format */
#define DHCP_EB_FEATURE_MULTIBOOT 0x19 /**< Multiboot format */
#define DHCP_EB_FEATURE_SLAM 0x1a /**< SLAM protocol */
+#define DHCP_EB_FEATURE_SRP 0x1b /**< SRP protocol */
#define DHCP_EB_FEATURE_NBI 0x20 /**< NBI format */
#define DHCP_EB_FEATURE_PXE 0x21 /**< PXE format */
#define DHCP_EB_FEATURE_ELF 0x22 /**< ELF format */
diff --git a/src/include/gpxe/ib_srp.h b/src/include/gpxe/ib_srp.h
new file mode 100644
index 00000000..cf705b30
--- /dev/null
+++ b/src/include/gpxe/ib_srp.h
@@ -0,0 +1,79 @@
+#ifndef _GPXE_IB_SRP_H
+#define _GPXE_IB_SRP_H
+
+/** @file
+ *
+ * SCSI RDMA Protocol over Infiniband
+ *
+ */
+
+FILE_LICENCE ( BSD2 );
+
+#include <stdint.h>
+#include <gpxe/infiniband.h>
+#include <gpxe/srp.h>
+
+/** SRP initiator port identifier for Infiniband */
+struct ib_srp_initiator_port_id {
+ /** Identifier extension */
+ struct ib_gid_half id_ext;
+ /** IB channel adapter GUID */
+ struct ib_gid_half hca_guid;
+} __attribute__ (( packed ));
+
+/** SRP target port identifier for Infiniband */
+struct ib_srp_target_port_id {
+ /** Identifier extension */
+ struct ib_gid_half id_ext;
+ /** I/O controller GUID */
+ struct ib_gid_half ioc_guid;
+} __attribute__ (( packed ));
+
+/**
+ * Get Infiniband-specific initiator port ID
+ *
+ * @v port_ids SRP port IDs
+ * @ret initiator_port_id Infiniband-specific initiator port ID
+ */
+static inline __always_inline struct ib_srp_initiator_port_id *
+ib_srp_initiator_port_id ( struct srp_port_ids *port_ids ) {
+ return ( ( struct ib_srp_initiator_port_id * ) &port_ids->initiator );
+}
+
+/**
+ * Get Infiniband-specific target port ID
+ *
+ * @v port_ids SRP port IDs
+ * @ret target_port_id Infiniband-specific target port ID
+ */
+static inline __always_inline struct ib_srp_target_port_id *
+ib_srp_target_port_id ( struct srp_port_ids *port_ids ) {
+ return ( ( struct ib_srp_target_port_id * ) &port_ids->target );
+}
+
+/** Infiniband-specific SRP parameters */
+struct ib_srp_parameters {
+ /** Source GID */
+ struct ib_gid sgid;
+ /** Destination GID */
+ struct ib_gid dgid;
+ /** Service ID */
+ struct ib_gid_half service_id;
+ /** Partition key */
+ uint16_t pkey;
+};
+
+/**
+ * Get Infiniband-specific transport parameters
+ *
+ * @v srp SRP device
+ * @ret ib_params Infiniband-specific transport parameters
+ */
+static inline __always_inline struct ib_srp_parameters *
+ib_srp_params ( struct srp_device *srp ) {
+ return srp_transport_priv ( srp );
+}
+
+extern struct srp_transport_type ib_srp_transport;
+
+#endif /* _GPXE_IB_SRP_H */
diff --git a/src/include/gpxe/srp.h b/src/include/gpxe/srp.h
new file mode 100644
index 00000000..75ec7b83
--- /dev/null
+++ b/src/include/gpxe/srp.h
@@ -0,0 +1,874 @@
+#ifndef _GPXE_SRP_H
+#define _GPXE_SRP_H
+
+/** @file
+ *
+ * SCSI RDMA Protocol
+ *
+ */
+
+FILE_LICENCE ( BSD2 );
+
+#include <stdint.h>
+#include <byteswap.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/xfer.h>
+#include <gpxe/scsi.h>
+
+/*****************************************************************************
+ *
+ * Common fields
+ *
+ *****************************************************************************
+ */
+
+/** An SRP information unit tag */
+struct srp_tag {
+ uint32_t dwords[2];
+} __attribute__ (( packed ));
+
+/** An SRP port ID */
+struct srp_port_id {
+ uint8_t bytes[16];
+} __attribute__ (( packed ));
+
+/** An SRP port ID pair */
+struct srp_port_ids {
+ /** Initiator port ID */
+ struct srp_port_id initiator;
+ /** Target port ID */
+ struct srp_port_id target;
+} __attribute__ (( packed ));
+
+/** SRP information unit common fields */
+struct srp_common {
+ /** Information unit type */
+ uint8_t type;
+ /** Reserved */
+ uint8_t reserved0[7];
+ /** Tag */
+ struct srp_tag tag;
+} __attribute__ (( packed ));
+
+/*****************************************************************************
+ *
+ * Login request
+ *
+ *****************************************************************************
+ */
+
+/** An SRP login request information unit */
+struct srp_login_req {
+ /** Information unit type
+ *
+ * This must be @c SRP_LOGIN_REQ
+ */
+ uint8_t type;
+ /** Reserved */
+ uint8_t reserved0[7];
+ /** Tag */
+ struct srp_tag tag;
+ /** Requested maximum initiator to target IU length */
+ uint32_t max_i_t_iu_len;
+ /** Reserved */
+ uint8_t reserved1[4];
+ /** Required buffer formats
+ *
+ * This is the bitwise OR of one or more @c
+ * SRP_LOGIN_REQ_FMT_XXX constants.
+ */
+ uint16_t required_buffer_formats;
+ /** Flags
+ *
+ * This is the bitwise OR of zero or more @c
+ * SRP_LOGIN_REQ_FLAG_XXX and @c SRP_LOGIN_REQ_MCA_XXX
+ * constants.
+ */
+ uint8_t flags;
+ /** Reserved */
+ uint8_t reserved2[5];
+ /** Initiator and target port identifiers */
+ struct srp_port_ids port_ids;
+} __attribute__ (( packed ));
+
+/** Type of an SRP login request */
+#define SRP_LOGIN_REQ 0x00
+
+/** Require indirect data buffer descriptor format */
+#define SRP_LOGIN_REQ_FMT_IDBD 0x04
+
+/** Require direct data buffer descriptor format */
+#define SRP_LOGIN_REQ_FMT_DDBD 0x02
+
+/** Use solicited notification for asynchronous events */
+#define SRP_LOGIN_REQ_FLAG_AESOLNT 0x40
+
+/** Use solicited notification for credit request */
+#define SRP_LOGIN_REQ_FLAG_CRSOLNT 0x20
+
+/** Use solicited notification for logouts */
+#define SRP_LOGIN_REQ_FLAG_LOSOLNT 0x10
+
+/** Multi-channel action mask */
+#define SRP_LOGIN_REQ_MCA_MASK 0x03
+
+/** Single RDMA channel operation */
+#define SRP_LOGIN_REQ_MCA_SINGLE_CHANNEL 0x00
+
+/** Multiple independent RDMA channel operation */
+#define SRP_LOGIN_REQ_MCA_MULTIPLE_CHANNELS 0x01
+
+/*****************************************************************************
+ *
+ * Login response
+ *
+ *****************************************************************************
+ */
+
+/** An SRP login response */
+struct srp_login_rsp {
+ /** Information unit type
+ *
+ * This must be @c SRP_LOGIN_RSP
+ */
+ uint8_t type;
+ /** Reserved */
+ uint8_t reserved0[3];
+ /** Request limit delta */
+ uint32_t request_limit_delta;
+ /** Tag */
+ struct srp_tag tag;
+ /** Maximum initiator to target IU length */
+ uint32_t max_i_t_iu_len;
+ /** Maximum target to initiator IU length */
+ uint32_t max_t_i_iu_len;
+ /** Supported buffer formats
+ *
+ * This is the bitwise OR of one or more @c
+ * SRP_LOGIN_RSP_FMT_XXX constants.
+ */
+ uint16_t supported_buffer_formats;
+ /** Flags
+ *
+ * This is the bitwise OR of zero or more @c
+ * SRP_LOGIN_RSP_FLAG_XXX and @c SRP_LOGIN_RSP_MCR_XXX
+ * constants.
+ */
+ uint8_t flags;
+ /** Reserved */
+ uint8_t reserved1[25];
+} __attribute__ (( packed ));
+
+/** Type of an SRP login response */
+#define SRP_LOGIN_RSP 0xc0
+
+/** Indirect data buffer descriptor format supported */
+#define SRP_LOGIN_RSP_FMT_IDBD 0x04
+
+/** Direct data buffer descriptor format supported */
+#define SRP_LOGIN_RSP_FMT_DDBD 0x02
+
+/** Solicited notification is supported */
+#define SRP_LOGIN_RSP_FLAG_SOLNTSUP 0x10
+
+/** Multi-channel result mask */
+#define SRP_LOGIN_RSP_MCR_MASK 0x03
+
+/** No existing RDMA channels were associated with the same I_T nexus */
+#define SRP_LOGIN_RSP_MCR_NO_EXISTING_CHANNELS 0x00
+
+/** One or more existing RDMA channels were terminated */
+#define SRP_LOGIN_RSP_MCR_EXISTING_CHANNELS_TERMINATED 0x01
+
+/** One or more existing RDMA channels continue to operate independently */
+#define SRP_LOGIN_RSP_MCR_EXISTING_CHANNELS_CONTINUE 0x02
+
+/*****************************************************************************
+ *
+ * Login rejection
+ *
+ *****************************************************************************
+ */
+
+/** An SRP login rejection */
+struct srp_login_rej {
+ /** Information unit type
+ *
+ * This must be @c SRP_LOGIN_REJ
+ */
+ uint8_t type;
+ /** Reserved */
+ uint8_t reserved0[3];
+ /** Reason
+ *
+ * This is a @c SRP_LOGIN_REJ_REASON_XXX constant.
+ */
+ uint32_t reason;
+ /** Tag */
+ struct srp_tag tag;
+ /** Reserved */
+ uint8_t reserved1[8];
+ /** Supported buffer formats
+ *
+ * This is the bitwise OR of one or more @c
+ * SRP_LOGIN_REJ_FMT_XXX constants.
+ */
+ uint16_t supported_buffer_formats;
+ /** Reserved */
+ uint8_t reserved2[6];
+} __attribute__ (( packed ));
+
+/** Type of an SRP login rejection */
+#define SRP_LOGIN_REJ 0xc2
+
+/** Unable to establish RDMA channel, no reason specified */
+#define SRP_LOGIN_REJ_REASON_UNKNOWN 0x00010000UL
+
+/** Insufficient RDMA channel resources */
+#define SRP_LOGIN_REJ_REASON_INSUFFICIENT_RESOURCES 0x00010001UL
+
+/** Requested maximum initiator to target IU length value too large */
+#define SRP_LOGIN_REJ_REASON_BAD_MAX_I_T_IU_LEN 0x00010002UL
+
+/** Unable to associate RDMA channel with specified I_T nexus */
+#define SRP_LOGIN_REJ_REASON_CANNOT_ASSOCIATE 0x00010003UL
+
+/** One or more requested data buffer descriptor formats are not supported */
+#define SRP_LOGIN_REJ_REASON_UNSUPPORTED_BUFFER_FORMAT 0x00010004UL
+
+/** SRP target port does not support multiple RDMA channels per I_T nexus */
+#define SRP_LOGIN_REJ_REASON_NO_MULTIPLE_CHANNELS 0x00010005UL
+
+/** RDMA channel limit reached for this initiator */
+#define SRP_LOGIN_REJ_REASON_NO_MORE_CHANNELS 0x00010006UL
+
+/** Indirect data buffer descriptor format supported */
+#define SRP_LOGIN_REJ_FMT_IDBD 0x04
+
+/** Direct data buffer descriptor format supported */
+#define SRP_LOGIN_REJ_FMT_DDBD 0x02
+
+/*****************************************************************************
+ *
+ * Initiator logout
+ *
+ *****************************************************************************
+ */
+
+/** An SRP initiator logout request */
+struct srp_i_logout {
+ /** Information unit type
+ *
+ * This must be @c SRP_I_LOGOUT
+ */
+ uint8_t type;
+ /** Reserved */
+ uint8_t reserved0[7];
+ /** Tag */
+ struct srp_tag tag;
+} __attribute__ (( packed ));
+
+/** Type of an SRP initiator logout request */
+#define SRP_I_LOGOUT 0x03
+
+/*****************************************************************************
+ *
+ * Target logout
+ *
+ *****************************************************************************
+ */
+
+/** An SRP target logout request */
+struct srp_t_logout {
+ /** Information unit type
+ *
+ * This must be @c SRP_T_LOGOUT
+ */
+ uint8_t type;
+ /** Flags
+ *
+ * This is the bitwise OR of zero or more @c
+ * SRP_T_LOGOUT_FLAG_XXX constants.
+ */
+ uint8_t flags;
+ /** Reserved */
+ uint8_t reserved0[2];
+ /** Reason
+ *
+ * This is a @c SRP_T_LOGOUT_REASON_XXX constant.
+ */
+ uint32_t reason;
+ /** Tag */
+ struct srp_tag tag;
+} __attribute__ (( packed ));
+
+/** Type of an SRP target logout request */
+#define SRP_T_LOGOUT 0x80
+
+/** The initiator specified solicited notification of logouts */
+#define SRP_T_LOGOUT_FLAG_SOLNT 0x01
+
+/** No reason specified */
+#define SRP_T_LOGOUT_REASON_UNKNOWN 0x00000000UL
+
+/** Inactive RDMA channel (reclaiming resources) */
+#define SRP_T_LOGOUT_REASON_INACTIVE 0x00000001UL
+
+/** Invalid information unit type code received by SRP target port */
+#define SRP_T_LOGOUT_REASON_INVALID_TYPE 0x00000002UL
+
+/** SRP initiator port sent response with no corresponding request */
+#define SRP_T_LOGOUT_REASON_SPURIOUS_RESPONSE 0x00000003UL
+
+/** RDMA channel disconnected due to multi-channel action code in new login */
+#define SRP_T_LOGOUT_REASON_MCA 0x00000004UL
+
+/** Unsuppported format code value specified in data-out buffer descriptor */
+#define SRP_T_LOGOUT_UNSUPPORTED_DATA_OUT_FORMAT 0x00000005UL
+
+/** Unsuppported format code value specified in data-in buffer descriptor */
+#define SRP_T_LOGOUT_UNSUPPORTED_DATA_IN_FORMAT 0x00000006UL
+
+/** Invalid length for IU type */
+#define SRP_T_LOGOUT_INVALID_IU_LEN 0x00000008UL
+
+/*****************************************************************************
+ *
+ * Task management
+ *
+ *****************************************************************************
+ */
+
+/** An SRP task management request */
+struct srp_tsk_mgmt {
+ /** Information unit type
+ *
+ * This must be @c SRP_TSK_MGMT
+ */
+ uint8_t type;
+ /** Flags
+ *
+ * This is the bitwise OR of zero or more
+ * @c SRP_TSK_MGMT_FLAG_XXX constants.
+ */
+ uint8_t flags;
+ /** Reserved */
+ uint8_t reserved0[6];
+ /** Tag */
+ struct srp_tag tag;
+ /** Reserved */
+ uint8_t reserved1[4];
+ /** Logical unit number */
+ struct scsi_lun lun;
+ /** Reserved */
+ uint8_t reserved2[2];
+ /** Task management function
+ *
+ * This is a @c SRP_TASK_MGMT_FUNC_XXX constant
+ */
+ uint8_t function;
+ /** Reserved */
+ uint8_t reserved3[1];
+ /** Tag of task to be managed */
+ struct srp_tag managed_tag;
+ /** Reserved */
+ uint8_t reserved4[8];
+} __attribute__ (( packed ));
+
+/** Type of an SRP task management request */
+#define SRP_TSK_MGMT 0x01
+
+/** Use solicited notification for unsuccessful completions */
+#define SRP_TSK_MGMT_FLAG_UCSOLNT 0x04
+
+/** Use solicited notification for successful completions */
+#define SRP_TSK_MGMT_FLAG_SCSOLNT 0x02
+
+/** The task manager shall perform an ABORT TASK function */
+#define SRP_TSK_MGMT_FUNC_ABORT_TASK 0x01
+
+/** The task manager shall perform an ABORT TASK SET function */
+#define SRP_TSK_MGMT_FUNC_ABORT_TASK_SET 0x02
+
+/** The task manager shall perform a CLEAR TASK SET function */
+#define SRP_TSK_MGMT_FUNC_CLEAR_TASK_SET 0x04
+
+/** The task manager shall perform a LOGICAL UNIT RESET function */
+#define SRP_TSK_MGMT_FUNC_LOGICAL_UNIT_RESET 0x08
+
+/** The task manager shall perform a CLEAR ACA function */
+#define SRP_TSK_MGMT_FUNC_CLEAR_ACA 0x40
+
+/*****************************************************************************
+ *
+ * SCSI command
+ *
+ *****************************************************************************
+ */
+
+/** An SRP SCSI command */
+struct srp_cmd {
+ /** Information unit type
+ *
+ * This must be @c SRP_CMD
+ */
+ uint8_t type;
+ /** Flags
+ *
+ * This is the bitwise OR of zero or more @c SRP_CMD_FLAG_XXX
+ * constants.
+ */
+ uint8_t flags;
+ /** Reserved */
+ uint8_t reserved0[3];
+ /** Data buffer descriptor formats
+ *
+ * This is the bitwise OR of one @c SRP_CMD_DO_FMT_XXX and one @c
+ * SRP_CMD_DI_FMT_XXX constant.
+ */
+ uint8_t data_buffer_formats;
+ /** Data-out buffer descriptor count */
+ uint8_t data_out_buffer_count;
+ /** Data-in buffer descriptor count */
+ uint8_t data_in_buffer_count;
+ /** Tag */
+ struct srp_tag tag;
+ /** Reserved */
+ uint8_t reserved1[4];
+ /** Logical unit number */
+ struct scsi_lun lun;
+ /** Reserved */
+ uint8_t reserved2[1];
+ /** Task attribute
+ *
+ * This is a @c SRP_CMD_TASK_ATTR_XXX constant.
+ */
+ uint8_t task_attr;
+ /** Reserved */
+ uint8_t reserved3[1];
+ /** Additional CDB length */
+ uint8_t additional_cdb_len;
+ /** Command data block */
+ union scsi_cdb cdb;
+} __attribute__ (( packed ));
+
+/** Type of an SRP SCSI command */
+#define SRP_CMD 0x02
+
+/** Use solicited notification for unsuccessful completions */
+#define SRP_CMD_FLAG_UCSOLNT 0x04
+
+/** Use solicited notification for successful completions */
+#define SRP_CMD_FLAG_SCSOLNT 0x02
+
+/** Data-out buffer format mask */
+#define SRP_CMD_DO_FMT_MASK 0xf0
+
+/** Direct data-out buffer format */
+#define SRP_CMD_DO_FMT_DIRECT 0x10
+
+/** Indirect data-out buffer format */
+#define SRP_CMD_DO_FMT_INDIRECT 0x20
+
+/** Data-in buffer format mask */
+#define SRP_CMD_DI_FMT_MASK 0x0f
+
+/** Direct data-in buffer format */
+#define SRP_CMD_DI_FMT_DIRECT 0x01
+
+/** Indirect data-in buffer format */
+#define SRP_CMD_DI_FMT_INDIRECT 0x02
+
+/** Use the rules for a simple task attribute */
+#define SRP_CMD_TASK_ATTR_SIMPLE 0x00
+
+/** Use the rules for a head of queue task attribute */
+#define SRP_CMD_TASK_ATTR_QUEUE_HEAD 0x01
+
+/** Use the rules for an ordered task attribute */
+#define SRP_CMD_TASK_ATTR_ORDERED 0x02
+
+/** Use the rules for an automatic contingent allegiance task attribute */
+#define SRP_CMD_TASK_ATTR_AUTOMATIC_CONTINGENT_ALLEGIANCE 0x08
+
+/** An SRP memory descriptor */
+struct srp_memory_descriptor {
+ /** Virtual address */
+ uint64_t address;
+ /** Memory handle */
+ uint32_t handle;
+ /** Data length */
+ uint32_t len;
+} __attribute__ (( packed ));
+
+/*****************************************************************************
+ *
+ * SCSI response
+ *
+ *****************************************************************************
+ */
+
+/** An SRP SCSI response */
+struct srp_rsp {
+ /** Information unit type
+ *
+ * This must be @c SRP_RSP
+ */
+ uint8_t type;
+ /** Flags
+ *
+ * This is the bitwise OR of zero or more @c SRP_RSP_FLAG_XXX
+ * constants.
+ */
+ uint8_t flags;
+ /** Reserved */
+ uint8_t reserved0[2];
+ /** Request limit delta */
+ uint32_t request_limit_delta;
+ /** Tag */
+ struct srp_tag tag;
+ /** Reserved */
+ uint8_t reserved1[2];
+ /** Valid fields
+ *
+ * This is the bitwise OR of zero or more @c SRP_RSP_VALID_XXX
+ * constants.
+ */
+ uint8_t valid;
+ /** Status
+ *
+ * This is the SCSI status code.
+ */
+ uint8_t status;
+ /** Data-out residual count */
+ uint32_t data_out_residual_count;
+ /** Data-in residual count */
+ uint32_t data_in_residual_count;
+ /** Sense data list length */
+ uint32_t sense_data_len;
+ /** Response data list length */
+ uint32_t response_data_len;
+} __attribute__ (( packed ));
+
+/** Type of an SRP SCSI response */
+#define SRP_RSP 0xc1
+
+/** The initiator specified solicited notification of this response */
+#define SRP_RSP_FLAG_SOLNT 0x01
+
+/** Data-in residual count field is valid and represents an underflow */
+#define SRP_RSP_VALID_DIUNDER 0x20
+
+/** Data-in residual count field is valid and represents an overflow */
+#define SRP_RSP_VALID_DIOVER 0x10
+
+/** Data-out residual count field is valid and represents an underflow */
+#define SRP_RSP_VALID_DOUNDER 0x08
+
+/** Data-out residual count field is valid and represents an overflow */
+#define SRP_RSP_VALID_DOOVER 0x04
+
+/** Sense data list length field is valid */
+#define SRP_RSP_VALID_SNSVALID 0x02
+
+/** Response data list length field is valid */
+#define SRP_RSP_VALID_RSPVALID 0x01
+
+/**
+ * Get response data portion of SCSI response
+ *
+ * @v rsp SCSI response
+ * @ret response_data Response data, or NULL if not present
+ */
+static inline void * srp_rsp_response_data ( struct srp_rsp *rsp ) {
+ return ( ( rsp->valid & SRP_RSP_VALID_RSPVALID ) ?
+ ( ( ( void * ) rsp ) + sizeof ( *rsp ) ) : NULL );
+}
+
+/**
+ * Get length of response data portion of SCSI response
+ *
+ * @v rsp SCSI response
+ * @ret response_data_len Response data length
+ */
+static inline size_t srp_rsp_response_data_len ( struct srp_rsp *rsp ) {
+ return ( ( rsp->valid & SRP_RSP_VALID_RSPVALID ) ?
+ ntohl ( rsp->response_data_len ) : 0 );
+}
+
+/**
+ * Get sense data portion of SCSI response
+ *
+ * @v rsp SCSI response
+ * @ret sense_data Sense data, or NULL if not present
+ */
+static inline void * srp_rsp_sense_data ( struct srp_rsp *rsp ) {
+ return ( ( rsp->valid & SRP_RSP_VALID_SNSVALID ) ?
+ ( ( ( void * ) rsp ) + sizeof ( *rsp ) +
+ srp_rsp_response_data_len ( rsp ) ) : NULL );
+}
+
+/**
+ * Get length of sense data portion of SCSI response
+ *
+ * @v rsp SCSI response
+ * @ret sense_data_len Sense data length
+ */
+static inline size_t srp_rsp_sense_data_len ( struct srp_rsp *rsp ) {
+ return ( ( rsp->valid & SRP_RSP_VALID_SNSVALID ) ?
+ ntohl ( rsp->sense_data_len ) : 0 );
+}
+
+/*****************************************************************************
+ *
+ * Credit request
+ *
+ *****************************************************************************
+ */
+
+/** An SRP credit request */
+struct srp_cred_req {
+ /** Information unit type
+ *
+ * This must be @c SRP_CRED_REQ
+ */
+ uint8_t type;
+ /** Flags
+ *
+ * This is the bitwise OR of zero or more
+ * @c SRP_CRED_REQ_FLAG_XXX constants.
+ */
+ uint8_t flags;
+ /** Reserved */
+ uint8_t reserved0[2];
+ /** Request limit delta */
+ uint32_t request_limit_delta;
+ /** Tag */
+ struct srp_tag tag;
+} __attribute__ (( packed ));
+
+/** Type of an SRP credit request */
+#define SRP_CRED_REQ 0x81
+
+/** The initiator specified solicited notification of credit requests */
+#define SRP_CRED_REQ_FLAG_SOLNT 0x01
+
+/*****************************************************************************
+ *
+ * Credit response
+ *
+ *****************************************************************************
+ */
+
+/** An SRP credit response */
+struct srp_cred_rsp {
+ /** Information unit type
+ *
+ * This must be @c SRP_CRED_RSP
+ */
+ uint8_t type;
+ /** Reserved */
+ uint8_t reserved0[7];
+ /** Tag */
+ struct srp_tag tag;
+} __attribute__ (( packed ));
+
+/** Type of an SRP credit response */
+#define SRP_CRED_RSP 0x41
+
+/*****************************************************************************
+ *
+ * Asynchronous event request
+ *
+ *****************************************************************************
+ */
+
+/** An SRP asynchronous event request */
+struct srp_aer_req {
+ /** Information unit type
+ *
+ * This must be @c SRP_AER_REQ
+ */
+ uint8_t type;
+ /** Flags
+ *
+ * This is the bitwise OR of zero or more @c
+ * SRP_AER_REQ_FLAG_XXX constants.
+ */
+ uint8_t flags;
+ /** Reserved */
+ uint8_t reserved0[2];
+ /** Request limit delta */
+ uint32_t request_limit_delta;
+ /** Tag */
+ struct srp_tag tag;
+ /** Reserved */
+ uint8_t reserved1[4];
+ /** Logical unit number */
+ struct scsi_lun lun;
+ /** Sense data list length */
+ uint32_t sense_data_len;
+ /** Reserved */
+ uint8_t reserved2[4];
+} __attribute__ (( packed ));
+
+/** Type of an SRP asynchronous event request */
+#define SRP_AER_REQ 0x82
+
+/** The initiator specified solicited notification of asynchronous events */
+#define SRP_AER_REQ_FLAG_SOLNT 0x01
+
+/**
+ * Get sense data portion of asynchronous event request
+ *
+ * @v aer_req SRP asynchronous event request
+ * @ret sense_data Sense data
+ */
+static inline __always_inline void *
+srp_aer_req_sense_data ( struct srp_aer_req *aer_req ) {
+ return ( ( ( void * ) aer_req ) + sizeof ( *aer_req ) );
+}
+
+/**
+ * Get length of sense data portion of asynchronous event request
+ *
+ * @v aer_req SRP asynchronous event request
+ * @ret sense_data_len Sense data length
+ */
+static inline __always_inline size_t
+srp_aer_req_sense_data_len ( struct srp_aer_req *aer_req ) {
+ return ( ntohl ( aer_req->sense_data_len ) );
+}
+
+/*****************************************************************************
+ *
+ * Asynchronous event response
+ *
+ *****************************************************************************
+ */
+
+/** An SRP asynchronous event response */
+struct srp_aer_rsp {
+ /** Information unit type
+ *
+ * This must be @c SRP_AER_RSP
+ */
+ uint8_t type;
+ /** Reserved */
+ uint8_t reserved0[7];
+ /** Tag */
+ struct srp_tag tag;
+} __attribute__ (( packed ));
+
+/** Type of an SRP asynchronous event response */
+#define SRP_AER_RSP 0x42
+
+/*****************************************************************************
+ *
+ * Information units
+ *
+ *****************************************************************************
+ */
+
+/** Maximum length of any initiator-to-target IU that we will send
+ *
+ * The longest IU is a SRP_CMD with no additional CDB and two direct
+ * data buffer descriptors, which comes to 80 bytes.
+ */
+#define SRP_MAX_I_T_IU_LEN 80
+
+/*****************************************************************************
+ *
+ * SRP device
+ *
+ *****************************************************************************
+ */
+
+struct srp_device;
+
+/** An SRP transport type */
+struct srp_transport_type {
+ /** Length of transport private data */
+ size_t priv_len;
+ /** Parse root path
+ *
+ * @v srp SRP device
+ * @v root_path Root path
+ * @ret Return status code
+ */
+ int ( * parse_root_path ) ( struct srp_device *srp,
+ const char *root_path );
+ /** Connect SRP session
+ *
+ * @v srp SRP device
+ * @ret rc Return status code
+ *
+ * This method should open the underlying socket.
+ */
+ int ( * connect ) ( struct srp_device *srp );
+};
+
+/** An SRP device */
+struct srp_device {
+ /** Reference count */
+ struct refcnt refcnt;
+
+ /** Initiator and target port IDs */
+ struct srp_port_ids port_ids;
+ /** Logical unit number */
+ struct scsi_lun lun;
+ /** Memory handle */
+ uint32_t memory_handle;
+
+ /** Current state
+ *
+ * This is the bitwise-OR of zero or more @c SRP_STATE_XXX
+ * flags.
+ */
+ unsigned int state;
+ /** Retry counter */
+ unsigned int retry_count;
+ /** Instant return status code
+ *
+ * Used to avoid retrying the connection on every new SCSI
+ * command after the retry count has been exceeded.
+ */
+ int instant_rc;
+ /** Current SCSI command */
+ struct scsi_command *command;
+
+ /** Underlying data transfer interface */
+ struct xfer_interface socket;
+
+ /** Transport type */
+ struct srp_transport_type *transport;
+ /** Transport private data */
+ char transport_priv[0];
+};
+
+/**
+ * Get SRP transport private data
+ *
+ * @v srp SRP device
+ * @ret priv SRP transport private data
+ */
+static inline __always_inline void *
+srp_transport_priv ( struct srp_device *srp ) {
+ return ( ( void * ) srp->transport_priv );
+}
+
+/** SRP state flags */
+enum srp_state {
+ /** Underlying socket is open */
+ SRP_STATE_SOCKET_OPEN = 0x0001,
+ /** Session is logged in */
+ SRP_STATE_LOGGED_IN = 0x0002,
+};
+
+/** Maximum number of SRP retry attempts */
+#define SRP_MAX_RETRIES 3
+
+extern int srp_attach ( struct scsi_device *scsi, const char *root_path );
+extern void srp_detach ( struct scsi_device *scsi );
+
+#endif /* _GPXE_SRP_H */
diff --git a/src/net/infiniband/ib_srp.c b/src/net/infiniband/ib_srp.c
new file mode 100644
index 00000000..4191c869
--- /dev/null
+++ b/src/net/infiniband/ib_srp.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2009 Fen Systems Ltd <mbrown@fensystems.co.uk>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+FILE_LICENCE ( BSD2 );
+
+#include <stdlib.h>
+#include <errno.h>
+#include <gpxe/srp.h>
+#include <gpxe/infiniband.h>
+#include <gpxe/ib_cmrc.h>
+#include <gpxe/ib_srp.h>
+
+/**
+ * @file
+ *
+ * SCSI RDMA Protocol over Infiniband
+ *
+ */
+
+/* Disambiguate the various possible EINVALs */
+#define EINVAL_BYTE_STRING_LEN ( EINVAL | EUNIQ_01 )
+#define EINVAL_BYTE_STRING ( EINVAL | EUNIQ_02 )
+#define EINVAL_INTEGER ( EINVAL | EUNIQ_03 )
+#define EINVAL_RP_TOO_SHORT ( EINVAL | EUNIQ_04 )
+
+/** IB SRP parse flags */
+enum ib_srp_parse_flags {
+ IB_SRP_PARSE_REQUIRED = 0x0000,
+ IB_SRP_PARSE_OPTIONAL = 0x8000,
+ IB_SRP_PARSE_FLAG_MASK = 0xf000,
+};
+
+/** IB SRP root path parameters */
+struct ib_srp_root_path {
+ /** SCSI LUN */
+ struct scsi_lun *lun;
+ /** SRP port IDs */
+ struct srp_port_ids *port_ids;
+ /** IB SRP parameters */
+ struct ib_srp_parameters *ib;
+};
+
+/**
+ * Parse IB SRP root path byte-string value
+ *
+ * @v rp_comp Root path component string
+ * @v default_value Default value to use if component string is empty
+ * @ret value Value
+ */
+static int ib_srp_parse_byte_string ( const char *rp_comp, uint8_t *bytes,
+ unsigned int size_flags ) {
+ size_t size = ( size_flags & ~IB_SRP_PARSE_FLAG_MASK );
+ size_t rp_comp_len = strlen ( rp_comp );
+ char buf[3];
+ char *buf_end;
+
+ /* Allow optional components to be empty */
+ if ( ( rp_comp_len == 0 ) &&
+ ( size_flags & IB_SRP_PARSE_OPTIONAL ) )
+ return 0;
+
+ /* Check string length */
+ if ( rp_comp_len != ( 2 * size ) )
+ return -EINVAL_BYTE_STRING_LEN;
+
+ /* Parse byte string */
+ for ( ; size ; size--, rp_comp += 2, bytes++ ) {
+ memcpy ( buf, rp_comp, 2 );
+ buf[2] = '\0';
+ *bytes = strtoul ( buf, &buf_end, 16 );
+ if ( buf_end != &buf[2] )
+ return -EINVAL_BYTE_STRING;
+ }
+ return 0;
+}
+
+/**
+ * Parse IB SRP root path integer value
+ *
+ * @v rp_comp Root path component string
+ * @v default_value Default value to use if component string is empty
+ * @ret value Value
+ */
+static int ib_srp_parse_integer ( const char *rp_comp, int default_value ) {
+ int value;
+ char *end;
+
+ value = strtoul ( rp_comp, &end, 16 );
+ if ( *end )
+ return -EINVAL_INTEGER;
+
+ if ( end == rp_comp )
+ return default_value;
+
+ return value;
+}
+
+/**
+ * Parse IB SRP root path literal component
+ *
+ * @v rp_comp Root path component string
+ * @v rp IB SRP root path
+ * @ret rc Return status code
+ */
+static int ib_srp_parse_literal ( const char *rp_comp __unused,
+ struct ib_srp_root_path *rp __unused ) {
+ /* Ignore */
+ return 0;
+}
+
+/**
+ * Parse IB SRP root path source GID
+ *
+ * @v rp_comp Root path component string
+ * @v rp IB SRP root path
+ * @ret rc Return status code
+ */
+static int ib_srp_parse_sgid ( const char *rp_comp,
+ struct ib_srp_root_path *rp ) {
+ struct ib_device *ibdev;
+
+ /* Default to the GID of the last opened Infiniband device */
+ if ( ( ibdev = last_opened_ibdev() ) != NULL )
+ memcpy ( &rp->ib->sgid, &ibdev->gid, sizeof ( rp->ib->sgid ) );
+
+ return ib_srp_parse_byte_string ( rp_comp, rp->ib->sgid.u.bytes,
+ ( sizeof ( rp->ib->sgid ) |
+ IB_SRP_PARSE_OPTIONAL ) );
+}
+
+/**
+ * Parse IB SRP root path initiator identifier extension
+ *
+ * @v rp_comp Root path component string
+ * @v rp IB SRP root path
+ * @ret rc Return status code
+ */
+static int ib_srp_parse_initiator_id_ext ( const char *rp_comp,
+ struct ib_srp_root_path *rp ) {
+ struct ib_srp_initiator_port_id *port_id =
+ ib_srp_initiator_port_id ( rp->port_ids );
+
+ return ib_srp_parse_byte_string ( rp_comp, port_id->id_ext.u.bytes,
+ ( sizeof ( port_id->id_ext ) |
+ IB_SRP_PARSE_OPTIONAL ) );
+}
+
+/**
+ * Parse IB SRP root path initiator HCA GUID
+ *
+ * @v rp_comp Root path component string
+ * @v rp IB SRP root path
+ * @ret rc Return status code
+ */
+static int ib_srp_parse_initiator_hca_guid ( const char *rp_comp,
+ struct ib_srp_root_path *rp ) {
+ struct ib_srp_initiator_port_id *port_id =
+ ib_srp_initiator_port_id ( rp->port_ids );
+
+ /* Default to the GUID portion of the source GID */
+ memcpy ( &port_id->hca_guid, &rp->ib->sgid.u.half[1],
+ sizeof ( port_id->hca_guid ) );
+
+ return ib_srp_parse_byte_string ( rp_comp, port_id->hca_guid.u.bytes,
+ ( sizeof ( port_id->hca_guid ) |
+ IB_SRP_PARSE_OPTIONAL ) );
+}
+
+/**
+ * Parse IB SRP root path destination GID
+ *
+ * @v rp_comp Root path component string
+ * @v rp IB SRP root path
+ * @ret rc Return status code
+ */
+static int ib_srp_parse_dgid ( const char *rp_comp,
+ struct ib_srp_root_path *rp ) {
+ return ib_srp_parse_byte_string ( rp_comp, rp->ib->dgid.u.bytes,
+ ( sizeof ( rp->ib->dgid ) |
+ IB_SRP_PARSE_REQUIRED ) );
+}
+
+/**
+ * Parse IB SRP root path partition key
+ *
+ * @v rp_comp Root path component string
+ * @v rp IB SRP root path
+ * @ret rc Return status code
+ */
+static int ib_srp_parse_pkey ( const char *rp_comp,
+ struct ib_srp_root_path *rp ) {
+ int pkey;
+
+ if ( ( pkey = ib_srp_parse_integer ( rp_comp, IB_PKEY_NONE ) ) < 0 )
+ return pkey;
+ rp->ib->pkey = pkey;
+ return 0;
+}
+
+/**
+ * Parse IB SRP root path service ID
+ *
+ * @v rp_comp Root path component string
+ * @v rp IB SRP root path
+ * @ret rc Return status code
+ */
+static int ib_srp_parse_service_id ( const char *rp_comp,
+ struct ib_srp_root_path *rp ) {
+ return ib_srp_parse_byte_string ( rp_comp, rp->ib->service_id.u.bytes,
+ ( sizeof ( rp->ib->service_id ) |
+ IB_SRP_PARSE_REQUIRED ) );
+}
+
+/**
+ * Parse IB SRP root path LUN
+ *
+ * @v rp_comp Root path component string
+ * @v rp IB SRP root path
+ * @ret rc Return status code
+ */
+static int ib_srp_parse_lun ( const char *rp_comp,
+ struct ib_srp_root_path *rp ) {
+ return scsi_parse_lun ( rp_comp, rp->lun );
+}
+
+/**
+ * Parse IB SRP root path target identifier extension
+ *
+ * @v rp_comp Root path component string
+ * @v rp IB SRP root path
+ * @ret rc Return status code
+ */
+static int ib_srp_parse_target_id_ext ( const char *rp_comp,
+ struct ib_srp_root_path *rp ) {
+ struct ib_srp_target_port_id *port_id =
+ ib_srp_target_port_id ( rp->port_ids );
+
+ return ib_srp_parse_byte_string ( rp_comp, port_id->id_ext.u.bytes,
+ ( sizeof ( port_id->id_ext ) |
+ IB_SRP_PARSE_REQUIRED ) );
+}
+
+/**
+ * Parse IB SRP root path target I/O controller GUID
+ *
+ * @v rp_comp Root path component string
+ * @v rp IB SRP root path
+ * @ret rc Return status code
+ */
+static int ib_srp_parse_target_ioc_guid ( const char *rp_comp,
+ struct ib_srp_root_path *rp ) {
+ struct ib_srp_target_port_id *port_id =
+ ib_srp_target_port_id ( rp->port_ids );
+
+ return ib_srp_parse_byte_string ( rp_comp, port_id->ioc_guid.u.bytes,
+ ( sizeof ( port_id->ioc_guid ) |
+ IB_SRP_PARSE_REQUIRED ) );
+}
+
+/** IB SRP root path component parser */
+struct ib_srp_root_path_parser {
+ /**
+ * Parse IB SRP root path component
+ *
+ * @v rp_comp Root path component string
+ * @v rp IB SRP root path
+ * @ret rc Return status code
+ */
+ int ( * parse ) ( const char *rp_comp, struct ib_srp_root_path *rp );
+};
+
+/** IB SRP root path components */
+static struct ib_srp_root_path_parser ib_srp_rp_parser[] = {
+ { ib_srp_parse_literal },
+ { ib_srp_parse_sgid },
+ { ib_srp_parse_initiator_id_ext },
+ { ib_srp_parse_initiator_hca_guid },
+ { ib_srp_parse_dgid },
+ { ib_srp_parse_pkey },
+ { ib_srp_parse_service_id },
+ { ib_srp_parse_lun },
+ { ib_srp_parse_target_id_ext },
+ { ib_srp_parse_target_ioc_guid },
+};
+
+/** Number of IB SRP root path components */
+#define IB_SRP_NUM_RP_COMPONENTS \
+ ( sizeof ( ib_srp_rp_parser ) / sizeof ( ib_srp_rp_parser[0] ) )
+
+/**
+ * Parse IB SRP root path
+ *
+ * @v srp SRP device
+ * @v rp_string Root path
+ * @ret rc Return status code
+ */
+static int ib_srp_parse_root_path ( struct srp_device *srp,
+ const char *rp_string ) {
+ struct ib_srp_parameters *ib_params = ib_srp_params ( srp );
+ struct ib_srp_root_path rp = {
+ .lun = &srp->lun,
+ .port_ids = &srp->port_ids,
+ .ib = ib_params,
+ };
+ char rp_string_copy[ strlen ( rp_string ) + 1 ];
+ char *rp_comp[IB_SRP_NUM_RP_COMPONENTS];
+ char *rp_string_tmp = rp_string_copy;
+ unsigned int i = 0;
+ int rc;
+
+ /* Split root path into component parts */
+ strcpy ( rp_string_copy, rp_string );
+ while ( 1 ) {
+ rp_comp[i++] = rp_string_tmp;
+ if ( i == IB_SRP_NUM_RP_COMPONENTS )
+ break;
+ for ( ; *rp_string_tmp != ':' ; rp_string_tmp++ ) {
+ if ( ! *rp_string_tmp ) {
+ DBGC ( srp, "SRP %p root path \"%s\" too "
+ "short\n", srp, rp_string );
+ return -EINVAL_RP_TOO_SHORT;
+ }
+ }
+ *(rp_string_tmp++) = '\0';
+ }
+
+ /* Parse root path components */
+ for ( i = 0 ; i < IB_SRP_NUM_RP_COMPONENTS ; i++ ) {
+ if ( ( rc = ib_srp_rp_parser[i].parse ( rp_comp[i],
+ &rp ) ) != 0 ) {
+ DBGC ( srp, "SRP %p could not parse \"%s\" in root "
+ "path \"%s\": %s\n", srp, rp_comp[i],
+ rp_string, strerror ( rc ) );
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Connect IB SRP session
+ *
+ * @v srp SRP device
+ * @ret rc Return status code
+ */
+static int ib_srp_connect ( struct srp_device *srp ) {
+ struct ib_srp_parameters *ib_params = ib_srp_params ( srp );
+ struct ib_device *ibdev;
+ int rc;
+
+ /* Identify Infiniband device */
+ ibdev = find_ibdev ( &ib_params->sgid );
+ if ( ! ibdev ) {
+ DBGC ( srp, "SRP %p could not identify Infiniband device\n",
+ srp );
+ return -ENODEV;
+ }
+
+ /* Configure remaining SRP parameters */
+ srp->memory_handle = ibdev->rdma_key;
+
+ /* Open CMRC socket */
+ if ( ( rc = ib_cmrc_open ( &srp->socket, ibdev, &ib_params->dgid,
+ &ib_params->service_id ) ) != 0 ) {
+ DBGC ( srp, "SRP %p could not open CMRC socket: %s\n",
+ srp, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/** IB SRP transport type */
+struct srp_transport_type ib_srp_transport = {
+ .priv_len = sizeof ( struct ib_srp_parameters ),
+ .parse_root_path = ib_srp_parse_root_path,
+ .connect = ib_srp_connect,
+};