summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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,
+};