#ifndef _GPXE_SRP_H #define _GPXE_SRP_H /** @file * * SCSI RDMA Protocol * */ FILE_LICENCE ( BSD2 ); #include #include #include #include #include /***************************************************************************** * * 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; /** 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 */