iPXE
fcp.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stddef.h>
00027 #include <stdint.h>
00028 #include <stdlib.h>
00029 #include <string.h>
00030 #include <errno.h>
00031 #include <stdio.h>
00032 #include <assert.h>
00033 #include <byteswap.h>
00034 #include <ipxe/refcnt.h>
00035 #include <ipxe/list.h>
00036 #include <ipxe/interface.h>
00037 #include <ipxe/xfer.h>
00038 #include <ipxe/iobuf.h>
00039 #include <ipxe/open.h>
00040 #include <ipxe/process.h>
00041 #include <ipxe/uri.h>
00042 #include <ipxe/acpi.h>
00043 #include <ipxe/scsi.h>
00044 #include <ipxe/device.h>
00045 #include <ipxe/edd.h>
00046 #include <ipxe/fc.h>
00047 #include <ipxe/fcels.h>
00048 #include <ipxe/fcp.h>
00049 
00050 /** @file
00051  *
00052  * Fibre Channel Protocol
00053  *
00054  */
00055 
00056 /* Disambiguate the various error causes */
00057 #define ERANGE_READ_DATA_ORDERING \
00058         __einfo_error ( EINFO_ERANGE_READ_DATA_ORDERING )
00059 #define EINFO_ERANGE_READ_DATA_ORDERING \
00060         __einfo_uniqify ( EINFO_ERANGE, 0x01, "Read data out of order" )
00061 #define ERANGE_READ_DATA_OVERRUN \
00062         __einfo_error ( EINFO_ERANGE_READ_DATA_OVERRUN )
00063 #define EINFO_ERANGE_READ_DATA_OVERRUN \
00064         __einfo_uniqify ( EINFO_ERANGE, 0x02, "Read data overrun" )
00065 #define ERANGE_WRITE_DATA_STUCK \
00066         __einfo_error ( EINFO_ERANGE_WRITE_DATA_STUCK )
00067 #define EINFO_ERANGE_WRITE_DATA_STUCK \
00068         __einfo_uniqify ( EINFO_ERANGE, 0x03, "Write data stuck" )
00069 #define ERANGE_WRITE_DATA_OVERRUN \
00070         __einfo_error ( EINFO_ERANGE_WRITE_DATA_OVERRUN )
00071 #define EINFO_ERANGE_WRITE_DATA_OVERRUN \
00072         __einfo_uniqify ( EINFO_ERANGE, 0x04, "Write data overrun" )
00073 #define ERANGE_DATA_UNDERRUN \
00074         __einfo_error ( EINFO_ERANGE_DATA_UNDERRUN )
00075 #define EINFO_ERANGE_DATA_UNDERRUN \
00076         __einfo_uniqify ( EINFO_ERANGE, 0x05, "Data underrun" )
00077 
00078 /******************************************************************************
00079  *
00080  * PRLI
00081  *
00082  ******************************************************************************
00083  */
00084 
00085 struct fc_els_prli_descriptor fcp_prli_descriptor __fc_els_prli_descriptor;
00086 
00087 /**
00088  * Transmit FCP PRLI
00089  *
00090  * @v els               Fibre Channel ELS transaction
00091  * @ret rc              Return status code
00092  */
00093 static int fcp_prli_tx ( struct fc_els *els ) {
00094         struct fcp_prli_service_parameters param;
00095 
00096         /* Build service parameter page */
00097         memset ( &param, 0, sizeof ( param ) );
00098         param.flags = htonl ( FCP_PRLI_NO_READ_RDY | FCP_PRLI_INITIATOR );
00099 
00100         return fc_els_prli_tx ( els, &fcp_prli_descriptor, &param );
00101 }
00102 
00103 /**
00104  * Receive FCP PRLI
00105  *
00106  * @v els               Fibre Channel ELS transaction
00107  * @v frame             ELS frame
00108  * @v len               Length of ELS frame
00109  * @ret rc              Return status code
00110  */
00111 static int fcp_prli_rx ( struct fc_els *els, void *data, size_t len ) {
00112         return fc_els_prli_rx ( els, &fcp_prli_descriptor, data, len );
00113 }
00114 
00115 /**
00116  * Detect FCP PRLI
00117  *
00118  * @v els               Fibre Channel ELS transaction
00119  * @v data              ELS frame
00120  * @v len               Length of ELS frame
00121  * @ret rc              Return status code
00122  */
00123 static int fcp_prli_detect ( struct fc_els *els, const void *data,
00124                              size_t len ) {
00125         return fc_els_prli_detect ( els, &fcp_prli_descriptor, data, len );
00126 }
00127 
00128 /** FCP PRLI ELS handler */
00129 struct fc_els_handler fcp_prli_handler __fc_els_handler = {
00130         .name           = "PRLI-FCP",
00131         .tx             = fcp_prli_tx,
00132         .rx             = fcp_prli_rx,
00133         .detect         = fcp_prli_detect,
00134 };
00135 
00136 /** FCP PRLI descriptor */
00137 struct fc_els_prli_descriptor fcp_prli_descriptor __fc_els_prli_descriptor = {
00138         .type           = FC_TYPE_FCP,
00139         .param_len      = sizeof ( struct fcp_prli_service_parameters ),
00140         .handler        = &fcp_prli_handler,
00141 };
00142 
00143 /******************************************************************************
00144  *
00145  * FCP devices and commands
00146  *
00147  ******************************************************************************
00148  */
00149 
00150 /** An FCP device */
00151 struct fcp_device {
00152         /** Reference count */
00153         struct refcnt refcnt;
00154         /** Fibre Channel upper-layer protocol user */
00155         struct fc_ulp_user user;
00156         /** SCSI command issuing interface */
00157         struct interface scsi;
00158         /** List of active commands */
00159         struct list_head fcpcmds;
00160 
00161         /** Fibre Channel WWN (for boot firmware table) */
00162         struct fc_name wwn;
00163         /** SCSI LUN (for boot firmware table) */
00164         struct scsi_lun lun;
00165 };
00166 
00167 /** An FCP command */
00168 struct fcp_command {
00169         /** Reference count */
00170         struct refcnt refcnt;
00171         /** FCP SCSI device */
00172         struct fcp_device *fcpdev;
00173         /** List of active commands */
00174         struct list_head list;
00175         /** SCSI command interface */
00176         struct interface scsi;
00177         /** Fibre Channel exchange interface */
00178         struct interface xchg;
00179         /** Send process */
00180         struct process process;
00181         /** Send current IU
00182          *
00183          * @v fcpcmd    FCP command
00184          * @ret rc      Return status code
00185          */
00186         int ( * send ) ( struct fcp_command *fcpcmd );
00187         /** SCSI command */
00188         struct scsi_cmd command;
00189         /** Data offset within command */
00190         size_t offset;
00191         /** Length of data remaining to be sent within this IU */
00192         size_t remaining;
00193         /** Exchange ID */
00194         uint16_t xchg_id;
00195 };
00196 
00197 /**
00198  * Get reference to FCP device
00199  *
00200  * @v fcpdev            FCP device
00201  * @ret fcpdev          FCP device
00202  */
00203 static inline __attribute__ (( always_inline )) struct fcp_device *
00204 fcpdev_get ( struct fcp_device *fcpdev ) {
00205         ref_get ( &fcpdev->refcnt );
00206         return fcpdev;
00207 }
00208 
00209 /**
00210  * Drop reference to FCP device
00211  *
00212  * @v fcpdev            FCP device
00213  */
00214 static inline __attribute__ (( always_inline )) void
00215 fcpdev_put ( struct fcp_device *fcpdev ) {
00216         ref_put ( &fcpdev->refcnt );
00217 }
00218 
00219 /**
00220  * Get reference to FCP command
00221  *
00222  * @v fcpcmd            FCP command
00223  * @ret fcpcmd          FCP command
00224  */
00225 static inline __attribute__ (( always_inline )) struct fcp_command *
00226 fcpcmd_get ( struct fcp_command *fcpcmd ) {
00227         ref_get ( &fcpcmd->refcnt );
00228         return fcpcmd;
00229 }
00230 
00231 /**
00232  * Drop reference to FCP command
00233  *
00234  * @v fcpcmd            FCP command
00235  */
00236 static inline __attribute__ (( always_inline )) void
00237 fcpcmd_put ( struct fcp_command *fcpcmd ) {
00238         ref_put ( &fcpcmd->refcnt );
00239 }
00240 
00241 /**
00242  * Start FCP command sending
00243  *
00244  * @v fcpcmd            FCP command
00245  * @v send              Send method
00246  */
00247 static inline __attribute__ (( always_inline )) void
00248 fcpcmd_start_send ( struct fcp_command *fcpcmd,
00249                     int ( * send ) ( struct fcp_command *fcpcmd ) ) {
00250         fcpcmd->send = send;
00251         process_add ( &fcpcmd->process );
00252 }
00253 
00254 /**
00255  * Stop FCP command sending
00256  *
00257  * @v fcpcmd            FCP command
00258  */
00259 static inline __attribute__ (( always_inline )) void
00260 fcpcmd_stop_send ( struct fcp_command *fcpcmd ) {
00261         process_del ( &fcpcmd->process );
00262 }
00263 
00264 /**
00265  * Free FCP command
00266  *
00267  * @v refcnt            Reference count
00268  */
00269 static void fcpcmd_free ( struct refcnt *refcnt ) {
00270         struct fcp_command *fcpcmd =
00271                 container_of ( refcnt, struct fcp_command, refcnt );
00272 
00273         /* Remove from list of commands */
00274         list_del ( &fcpcmd->list );
00275         fcpdev_put ( fcpcmd->fcpdev );
00276 
00277         /* Free command */
00278         free ( fcpcmd );
00279 }
00280 
00281 /**
00282  * Close FCP command
00283  *
00284  * @v fcpcmd            FCP command
00285  * @v rc                Reason for close
00286  */
00287 static void fcpcmd_close ( struct fcp_command *fcpcmd, int rc ) {
00288         struct fcp_device *fcpdev = fcpcmd->fcpdev;
00289 
00290         if ( rc != 0 ) {
00291                 DBGC ( fcpdev, "FCP %p xchg %04x closed: %s\n",
00292                        fcpdev, fcpcmd->xchg_id, strerror ( rc ) );
00293         }
00294 
00295         /* Stop sending */
00296         fcpcmd_stop_send ( fcpcmd );
00297 
00298         /* Shut down interfaces */
00299         intf_shutdown ( &fcpcmd->scsi, rc );
00300         intf_shutdown ( &fcpcmd->xchg, rc );
00301 }
00302 
00303 /**
00304  * Close FCP command in error
00305  *
00306  * @v fcpcmd            FCP command
00307  * @v rc                Reason for close
00308  */
00309 static void fcpcmd_close_err ( struct fcp_command *fcpcmd, int rc ) {
00310         if ( rc == 0 )
00311                 rc = -EPIPE;
00312         fcpcmd_close ( fcpcmd, rc );
00313 }
00314 
00315 /**
00316  * Send FCP command IU
00317  *
00318  * @v fcpcmd            FCP command
00319  * @ret rc              Return status code
00320  */
00321 static int fcpcmd_send_cmnd ( struct fcp_command *fcpcmd ) {
00322         struct fcp_device *fcpdev = fcpcmd->fcpdev;
00323         struct scsi_cmd *command = &fcpcmd->command;
00324         struct io_buffer *iobuf;
00325         struct fcp_cmnd *cmnd;
00326         struct xfer_metadata meta;
00327         int rc;
00328 
00329         /* Sanity check */
00330         if ( command->data_in_len && command->data_out_len ) {
00331                 DBGC ( fcpdev, "FCP %p xchg %04x cannot handle bidirectional "
00332                        "command\n", fcpdev, fcpcmd->xchg_id );
00333                 return -ENOTSUP;
00334         }
00335 
00336         /* Allocate I/O buffer */
00337         iobuf = xfer_alloc_iob ( &fcpcmd->xchg, sizeof ( *cmnd ) );
00338         if ( ! iobuf ) {
00339                 DBGC ( fcpdev, "FCP %p xchg %04x cannot allocate command IU\n",
00340                        fcpdev, fcpcmd->xchg_id );
00341                 return -ENOMEM;
00342         }
00343 
00344         /* Construct command IU frame */
00345         cmnd = iob_put ( iobuf, sizeof ( *cmnd ) );
00346         memset ( cmnd, 0, sizeof ( *cmnd ) );
00347         memcpy ( &cmnd->lun, &command->lun, sizeof ( cmnd->lun ) );
00348         assert ( ! ( command->data_in_len && command->data_out_len ) );
00349         if ( command->data_in_len )
00350                 cmnd->dirn |= FCP_CMND_RDDATA;
00351         if ( command->data_out_len )
00352                 cmnd->dirn |= FCP_CMND_WRDATA;
00353         memcpy ( &cmnd->cdb, &fcpcmd->command.cdb, sizeof ( cmnd->cdb ) );
00354         cmnd->len = htonl ( command->data_in_len + command->data_out_len );
00355         memset ( &meta, 0, sizeof ( meta ) );
00356         meta.flags = ( XFER_FL_CMD_STAT | XFER_FL_OVER );
00357         DBGC2 ( fcpdev, "FCP %p xchg %04x CMND " SCSI_CDB_FORMAT " %04x\n",
00358                 fcpdev, fcpcmd->xchg_id, SCSI_CDB_DATA ( cmnd->cdb ),
00359                 ntohl ( cmnd->len ) );
00360 
00361         /* No further data to send within this IU */
00362         fcpcmd_stop_send ( fcpcmd );
00363 
00364         /* Send command IU frame */
00365         if ( ( rc = xfer_deliver ( &fcpcmd->xchg, iob_disown ( iobuf ),
00366                                    &meta ) ) != 0 ) {
00367                 DBGC ( fcpdev, "FCP %p xchg %04x cannot deliver command IU: "
00368                        "%s\n", fcpdev, fcpcmd->xchg_id, strerror ( rc ) );
00369                 return rc;
00370         }
00371 
00372         return 0;
00373 }
00374 
00375 /**
00376  * Handle FCP read data IU
00377  *
00378  * @v fcpcmd            FCP command
00379  * @v iobuf             I/O buffer
00380  * @v meta              Data transfer metadata
00381  * @ret rc              Return status code
00382  */
00383 static int fcpcmd_recv_rddata ( struct fcp_command *fcpcmd,
00384                                 struct io_buffer *iobuf,
00385                                 struct xfer_metadata *meta ) {
00386         struct fcp_device *fcpdev = fcpcmd->fcpdev;
00387         struct scsi_cmd *command = &fcpcmd->command;
00388         size_t offset = meta->offset;
00389         size_t len = iob_len ( iobuf );
00390         int rc;
00391 
00392         /* Sanity checks */
00393         if ( ! ( meta->flags & XFER_FL_ABS_OFFSET ) ) {
00394                 DBGC ( fcpdev, "FCP %p xchg %04x read data missing offset\n",
00395                        fcpdev, fcpcmd->xchg_id );
00396                 rc = -ERANGE_READ_DATA_ORDERING;
00397                 goto done;
00398         }
00399         if ( offset != fcpcmd->offset ) {
00400                 DBGC ( fcpdev, "FCP %p xchg %04x read data out of order "
00401                        "(expected %zd, received %zd)\n",
00402                        fcpdev, fcpcmd->xchg_id, fcpcmd->offset, offset );
00403                 rc = -ERANGE_READ_DATA_ORDERING;
00404                 goto done;
00405         }
00406         if ( ( offset + len ) > command->data_in_len ) {
00407                 DBGC ( fcpdev, "FCP %p xchg %04x read data overrun (max %zd, "
00408                        "received %zd)\n", fcpdev, fcpcmd->xchg_id,
00409                        command->data_in_len, ( offset + len ) );
00410                 rc = -ERANGE_READ_DATA_OVERRUN;
00411                 goto done;
00412         }
00413         DBGC2 ( fcpdev, "FCP %p xchg %04x RDDATA [%08zx,%08zx)\n",
00414                 fcpdev, fcpcmd->xchg_id, offset, ( offset + len ) );
00415 
00416         /* Copy to user buffer */
00417         copy_to_user ( command->data_in, offset, iobuf->data, len );
00418         fcpcmd->offset += len;
00419         assert ( fcpcmd->offset <= command->data_in_len );
00420 
00421         rc = 0;
00422  done:
00423         free_iob ( iobuf );
00424         return rc;
00425 }
00426 
00427 /**
00428  * Send FCP write data IU
00429  *
00430  * @v fcpcmd            FCP command
00431  * @ret rc              Return status code
00432  */
00433 static int fcpcmd_send_wrdata ( struct fcp_command *fcpcmd ) {
00434         struct fcp_device *fcpdev = fcpcmd->fcpdev;
00435         struct scsi_cmd *command = &fcpcmd->command;
00436         struct io_buffer *iobuf;
00437         struct xfer_metadata meta;
00438         size_t len;
00439         int rc;
00440 
00441         /* Calculate length to be sent */
00442         len = xfer_window ( &fcpcmd->xchg );
00443         if ( len > fcpcmd->remaining )
00444                 len = fcpcmd->remaining;
00445 
00446         /* Sanity checks */
00447         if ( len == 0 ) {
00448                 DBGC ( fcpdev, "FCP %p xchg %04x write data stuck\n",
00449                        fcpdev, fcpcmd->xchg_id );
00450                 return -ERANGE_WRITE_DATA_STUCK;
00451         }
00452         if ( ( fcpcmd->offset + len ) > command->data_out_len ) {
00453                 DBGC ( fcpdev, "FCP %p xchg %04x write data overrun (max %zd, "
00454                        "requested %zd)\n", fcpdev, fcpcmd->xchg_id,
00455                        command->data_out_len, ( fcpcmd->offset + len ) );
00456                 return -ERANGE_WRITE_DATA_OVERRUN;
00457         }
00458 
00459         /* Allocate I/O buffer */
00460         iobuf = xfer_alloc_iob ( &fcpcmd->xchg, len );
00461         if ( ! iobuf ) {
00462                 DBGC ( fcpdev, "FCP %p xchg %04x cannot allocate write data "
00463                        "IU for %zd bytes\n", fcpdev, fcpcmd->xchg_id, len );
00464                 return -ENOMEM;
00465         }
00466 
00467         /* Construct data IU frame */
00468         copy_from_user ( iob_put ( iobuf, len ), command->data_out,
00469                          fcpcmd->offset, len );
00470         memset ( &meta, 0, sizeof ( meta ) );
00471         meta.flags = ( XFER_FL_RESPONSE | XFER_FL_ABS_OFFSET );
00472         meta.offset = fcpcmd->offset;
00473         DBGC2 ( fcpdev, "FCP %p xchg %04x WRDATA [%08zx,%04zx)\n",
00474                 fcpdev, fcpcmd->xchg_id, fcpcmd->offset,
00475                 ( fcpcmd->offset + iob_len ( iobuf ) ) );
00476 
00477         /* Calculate amount of data remaining to be sent within this IU */
00478         assert ( len <= fcpcmd->remaining );
00479         fcpcmd->offset += len;
00480         fcpcmd->remaining -= len;
00481         assert ( fcpcmd->offset <= command->data_out_len );
00482         if ( fcpcmd->remaining == 0 ) {
00483                 fcpcmd_stop_send ( fcpcmd );
00484                 meta.flags |= XFER_FL_OVER;
00485         }
00486 
00487         /* Send data IU frame */
00488         if ( ( rc = xfer_deliver ( &fcpcmd->xchg, iob_disown ( iobuf ),
00489                                    &meta ) ) != 0 ) {
00490                 DBGC ( fcpdev, "FCP %p xchg %04x cannot deliver write data "
00491                        "IU: %s\n", fcpdev, fcpcmd->xchg_id, strerror ( rc ) );
00492                 return rc;
00493         }
00494 
00495         return 0;
00496 }
00497 
00498 /**
00499  * Handle FCP transfer ready IU
00500  *
00501  * @v fcpcmd            FCP command
00502  * @v iobuf             I/O buffer
00503  * @v meta              Data transfer metadata
00504  * @ret rc              Return status code
00505  */
00506 static int fcpcmd_recv_xfer_rdy ( struct fcp_command *fcpcmd,
00507                                   struct io_buffer *iobuf,
00508                                   struct xfer_metadata *meta __unused ) {
00509         struct fcp_device *fcpdev = fcpcmd->fcpdev;
00510         struct fcp_xfer_rdy *xfer_rdy = iobuf->data;
00511         int rc;
00512 
00513         /* Sanity checks */
00514         if ( iob_len ( iobuf ) != sizeof ( *xfer_rdy ) ) {
00515                 DBGC ( fcpdev, "FCP %p xchg %04x received invalid transfer "
00516                        "ready IU:\n", fcpdev, fcpcmd->xchg_id );
00517                 DBGC_HDA ( fcpdev, 0, iobuf->data, iob_len ( iobuf ) );
00518                 rc = -EPROTO;
00519                 goto done;
00520         }
00521         if ( ntohl ( xfer_rdy->offset ) != fcpcmd->offset ) {
00522                 /* We do not advertise out-of-order delivery */
00523                 DBGC ( fcpdev, "FCP %p xchg %04x cannot support out-of-order "
00524                        "delivery (expected %zd, requested %d)\n",
00525                        fcpdev, fcpcmd->xchg_id, fcpcmd->offset,
00526                        ntohl ( xfer_rdy->offset ) );
00527                 rc = -EPROTO;
00528                 goto done;
00529         }
00530         DBGC2 ( fcpdev, "FCP %p xchg %04x XFER_RDY [%08x,%08x)\n",
00531                 fcpdev, fcpcmd->xchg_id, ntohl ( xfer_rdy->offset ),
00532                 ( ntohl ( xfer_rdy->offset ) + ntohl ( xfer_rdy->len ) ) );
00533 
00534         /* Start sending requested data */
00535         fcpcmd->remaining = ntohl ( xfer_rdy->len );
00536         fcpcmd_start_send ( fcpcmd, fcpcmd_send_wrdata );
00537 
00538         rc = 0;
00539  done:
00540         free_iob ( iobuf );
00541         return rc;
00542 }
00543 
00544 /**
00545  * Handle FCP response IU
00546  *
00547  * @v fcpcmd            FCP command
00548  * @v iobuf             I/O buffer
00549  * @v meta              Data transfer metadata
00550  * @ret rc              Return status code
00551  */
00552 static int fcpcmd_recv_rsp ( struct fcp_command *fcpcmd,
00553                              struct io_buffer *iobuf,
00554                              struct xfer_metadata *meta __unused ) {
00555         struct fcp_device *fcpdev = fcpcmd->fcpdev;
00556         struct scsi_cmd *command = &fcpcmd->command;
00557         struct fcp_rsp *rsp = iobuf->data;
00558         struct scsi_rsp response;
00559         int rc;
00560 
00561         /* Sanity check */
00562         if ( ( iob_len ( iobuf ) < sizeof ( *rsp ) ) ||
00563              ( iob_len ( iobuf ) < ( sizeof ( *rsp ) +
00564                                      fcp_rsp_response_data_len ( rsp ) +
00565                                      fcp_rsp_sense_data_len ( rsp ) ) ) ) {
00566                 DBGC ( fcpdev, "FCP %p xchg %04x received invalid response "
00567                        "IU:\n", fcpdev, fcpcmd->xchg_id );
00568                 DBGC_HDA ( fcpdev, 0, iobuf->data, iob_len ( iobuf ) );
00569                 rc = -EPROTO;
00570                 goto done;
00571         }
00572         DBGC2 ( fcpdev, "FCP %p xchg %04x RSP stat %02x resid %08x flags %02x"
00573                 "%s%s%s%s\n", fcpdev, fcpcmd->xchg_id, rsp->status,
00574                 ntohl ( rsp->residual ), rsp->flags,
00575                 ( ( rsp->flags & FCP_RSP_RESPONSE_LEN_VALID ) ? " resp" : "" ),
00576                 ( ( rsp->flags & FCP_RSP_SENSE_LEN_VALID ) ? " sense" : "" ),
00577                 ( ( rsp->flags & FCP_RSP_RESIDUAL_OVERRUN ) ? " over" : "" ),
00578                 ( ( rsp->flags & FCP_RSP_RESIDUAL_UNDERRUN ) ? " under" : "" ));
00579         if ( fcp_rsp_response_data ( rsp ) ) {
00580                 DBGC2 ( fcpdev, "FCP %p xchg %04x response data:\n",
00581                         fcpdev, fcpcmd->xchg_id );
00582                 DBGC2_HDA ( fcpdev, 0, fcp_rsp_response_data ( rsp ),
00583                             fcp_rsp_response_data_len ( rsp ) );
00584         }
00585         if ( fcp_rsp_sense_data ( rsp ) ) {
00586                 DBGC2 ( fcpdev, "FCP %p xchg %04x sense data:\n",
00587                         fcpdev, fcpcmd->xchg_id );
00588                 DBGC2_HDA ( fcpdev, 0, fcp_rsp_sense_data ( rsp ),
00589                             fcp_rsp_sense_data_len ( rsp ) );
00590         }
00591 
00592         /* Check for locally-detected command underrun */
00593         if ( ( rsp->status == 0 ) &&
00594              ( fcpcmd->offset != ( command->data_in_len +
00595                                    command->data_out_len ) ) ) {
00596                 DBGC ( fcpdev, "FCP %p xchg %04x data underrun (expected %zd, "
00597                        "got %zd)\n", fcpdev, fcpcmd->xchg_id,
00598                        ( command->data_in_len + command->data_out_len ),
00599                        fcpcmd->offset );
00600                 rc = -ERANGE_DATA_UNDERRUN;
00601                 goto done;
00602         }
00603 
00604         /* Build SCSI response */
00605         memset ( &response, 0, sizeof ( response ) );
00606         response.status = rsp->status;
00607         if ( rsp->flags & ( FCP_RSP_RESIDUAL_OVERRUN |
00608                             FCP_RSP_RESIDUAL_UNDERRUN ) ) {
00609                 response.overrun = ntohl ( rsp->residual );
00610                 if ( rsp->flags & FCP_RSP_RESIDUAL_UNDERRUN )
00611                         response.overrun = -response.overrun;
00612         }
00613         scsi_parse_sense ( fcp_rsp_sense_data ( rsp ),
00614                            fcp_rsp_sense_data_len ( rsp ), &response.sense );
00615 
00616         /* Free buffer before sending response, to minimise
00617          * out-of-memory errors.
00618          */
00619         free_iob ( iob_disown ( iobuf ) );
00620 
00621         /* Send SCSI response */
00622         scsi_response ( &fcpcmd->scsi, &response );
00623 
00624         /* Terminate command */
00625         fcpcmd_close ( fcpcmd, 0 );
00626 
00627         rc = 0;
00628  done:
00629         free_iob ( iobuf );
00630         return rc;
00631 }
00632 
00633 /**
00634  * Handle unknown FCP IU
00635  *
00636  * @v fcpcmd            FCP command
00637  * @v iobuf             I/O buffer
00638  * @v meta              Data transfer metadata
00639  * @ret rc              Return status code
00640  */
00641 static int fcpcmd_recv_unknown ( struct fcp_command *fcpcmd,
00642                                  struct io_buffer *iobuf,
00643                                  struct xfer_metadata *meta __unused ) {
00644         struct fcp_device *fcpdev = fcpcmd->fcpdev;
00645 
00646         DBGC ( fcpdev, "FCP %p xchg %04x received unknown IU:\n",
00647                fcpdev, fcpcmd->xchg_id );
00648         DBGC_HDA ( fcpdev, 0, iobuf->data, iob_len ( iobuf ) );
00649         free_iob ( iobuf );
00650         return -EPROTO;
00651 }
00652 
00653 /**
00654  * Transmit FCP frame
00655  *
00656  * @v fcpcmd            FCP command
00657  */
00658 static void fcpcmd_step ( struct fcp_command *fcpcmd ) {
00659         int rc;
00660 
00661         /* Send the current IU */
00662         if ( ( rc = fcpcmd->send ( fcpcmd ) ) != 0 ) {
00663                 /* Treat failure as a fatal error */
00664                 fcpcmd_close ( fcpcmd, rc );
00665         }
00666 }
00667 
00668 /**
00669  * Receive FCP frame
00670  *
00671  * @v fcpcmd            FCP command
00672  * @v iobuf             I/O buffer
00673  * @v meta              Data transfer metadata
00674  * @ret rc              Return status code
00675  */
00676 static int fcpcmd_deliver ( struct fcp_command *fcpcmd,
00677                             struct io_buffer *iobuf,
00678                             struct xfer_metadata *meta ) {
00679         int ( * fcpcmd_recv ) ( struct fcp_command *fcpcmd,
00680                                 struct io_buffer *iobuf,
00681                                 struct xfer_metadata *meta );
00682         int rc;
00683 
00684         /* Determine handler */
00685         switch ( meta->flags & ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ) ) {
00686         case ( XFER_FL_RESPONSE ) :
00687                 fcpcmd_recv = fcpcmd_recv_rddata;
00688                 break;
00689         case ( XFER_FL_CMD_STAT ) :
00690                 fcpcmd_recv = fcpcmd_recv_xfer_rdy;
00691                 break;
00692         case ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ) :
00693                 fcpcmd_recv = fcpcmd_recv_rsp;
00694                 break;
00695         default:
00696                 fcpcmd_recv = fcpcmd_recv_unknown;
00697                 break;
00698         }
00699 
00700         /* Handle IU */
00701         if ( ( rc = fcpcmd_recv ( fcpcmd, iob_disown ( iobuf ), meta ) ) != 0 ){
00702                 /* Treat any error as fatal to the command */
00703                 fcpcmd_close ( fcpcmd, rc );
00704         }
00705 
00706         return rc;
00707 }
00708 
00709 /** FCP command SCSI interface operations */
00710 static struct interface_operation fcpcmd_scsi_op[] = {
00711         INTF_OP ( intf_close, struct fcp_command *, fcpcmd_close ),
00712 };
00713 
00714 /** FCP command SCSI interface descriptor */
00715 static struct interface_descriptor fcpcmd_scsi_desc =
00716         INTF_DESC_PASSTHRU ( struct fcp_command, scsi, fcpcmd_scsi_op, xchg );
00717 
00718 /** FCP command Fibre Channel exchange interface operations */
00719 static struct interface_operation fcpcmd_xchg_op[] = {
00720         INTF_OP ( xfer_deliver, struct fcp_command *, fcpcmd_deliver ),
00721         INTF_OP ( intf_close, struct fcp_command *, fcpcmd_close_err ),
00722 };
00723 
00724 /** FCP command Fibre Channel exchange interface descriptor */
00725 static struct interface_descriptor fcpcmd_xchg_desc =
00726         INTF_DESC_PASSTHRU ( struct fcp_command, xchg, fcpcmd_xchg_op, scsi );
00727 
00728 /** FCP command process descriptor */
00729 static struct process_descriptor fcpcmd_process_desc =
00730         PROC_DESC ( struct fcp_command, process, fcpcmd_step );
00731 
00732 /**
00733  * Issue FCP SCSI command
00734  *
00735  * @v fcpdev            FCP device
00736  * @v parent            Parent interface
00737  * @v command           SCSI command
00738  * @ret tag             Command tag, or negative error
00739  */
00740 static int fcpdev_scsi_command ( struct fcp_device *fcpdev,
00741                                  struct interface *parent,
00742                                  struct scsi_cmd *command ) {
00743         struct fcp_prli_service_parameters *param = fcpdev->user.ulp->param;
00744         struct fcp_command *fcpcmd;
00745         int xchg_id;
00746         int rc;
00747 
00748         /* Check link */
00749         if ( ( rc = fcpdev->user.ulp->link.rc ) != 0 ) {
00750                 DBGC ( fcpdev, "FCP %p could not issue command while link is "
00751                        "down: %s\n", fcpdev, strerror ( rc ) );
00752                 goto err_link;
00753         }
00754 
00755         /* Check target capability */
00756         assert ( param != NULL );
00757         assert ( fcpdev->user.ulp->param_len >= sizeof ( *param ) );
00758         if ( ! ( param->flags & htonl ( FCP_PRLI_TARGET ) ) ) {
00759                 DBGC ( fcpdev, "FCP %p could not issue command: not a target\n",
00760                        fcpdev );
00761                 rc = -ENOTTY;
00762                 goto err_target;
00763         }
00764 
00765         /* Allocate and initialise structure */
00766         fcpcmd = zalloc ( sizeof ( *fcpcmd ) );
00767         if ( ! fcpcmd ) {
00768                 rc = -ENOMEM;
00769                 goto err_zalloc;
00770         }
00771         ref_init ( &fcpcmd->refcnt, fcpcmd_free );
00772         intf_init ( &fcpcmd->scsi, &fcpcmd_scsi_desc, &fcpcmd->refcnt );
00773         intf_init ( &fcpcmd->xchg, &fcpcmd_xchg_desc, &fcpcmd->refcnt );
00774         process_init_stopped ( &fcpcmd->process, &fcpcmd_process_desc,
00775                                &fcpcmd->refcnt );
00776         fcpcmd->fcpdev = fcpdev_get ( fcpdev );
00777         list_add ( &fcpcmd->list, &fcpdev->fcpcmds );
00778         memcpy ( &fcpcmd->command, command, sizeof ( fcpcmd->command ) );
00779 
00780         /* Create new exchange */
00781         if ( ( xchg_id = fc_xchg_originate ( &fcpcmd->xchg,
00782                                              fcpdev->user.ulp->peer->port,
00783                                              &fcpdev->user.ulp->peer->port_id,
00784                                              FC_TYPE_FCP ) ) < 0 ) {
00785                 rc = xchg_id;
00786                 DBGC ( fcpdev, "FCP %p could not create exchange: %s\n",
00787                        fcpdev, strerror ( rc ) );
00788                 goto err_xchg_originate;
00789         }
00790         fcpcmd->xchg_id = xchg_id;
00791 
00792         /* Start sending command IU */
00793         fcpcmd_start_send ( fcpcmd, fcpcmd_send_cmnd );
00794 
00795         /* Attach to parent interface, mortalise self, and return */
00796         intf_plug_plug ( &fcpcmd->scsi, parent );
00797         ref_put ( &fcpcmd->refcnt );
00798         return ( FCP_TAG_MAGIC | fcpcmd->xchg_id );
00799 
00800  err_xchg_originate:
00801         fcpcmd_close ( fcpcmd, rc );
00802         ref_put ( &fcpcmd->refcnt );
00803  err_zalloc:
00804  err_target:
00805  err_link:
00806         return rc;
00807 }
00808 
00809 /**
00810  * Close FCP device
00811  *
00812  * @v fcpdev            FCP device
00813  * @v rc                Reason for close
00814  */
00815 static void fcpdev_close ( struct fcp_device *fcpdev, int rc ) {
00816         struct fcp_command *fcpcmd;
00817         struct fcp_command *tmp;
00818 
00819         DBGC ( fcpdev, "FCP %p closed: %s\n", fcpdev, strerror ( rc ) );
00820 
00821         /* Shut down interfaces */
00822         intf_shutdown ( &fcpdev->scsi, rc );
00823 
00824         /* Shut down any active commands */
00825         list_for_each_entry_safe ( fcpcmd, tmp, &fcpdev->fcpcmds, list ) {
00826                 fcpcmd_get ( fcpcmd );
00827                 fcpcmd_close ( fcpcmd, rc );
00828                 fcpcmd_put ( fcpcmd );
00829         }
00830 
00831         /* Drop reference to ULP */
00832         fc_ulp_detach ( &fcpdev->user );
00833 }
00834 
00835 /**
00836  * Check FCP device flow-control window
00837  *
00838  * @v fcpdev            FCP device
00839  * @ret len             Length of window
00840  */
00841 static size_t fcpdev_window ( struct fcp_device *fcpdev ) {
00842         return ( fc_link_ok ( &fcpdev->user.ulp->link ) ?
00843                  ~( ( size_t ) 0 ) : 0 );
00844 }
00845 
00846 /**
00847  * Describe FCP device using EDD
00848  *
00849  * @v fcpdev            FCP device
00850  * @v type              EDD interface type
00851  * @v path              EDD device path
00852  * @ret rc              Return status code
00853  */
00854 static int fcpdev_edd_describe ( struct fcp_device *fcpdev,
00855                                  struct edd_interface_type *type,
00856                                  union edd_device_path *path ) {
00857         union {
00858                 struct fc_name fc;
00859                 uint64_t u64;
00860         } wwn;
00861         union {
00862                 struct scsi_lun scsi;
00863                 uint64_t u64;
00864         } lun;
00865 
00866         type->type = cpu_to_le64 ( EDD_INTF_TYPE_FIBRE );
00867         memcpy ( &wwn.fc, &fcpdev->wwn, sizeof ( wwn.fc ) );
00868         path->fibre.wwn = be64_to_cpu ( wwn.u64 );
00869         memcpy ( &lun.scsi, &fcpdev->lun, sizeof ( lun.scsi ) );
00870         path->fibre.lun = be64_to_cpu ( lun.u64 );
00871         return 0;
00872 }
00873 
00874 /**
00875  * Identify device underlying FCP device
00876  *
00877  * @v fcpdev            FCP device
00878  * @ret device          Underlying device
00879  */
00880 static struct device * fcpdev_identify_device ( struct fcp_device *fcpdev ) {
00881 
00882         /* We know the underlying device only if the link is up;
00883          * otherwise we don't have a port to examine.
00884          */
00885         if ( ! fc_link_ok ( &fcpdev->user.ulp->link ) ) {
00886                 DBGC ( fcpdev, "FCP %p doesn't know underlying device "
00887                        "until link is up\n", fcpdev );
00888                 return NULL;
00889         }
00890 
00891         /* Hand off to port's transport interface */
00892         assert ( fcpdev->user.ulp->peer->port != NULL );
00893         return identify_device ( &fcpdev->user.ulp->peer->port->transport );
00894 }
00895 
00896 /** FCP device SCSI interface operations */
00897 static struct interface_operation fcpdev_scsi_op[] = {
00898         INTF_OP ( scsi_command, struct fcp_device *, fcpdev_scsi_command ),
00899         INTF_OP ( xfer_window, struct fcp_device *, fcpdev_window ),
00900         INTF_OP ( intf_close, struct fcp_device *, fcpdev_close ),
00901         INTF_OP ( edd_describe, struct fcp_device *, fcpdev_edd_describe ),
00902         INTF_OP ( identify_device, struct fcp_device *,
00903                   fcpdev_identify_device ),
00904 };
00905 
00906 /** FCP device SCSI interface descriptor */
00907 static struct interface_descriptor fcpdev_scsi_desc =
00908         INTF_DESC ( struct fcp_device, scsi, fcpdev_scsi_op );
00909 
00910 /**
00911  * Examine FCP ULP link state
00912  *
00913  * @v user              Fibre Channel upper-layer protocol user
00914  */
00915 static void fcpdev_examine ( struct fc_ulp_user *user ) {
00916         struct fcp_device *fcpdev =
00917                 container_of ( user, struct fcp_device, user );
00918 
00919         if ( fc_link_ok ( &fcpdev->user.ulp->link ) ) {
00920                 DBGC ( fcpdev, "FCP %p link is up\n", fcpdev );
00921         } else {
00922                 DBGC ( fcpdev, "FCP %p link is down: %s\n",
00923                        fcpdev, strerror ( fcpdev->user.ulp->link.rc ) );
00924         }
00925 
00926         /* Notify SCSI layer of window change */
00927         xfer_window_changed ( &fcpdev->scsi );
00928 }
00929 
00930 /**
00931  * Open FCP device
00932  *
00933  * @v parent            Parent interface
00934  * @v wwn               Fibre Channel WWN
00935  * @v lun               SCSI LUN
00936  * @ret rc              Return status code
00937  */
00938 static int fcpdev_open ( struct interface *parent, struct fc_name *wwn,
00939                          struct scsi_lun *lun ) {
00940         struct fc_ulp *ulp;
00941         struct fcp_device *fcpdev;
00942         int rc;
00943 
00944         /* Get Fibre Channel ULP interface */
00945         ulp = fc_ulp_get_wwn_type ( wwn, FC_TYPE_FCP );
00946         if ( ! ulp ) {
00947                 rc = -ENOMEM;
00948                 goto err_ulp_get;
00949         }
00950 
00951         /* Allocate and initialise structure */
00952         fcpdev = zalloc ( sizeof ( *fcpdev ) );
00953         if ( ! fcpdev ) {
00954                 rc = -ENOMEM;
00955                 goto err_zalloc;
00956         }
00957         ref_init ( &fcpdev->refcnt, NULL );
00958         intf_init ( &fcpdev->scsi, &fcpdev_scsi_desc, &fcpdev->refcnt );
00959         INIT_LIST_HEAD ( &fcpdev->fcpcmds );
00960         fc_ulp_user_init ( &fcpdev->user, fcpdev_examine, &fcpdev->refcnt );
00961 
00962         DBGC ( fcpdev, "FCP %p opened for %s\n", fcpdev, fc_ntoa ( wwn ) );
00963 
00964         /* Attach to Fibre Channel ULP */
00965         fc_ulp_attach ( ulp, &fcpdev->user );
00966 
00967         /* Preserve parameters required for boot firmware table */
00968         memcpy ( &fcpdev->wwn, wwn, sizeof ( fcpdev->wwn ) );
00969         memcpy ( &fcpdev->lun, lun, sizeof ( fcpdev->lun ) );
00970 
00971         /* Attach SCSI device to parent interface */
00972         if ( ( rc = scsi_open ( parent, &fcpdev->scsi, lun ) ) != 0 ) {
00973                 DBGC ( fcpdev, "FCP %p could not create SCSI device: %s\n",
00974                        fcpdev, strerror ( rc ) );
00975                 goto err_scsi_open;
00976         }
00977 
00978         /* Drop temporary reference to ULP */
00979         fc_ulp_put ( ulp );
00980 
00981         /* Mortalise self and return */
00982         ref_put ( &fcpdev->refcnt );
00983         return 0;
00984 
00985  err_scsi_open:
00986         fcpdev_close ( fcpdev, rc );
00987         ref_put ( &fcpdev->refcnt );
00988  err_zalloc:
00989         fc_ulp_put ( ulp );
00990  err_ulp_get:
00991         return rc;
00992 }
00993 
00994 /******************************************************************************
00995  *
00996  * FCP URIs
00997  *
00998  ******************************************************************************
00999  */
01000 
01001 /**
01002  * Parse FCP URI
01003  *
01004  * @v uri               URI
01005  * @ret wwn             Fibre Channel WWN
01006  * @ret lun             SCSI LUN
01007  * @ret rc              Return status code
01008  *
01009  * An FCP URI has the form "fcp:<wwn>:<lun>" or "fcp://<wwn>/<lun>"
01010  */
01011 static int fcp_parse_uri ( struct uri *uri, struct fc_name *wwn,
01012                            struct scsi_lun *lun ) {
01013         char wwn_buf[ FC_NAME_STRLEN + 1 /* NUL */ ];
01014         const char *wwn_text;
01015         const char *lun_text;
01016         int rc;
01017 
01018         /* Extract WWN and LUN texts from URI */
01019         if ( uri->opaque ) {
01020                 /* "fcp:<wwn>:<lun>" */
01021                 if ( snprintf ( wwn_buf, sizeof ( wwn_buf ), "%s",
01022                                 uri->opaque ) < ( FC_NAME_STRLEN + 1 /* : */ ) )
01023                         return -EINVAL;
01024                 if ( uri->opaque[FC_NAME_STRLEN] != ':' )
01025                         return -EINVAL;
01026                 wwn_text = wwn_buf;
01027                 lun_text = &uri->opaque[FC_NAME_STRLEN + 1];
01028         } else {
01029                 /* If host exists, path must also exist */
01030                 if ( ! ( uri->host && uri->path ) )
01031                         return -EINVAL;
01032                 if ( uri->path[0] != '/' )
01033                         return -EINVAL;
01034                 wwn_text = uri->host;
01035                 lun_text = ( uri->path + 1 );
01036         }
01037 
01038         /* Parse WWN */
01039         if ( ( rc = fc_aton ( wwn_text, wwn ) ) != 0 )
01040                 return rc;
01041 
01042         /* Parse LUN */
01043         if ( ( rc = scsi_parse_lun ( lun_text, lun ) ) != 0 )
01044                 return rc;
01045 
01046         return 0;
01047 }
01048 
01049 /**
01050  * Open FCP URI
01051  *
01052  * @v parent            Parent interface
01053  * @v uri               URI
01054  * @ret rc              Return status code
01055  */
01056 static int fcp_open ( struct interface *parent, struct uri *uri ) {
01057         struct fc_name wwn;
01058         struct scsi_lun lun;
01059         int rc;
01060 
01061         /* Parse URI */
01062         if ( ( rc = fcp_parse_uri ( uri, &wwn, &lun ) ) != 0 )
01063                 return rc;
01064 
01065         /* Open FCP device */
01066         if ( ( rc = fcpdev_open ( parent, &wwn, &lun ) ) != 0 )
01067                 return rc;
01068 
01069         return 0;
01070 }
01071 
01072 /** FCP URI opener */
01073 struct uri_opener fcp_uri_opener __uri_opener = {
01074         .scheme = "fcp",
01075         .open = fcp_open,
01076 };