Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / net / infiniband / ib_srp.c
diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_srp.c b/qemu/roms/ipxe/src/net/infiniband/ib_srp.c
new file mode 100644 (file)
index 0000000..7b2b2b4
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+ * 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 <ipxe/interface.h>
+#include <ipxe/uri.h>
+#include <ipxe/open.h>
+#include <ipxe/base16.h>
+#include <ipxe/acpi.h>
+#include <ipxe/srp.h>
+#include <ipxe/infiniband.h>
+#include <ipxe/ib_cmrc.h>
+#include <ipxe/ib_srp.h>
+
+/**
+ * @file
+ *
+ * SCSI RDMA Protocol over Infiniband
+ *
+ */
+
+/* Disambiguate the various possible EINVALs */
+#define EINVAL_BYTE_STRING_LEN __einfo_error ( EINFO_EINVAL_BYTE_STRING_LEN )
+#define EINFO_EINVAL_BYTE_STRING_LEN __einfo_uniqify \
+       ( EINFO_EINVAL, 0x01, "Invalid byte string length" )
+#define EINVAL_INTEGER __einfo_error ( EINFO_EINVAL_INTEGER )
+#define EINFO_EINVAL_INTEGER __einfo_uniqify \
+       ( EINFO_EINVAL, 0x03, "Invalid integer" )
+#define EINVAL_RP_TOO_SHORT __einfo_error ( EINFO_EINVAL_RP_TOO_SHORT )
+#define EINFO_EINVAL_RP_TOO_SHORT __einfo_uniqify \
+       ( EINFO_EINVAL, 0x04, "Root path too short" )
+
+/******************************************************************************
+ *
+ * IB SRP devices
+ *
+ ******************************************************************************
+ */
+
+/** An Infiniband SRP device */
+struct ib_srp_device {
+       /** Reference count */
+       struct refcnt refcnt;
+
+       /** SRP transport interface */
+       struct interface srp;
+       /** CMRC interface */
+       struct interface cmrc;
+
+       /** Infiniband device */
+       struct ib_device *ibdev;
+
+       /** Destination GID (for boot firmware table) */
+       union ib_gid dgid;
+       /** Service ID (for boot firmware table) */
+       union ib_guid service_id;
+};
+
+/**
+ * Free IB SRP device
+ *
+ * @v refcnt           Reference count
+ */
+static void ib_srp_free ( struct refcnt *refcnt ) {
+       struct ib_srp_device *ib_srp =
+               container_of ( refcnt, struct ib_srp_device, refcnt );
+
+       ibdev_put ( ib_srp->ibdev );
+       free ( ib_srp );
+}
+
+/**
+ * Close IB SRP device
+ *
+ * @v ib_srp           IB SRP device
+ * @v rc               Reason for close
+ */
+static void ib_srp_close ( struct ib_srp_device *ib_srp, int rc ) {
+
+       /* Shut down interfaces */
+       intf_shutdown ( &ib_srp->cmrc, rc );
+       intf_shutdown ( &ib_srp->srp, rc );
+}
+
+/**
+ * Describe IB SRP device in an ACPI table
+ *
+ * @v srpdev           SRP device
+ * @v acpi             ACPI table
+ * @v len              Length of ACPI table
+ * @ret rc             Return status code
+ */
+static int ib_srp_describe ( struct ib_srp_device *ib_srp,
+                            struct acpi_description_header *acpi,
+                            size_t len ) {
+       struct ib_device *ibdev = ib_srp->ibdev;
+       struct sbft_table *sbft =
+               container_of ( acpi, struct sbft_table, acpi );
+       struct sbft_ib_subtable *ib_sbft;
+       size_t used;
+
+       /* Sanity check */
+       if ( acpi->signature != SBFT_SIG )
+               return -EINVAL;
+
+       /* Append IB subtable to existing table */
+       used = le32_to_cpu ( sbft->acpi.length );
+       sbft->ib_offset = cpu_to_le16 ( used );
+       ib_sbft = ( ( ( void * ) sbft ) + used );
+       used += sizeof ( *ib_sbft );
+       if ( used > len )
+               return -ENOBUFS;
+       sbft->acpi.length = cpu_to_le32 ( used );
+
+       /* Populate subtable */
+       memcpy ( &ib_sbft->sgid, &ibdev->gid, sizeof ( ib_sbft->sgid ) );
+       memcpy ( &ib_sbft->dgid, &ib_srp->dgid, sizeof ( ib_sbft->dgid ) );
+       memcpy ( &ib_sbft->service_id, &ib_srp->service_id,
+                sizeof ( ib_sbft->service_id ) );
+       ib_sbft->pkey = cpu_to_le16 ( ibdev->pkey );
+
+       return 0;
+}
+
+/** IB SRP CMRC interface operations */
+static struct interface_operation ib_srp_cmrc_op[] = {
+       INTF_OP ( intf_close, struct ib_srp_device *, ib_srp_close ),
+};
+
+/** IB SRP CMRC interface descriptor */
+static struct interface_descriptor ib_srp_cmrc_desc =
+       INTF_DESC_PASSTHRU ( struct ib_srp_device, cmrc, ib_srp_cmrc_op, srp );
+
+/** IB SRP SRP interface operations */
+static struct interface_operation ib_srp_srp_op[] = {
+       INTF_OP ( acpi_describe, struct ib_srp_device *, ib_srp_describe ),
+       INTF_OP ( intf_close, struct ib_srp_device *, ib_srp_close ),
+};
+
+/** IB SRP SRP interface descriptor */
+static struct interface_descriptor ib_srp_srp_desc =
+       INTF_DESC_PASSTHRU ( struct ib_srp_device, srp, ib_srp_srp_op, cmrc );
+
+/**
+ * Open IB SRP device
+ *
+ * @v block            Block control interface
+ * @v ibdev            Infiniband device
+ * @v dgid             Destination GID
+ * @v service_id       Service ID
+ * @v initiator                Initiator port ID
+ * @v target           Target port ID
+ * @v lun              SCSI LUN
+ * @ret rc             Return status code
+ */
+static int ib_srp_open ( struct interface *block, struct ib_device *ibdev,
+                        union ib_gid *dgid, union ib_guid *service_id,
+                        union srp_port_id *initiator,
+                        union srp_port_id *target, struct scsi_lun *lun ) {
+       struct ib_srp_device *ib_srp;
+       int rc;
+
+       /* Allocate and initialise structure */
+       ib_srp = zalloc ( sizeof ( *ib_srp ) );
+       if ( ! ib_srp ) {
+               rc = -ENOMEM;
+               goto err_zalloc;
+       }
+       ref_init ( &ib_srp->refcnt, ib_srp_free );
+       intf_init ( &ib_srp->srp, &ib_srp_srp_desc, &ib_srp->refcnt );
+       intf_init ( &ib_srp->cmrc, &ib_srp_cmrc_desc, &ib_srp->refcnt );
+       ib_srp->ibdev = ibdev_get ( ibdev );
+       DBGC ( ib_srp, "IBSRP %p for " IB_GID_FMT " " IB_GUID_FMT "\n",
+              ib_srp, IB_GID_ARGS ( dgid ), IB_GUID_ARGS ( service_id ) );
+
+       /* Preserve parameters required for boot firmware table */
+       memcpy ( &ib_srp->dgid, dgid, sizeof ( ib_srp->dgid ) );
+       memcpy ( &ib_srp->service_id, service_id,
+                sizeof ( ib_srp->service_id ) );
+
+       /* Open CMRC socket */
+       if ( ( rc = ib_cmrc_open ( &ib_srp->cmrc, ibdev, dgid,
+                                  service_id ) ) != 0 ) {
+               DBGC ( ib_srp, "IBSRP %p could not open CMRC socket: %s\n",
+                      ib_srp, strerror ( rc ) );
+               goto err_cmrc_open;
+       }
+
+       /* Attach SRP device to parent interface */
+       if ( ( rc = srp_open ( block, &ib_srp->srp, initiator, target,
+                              ibdev->rdma_key, lun ) ) != 0 ) {
+               DBGC ( ib_srp, "IBSRP %p could not create SRP device: %s\n",
+                      ib_srp, strerror ( rc ) );
+               goto err_srp_open;
+       }
+
+       /* Mortalise self and return */
+       ref_put ( &ib_srp->refcnt );
+       return 0;
+
+ err_srp_open:
+ err_cmrc_open:
+       ib_srp_close ( ib_srp, rc );
+       ref_put ( &ib_srp->refcnt );
+ err_zalloc:
+       return rc;
+}
+
+/******************************************************************************
+ *
+ * IB SRP URIs
+ *
+ ******************************************************************************
+ */
+
+/** 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 {
+       /** Source GID */
+       union ib_gid sgid;
+       /** Initiator port ID */
+       union ib_srp_initiator_port_id initiator;
+       /** Destination GID */
+       union ib_gid dgid;
+       /** Partition key */
+       uint16_t pkey;
+       /** Service ID */
+       union ib_guid service_id;
+       /** SCSI LUN */
+       struct scsi_lun lun;
+       /** Target port ID */
+       union ib_srp_target_port_id target;
+};
+
+/**
+ * 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 );
+       int decoded_size;
+
+       /* 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 */
+       decoded_size = base16_decode ( rp_comp, bytes );
+       if ( decoded_size < 0 )
+               return decoded_size;
+
+       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 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->sgid, &ibdev->gid, sizeof ( rp->sgid ) );
+
+       return ib_srp_parse_byte_string ( rp_comp, rp->sgid.bytes,
+                                         ( sizeof ( rp->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 ) {
+       union ib_srp_initiator_port_id *port_id = &rp->initiator;
+
+       return ib_srp_parse_byte_string ( rp_comp, port_id->ib.id_ext.bytes,
+                                         ( sizeof ( port_id->ib.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 ) {
+       union ib_srp_initiator_port_id *port_id = &rp->initiator;
+
+       /* Default to the GUID portion of the source GID */
+       memcpy ( &port_id->ib.hca_guid, &rp->sgid.s.guid,
+                sizeof ( port_id->ib.hca_guid ) );
+
+       return ib_srp_parse_byte_string ( rp_comp, port_id->ib.hca_guid.bytes,
+                                         ( sizeof ( port_id->ib.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->dgid.bytes,
+                                         ( sizeof ( rp->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_DEFAULT ) ) < 0 )
+               return pkey;
+       rp->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->service_id.bytes,
+                                         ( sizeof ( rp->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 ) {
+       union ib_srp_target_port_id *port_id = &rp->target;
+
+       return ib_srp_parse_byte_string ( rp_comp, port_id->ib.id_ext.bytes,
+                                         ( sizeof ( port_id->ib.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 ) {
+       union ib_srp_target_port_id *port_id = &rp->target;
+
+       return ib_srp_parse_byte_string ( rp_comp, port_id->ib.ioc_guid.bytes,
+                                         ( sizeof ( port_id->ib.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_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 rp_string                Root path string
+ * @v rp               IB SRP root path
+ * @ret rc             Return status code
+ */
+static int ib_srp_parse_root_path ( const char *rp_string,
+                                   struct ib_srp_root_path *rp ) {
+       struct ib_srp_root_path_parser *parser;
+       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 ) {
+                               DBG ( "IBSRP root path \"%s\" too short\n",
+                                     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++ ) {
+               parser = &ib_srp_rp_parser[i];
+               if ( ( rc = parser->parse ( rp_comp[i], rp ) ) != 0 ) {
+                       DBG ( "IBSRP could not parse \"%s\" in root path "
+                             "\"%s\": %s\n", rp_comp[i], rp_string,
+                             strerror ( rc ) );
+                       return rc;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * Open IB SRP URI
+ *
+ * @v parent           Parent interface
+ * @v uri              URI
+ * @ret rc             Return status code
+ */
+static int ib_srp_open_uri ( struct interface *parent, struct uri *uri ) {
+       struct ib_srp_root_path rp;
+       struct ib_device *ibdev;
+       int rc;
+
+       /* Parse URI */
+       if ( ! uri->opaque )
+               return -EINVAL;
+       memset ( &rp, 0, sizeof ( rp ) );
+       if ( ( rc = ib_srp_parse_root_path ( uri->opaque, &rp ) ) != 0 )
+               return rc;
+
+       /* Identify Infiniband device */
+       ibdev = find_ibdev ( &rp.sgid );
+       if ( ! ibdev ) {
+               DBG ( "IBSRP could not identify Infiniband device\n" );
+               return -ENODEV;
+       }
+
+       /* Open IB SRP device */
+       if ( ( rc = ib_srp_open ( parent, ibdev, &rp.dgid, &rp.service_id,
+                                 &rp.initiator.srp, &rp.target.srp,
+                                 &rp.lun ) ) != 0 )
+               return rc;
+
+       return 0;
+}
+
+/** IB SRP URI opener */
+struct uri_opener ib_srp_uri_opener __uri_opener = {
+       .scheme = "ib_srp",
+       .open = ib_srp_open_uri,
+};