| /* |
| * 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/efi/efi_path.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" ) |
| |
| struct acpi_model ib_sbft_model __acpi_model; |
| |
| /****************************************************************************** |
| * |
| * IB SRP devices |
| * |
| ****************************************************************************** |
| */ |
| |
| /** |
| * 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 ); |
| } |
| |
| /** |
| * Get IB SRP ACPI descriptor |
| * |
| * @v ib_srp IB SRP device |
| * @ret desc ACPI descriptor |
| */ |
| static struct acpi_descriptor * |
| ib_srp_describe ( struct ib_srp_device *ib_srp ) { |
| |
| return &ib_srp->desc; |
| } |
| |
| /** 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 ), |
| EFI_INTF_OP ( efi_describe, struct ib_srp_device *, efi_ib_srp_path ), |
| }; |
| |
| /** 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; |
| struct ipxe_ib_sbft *sbft; |
| 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 ); |
| acpi_init ( &ib_srp->desc, &ib_sbft_model, &ib_srp->refcnt ); |
| 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 */ |
| sbft = &ib_srp->sbft; |
| memcpy ( &sbft->scsi.lun, lun, sizeof ( sbft->scsi.lun ) ); |
| memcpy ( &sbft->srp.initiator, initiator, |
| sizeof ( sbft->srp.initiator ) ); |
| memcpy ( &sbft->srp.target, target, sizeof ( sbft->srp.target ) ); |
| memcpy ( &sbft->ib.dgid, dgid, sizeof ( sbft->ib.dgid ) ); |
| memcpy ( &sbft->ib.service_id, service_id, |
| sizeof ( sbft->ib.service_id ) ); |
| |
| /* Open CMRC socket */ |
| if ( ( rc = ib_cmrc_open ( &ib_srp->cmrc, ibdev, dgid, |
| service_id, "SRP" ) ) != 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, size ); |
| 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_comp[IB_SRP_NUM_RP_COMPONENTS]; |
| char *rp_string_copy; |
| char *rp_string_tmp; |
| unsigned int i = 0; |
| int rc; |
| |
| /* Create modifiable copy of root path */ |
| rp_string_copy = strdup ( rp_string ); |
| if ( ! rp_string_copy ) { |
| rc = -ENOMEM; |
| goto err_strdup; |
| } |
| rp_string_tmp = rp_string_copy; |
| |
| /* Split root path into component parts */ |
| 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 ); |
| rc = -EINVAL_RP_TOO_SHORT; |
| goto err_split; |
| } |
| } |
| *(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 ) ); |
| goto err_parse; |
| } |
| } |
| |
| err_parse: |
| err_split: |
| free ( rp_string_copy ); |
| err_strdup: |
| return rc; |
| } |
| |
| /** |
| * 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, |
| }; |
| |
| /****************************************************************************** |
| * |
| * IB SRP boot firmware table (sBFT) |
| * |
| ****************************************************************************** |
| */ |
| |
| /** |
| * Check if IB SRP boot firmware table descriptor is complete |
| * |
| * @v desc ACPI descriptor |
| * @ret rc Return status code |
| */ |
| static int ib_sbft_complete ( struct acpi_descriptor *desc __unused ) { |
| return 0; |
| } |
| |
| /** |
| * Install IB SRP boot firmware table(s) |
| * |
| * @v install Installation method |
| * @ret rc Return status code |
| */ |
| static int ib_sbft_install ( int ( * install ) ( struct acpi_header *acpi ) ) { |
| struct ib_srp_device *ib_srp; |
| struct ipxe_ib_sbft *sbft; |
| struct ib_device *ibdev; |
| int rc; |
| |
| list_for_each_entry ( ib_srp, &ib_sbft_model.descs, desc.list ) { |
| |
| /* Complete table */ |
| sbft = &ib_srp->sbft; |
| ibdev = ib_srp->ibdev; |
| sbft->table.acpi.signature = cpu_to_le32 ( SBFT_SIG ); |
| sbft->table.acpi.length = cpu_to_le32 ( sizeof ( *sbft ) ); |
| sbft->table.acpi.revision = 1; |
| sbft->table.scsi_offset = |
| cpu_to_le16 ( offsetof ( typeof ( *sbft ), scsi ) ); |
| sbft->table.srp_offset = |
| cpu_to_le16 ( offsetof ( typeof ( *sbft ), srp ) ); |
| sbft->table.ib_offset = |
| cpu_to_le16 ( offsetof ( typeof ( *sbft ), ib ) ); |
| memcpy ( &sbft->ib.sgid, &ibdev->gid, sizeof ( sbft->ib.sgid )); |
| sbft->ib.pkey = cpu_to_le16 ( ibdev->pkey ); |
| |
| /* Install table */ |
| if ( ( rc = install ( &sbft->table.acpi ) ) != 0 ) { |
| DBGC ( ib_srp, "IBSRP %p could not install sBFT: %s\n", |
| ib_srp, strerror ( rc ) ); |
| return rc; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /** IB sBFT model */ |
| struct acpi_model ib_sbft_model __acpi_model = { |
| .descs = LIST_HEAD_INIT ( ib_sbft_model.descs ), |
| .complete = ib_sbft_complete, |
| .install = ib_sbft_install, |
| }; |