1/** @file
2  The implementation of EFI_EXT_SCSI_PASS_THRU_PROTOCOL.
3
4Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution.  The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13**/
14
15#include "IScsiImpl.h"
16
17EFI_EXT_SCSI_PASS_THRU_PROTOCOL gIScsiExtScsiPassThruProtocolTemplate = {
18  NULL,
19  IScsiExtScsiPassThruFunction,
20  IScsiExtScsiPassThruGetNextTargetLun,
21  IScsiExtScsiPassThruBuildDevicePath,
22  IScsiExtScsiPassThruGetTargetLun,
23  IScsiExtScsiPassThruResetChannel,
24  IScsiExtScsiPassThruResetTargetLun,
25  IScsiExtScsiPassThruGetNextTarget
26};
27
28
29/**
30  Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel.
31  This function supports both blocking I/O and nonblocking I/O. The blocking I/O
32  functionality is required, and the nonblocking I/O functionality is optional.
33
34  @param[in]       This    A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
35  @param[in]       Target  The Target is an array of size TARGET_MAX_BYTES and it
36                           represents the id of the SCSI device to send the SCSI
37                           Request Packet. Each transport driver may choose to
38                           utilize a subset of this size to suit the needs
39                           of transport target representation. For example, a
40                           Fibre Channel driver may use only 8 bytes (WWN)
41                           to represent an FC target.
42  @param[in]       Lun     The LUN of the SCSI device to send the SCSI Request Packet.
43  @param[in, out]  Packet  A pointer to the SCSI Request Packet to send to the
44                           SCSI device specified by Target and Lun.
45  @param[in]       Event   If nonblocking I/O is not supported then Event is ignored,
46                           and blocking I/O is performed. If Event is NULL, then
47                           blocking I/O is performed. If Event is not NULL and non
48                           blocking I/O is supported, then nonblocking I/O is performed,
49                           and Event will be signaled when the SCSI Request Packet
50                           completes.
51
52  @retval EFI_SUCCESS           The SCSI Request Packet was sent by the host. For
53                                bi-directional commands, InTransferLength bytes
54                                were transferred from InDataBuffer.
55                                For write and bi-directional commands, OutTransferLength
56                                bytes were transferred by OutDataBuffer.
57  @retval EFI_BAD_BUFFER_SIZE   The SCSI Request Packet was not executed.
58                                The number of bytes that could be transferred is
59                                returned in InTransferLength. For write and
60                                bi-directional commands, OutTransferLength bytes
61                                were transferred by OutDataBuffer.
62  @retval EFI_NOT_READY         The SCSI Request Packet could not be sent because
63                                there are too many SCSI Request Packets already
64                                queued. The caller may retry later.
65  @retval EFI_DEVICE_ERROR      A device error occurred while attempting to send
66                                the SCSI Request Packet.
67  @retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket,
68                                are invalid.
69  @retval EFI_UNSUPPORTED       The command described by the SCSI Request Packet
70                                is not supported by the host adapter.
71                                This includes the case of Bi-directional SCSI
72                                commands not supported by the implementation.
73                                The SCSI Request Packet was not sent,
74                                so no additional status information is available.
75  @retval EFI_TIMEOUT           A timeout occurred while waiting for the SCSI
76                                Request Packet to execute.
77
78**/
79EFI_STATUS
80EFIAPI
81IScsiExtScsiPassThruFunction (
82  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                          *This,
83  IN UINT8                                                    *Target,
84  IN UINT64                                                   Lun,
85  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET           *Packet,
86  IN EFI_EVENT                                                Event     OPTIONAL
87  )
88{
89  EFI_STATUS         Status;
90  ISCSI_DRIVER_DATA  *Private;
91
92  if (Target[0] != 0) {
93    return EFI_INVALID_PARAMETER;
94  }
95
96  if ((Packet == NULL) || (Packet->Cdb == NULL)) {
97    return EFI_INVALID_PARAMETER;
98  }
99
100  Status = IScsiExecuteScsiCommand (This, Target, Lun, Packet);
101  if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {
102    //
103    // Try to reinstate the session and re-execute the Scsi command.
104    //
105    Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);
106    if (EFI_ERROR (IScsiSessionReinstatement (Private->Session))) {
107      return EFI_DEVICE_ERROR;
108    }
109
110    Status = IScsiExecuteScsiCommand (This, Target, Lun, Packet);
111  }
112
113  return Status;
114}
115
116
117/**
118  Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on
119  a SCSI channel. These can either be the list SCSI devices that are actually
120  present on the SCSI channel, or the list of legal Target Ids and LUNs for the
121  SCSI channel. Regardless, the caller of this function must probe the Target ID
122  and LUN returned to see if a SCSI device is actually present at that location
123  on the SCSI channel.
124
125  @param[in]       This          The EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
126  @param[in, out]  Target        On input, a pointer to the Target ID of a SCSI
127                                 device present on the SCSI channel.  On output, a
128                                 pointer to the Target ID of the next SCSI device
129                                 present on a SCSI channel.  An input value of
130                                 0xFFFFFFFF retrieves the Target ID of the first
131                                 SCSI device present on a SCSI channel.
132  @param[in, out]  Lun           On input, a pointer to the LUN of a SCSI device
133                                 present on the SCSI channel. On output, a pointer
134                                 to the LUN of the next SCSI device present on a
135                                 SCSI channel.
136
137  @retval EFI_SUCCESS            The Target ID and Lun of the next SCSI device on
138                                 the SCSI channel was returned in Target and Lun.
139  @retval EFI_NOT_FOUND          There are no more SCSI devices on this SCSI
140                                 channel.
141  @retval EFI_INVALID_PARAMETER  Target is not 0xFFFFFFFF,and Target and Lun were
142                                 not returned on a previous call to
143                                 GetNextDevice().
144
145**/
146EFI_STATUS
147EFIAPI
148IScsiExtScsiPassThruGetNextTargetLun (
149  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,
150  IN OUT UINT8                        **Target,
151  IN OUT UINT64                       *Lun
152  )
153{
154  ISCSI_DRIVER_DATA           *Private;
155  ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
156  UINT8                       TargetId[TARGET_MAX_BYTES];
157
158  Private       = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);
159  ConfigNvData  = &Private->Session->ConfigData->SessionConfigData;
160
161  if ((*Target)[0] == 0 && (CompareMem (Lun, ConfigNvData->BootLun, sizeof (UINT64)) == 0)) {
162    //
163    // Only one <Target, Lun> pair per iSCSI Driver instance.
164    //
165    return EFI_NOT_FOUND;
166  }
167
168  SetMem (TargetId, TARGET_MAX_BYTES, 0xFF);
169  if (CompareMem (*Target, TargetId, TARGET_MAX_BYTES) == 0) {
170    (*Target)[0] = 0;
171    CopyMem (Lun, ConfigNvData->BootLun, sizeof (UINT64));
172
173    return EFI_SUCCESS;
174  }
175
176  return EFI_INVALID_PARAMETER;
177}
178
179
180/**
181  Allocate and build a device path node for a SCSI device on a SCSI channel.
182
183  @param[in]       This          Protocol instance pointer.
184  @param[in]       Target        The Target ID of the SCSI device for which a
185                                 device path node is to be allocated and built.
186  @param[in]       Lun           The LUN of the SCSI device for which a device
187                                 path node is to be allocated and built.
188  @param[in, out]  DevicePath    A pointer to a single device path node that
189                                 describes the SCSI device specified by  Target and
190                                 Lun. This function is responsible  for allocating
191                                 the buffer DevicePath with the boot service
192                                 AllocatePool().  It is the caller's
193                                 responsibility to free DevicePath when the caller
194                                 is finished with DevicePath.
195
196  @retval EFI_SUCCESS            The device path node that describes the SCSI
197                                 device specified by Target and Lun was allocated
198                                 and  returned in DevicePath.
199  @retval EFI_NOT_FOUND          The SCSI devices specified by Target and Lun does
200                                 not exist on the SCSI channel.
201  @retval EFI_INVALID_PARAMETER  DevicePath is NULL.
202  @retval EFI_OUT_OF_RESOURCES   There are not enough resources to allocate
203                                 DevicePath.
204
205**/
206EFI_STATUS
207EFIAPI
208IScsiExtScsiPassThruBuildDevicePath (
209  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,
210  IN UINT8                            *Target,
211  IN UINT64                           Lun,
212  IN OUT EFI_DEVICE_PATH_PROTOCOL     **DevicePath
213  )
214{
215  ISCSI_DRIVER_DATA             *Private;
216  ISCSI_SESSION                 *Session;
217  ISCSI_SESSION_CONFIG_NVDATA   *ConfigNvData;
218  ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfig;
219  EFI_DEV_PATH                  *Node;
220  UINTN                         DevPathNodeLen;
221
222  if ((DevicePath == NULL)) {
223    return EFI_INVALID_PARAMETER;
224  }
225
226  if (Target[0] != 0) {
227    return EFI_NOT_FOUND;
228  }
229
230  Private       = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);
231  Session       = Private->Session;
232  ConfigNvData  = &Session->ConfigData->SessionConfigData;
233  AuthConfig    = Session->AuthData.CHAP.AuthConfig;
234
235  if (CompareMem (&Lun, ConfigNvData->BootLun, sizeof (UINT64)) != 0) {
236    return EFI_NOT_FOUND;
237  }
238
239  DevPathNodeLen  = sizeof (ISCSI_DEVICE_PATH) + AsciiStrLen (ConfigNvData->TargetName) + 1;
240  Node            = AllocateZeroPool (DevPathNodeLen);
241  if (Node == NULL) {
242    return EFI_OUT_OF_RESOURCES;
243  }
244
245  Node->DevPath.Type    = MESSAGING_DEVICE_PATH;
246  Node->DevPath.SubType = MSG_ISCSI_DP;
247  SetDevicePathNodeLength (&Node->DevPath, DevPathNodeLen);
248
249  //
250  // 0 for TCP, others are reserved.
251  //
252  Node->Iscsi.NetworkProtocol = 0;
253
254  Node->Iscsi.LoginOption     = 0;
255
256  switch (Session->AuthType) {
257  case ISCSI_AUTH_TYPE_NONE:
258    Node->Iscsi.LoginOption |= 0x0800;
259    break;
260
261  case ISCSI_AUTH_TYPE_CHAP:
262    //
263    // Bit12: 0=CHAP_BI, 1=CHAP_UNI
264    //
265    if (AuthConfig->CHAPType == ISCSI_CHAP_UNI) {
266      Node->Iscsi.LoginOption |= 0x1000;
267    }
268    break;
269
270  default:
271    break;
272  }
273
274  CopyMem (&Node->Iscsi.Lun, ConfigNvData->BootLun, sizeof (UINT64));
275  Node->Iscsi.TargetPortalGroupTag = Session->TargetPortalGroupTag;
276  AsciiStrCpyS ((CHAR8 *) Node + sizeof (ISCSI_DEVICE_PATH), AsciiStrLen (ConfigNvData->TargetName) + 1, ConfigNvData->TargetName);
277
278  *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Node;
279
280  return EFI_SUCCESS;
281}
282
283
284/**
285  Translate a device path node to a Target ID and LUN.
286
287  @param[in]   This              Protocol instance pointer.
288  @param[in]   DevicePath        A pointer to the device path node that  describes
289                                 a SCSI device on the SCSI channel.
290  @param[out]  Target            A pointer to the Target ID of a SCSI device on
291                                 the SCSI channel.
292  @param[out]  Lun               A pointer to the LUN of a SCSI device on the SCSI
293                                 channel.
294
295  @retval EFI_SUCCESS            DevicePath was successfully translated to a
296                                 Target ID and LUN, and they were returned  in
297                                 Target and Lun.
298  @retval EFI_INVALID_PARAMETER  DevicePath/Target/Lun is NULL.
299  @retval EFI_UNSUPPORTED        This driver does not support the device path  node
300                                 type in DevicePath.
301  @retval EFI_NOT_FOUND          A valid translation does not exist from DevicePath
302                                 to a TargetID and LUN.
303
304**/
305EFI_STATUS
306EFIAPI
307IScsiExtScsiPassThruGetTargetLun (
308  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,
309  IN EFI_DEVICE_PATH_PROTOCOL         *DevicePath,
310  OUT UINT8                           **Target,
311  OUT UINT64                          *Lun
312  )
313{
314  ISCSI_DRIVER_DATA           *Private;
315  ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
316
317  if ((DevicePath == NULL) || (Target == NULL) || (Lun == NULL)) {
318    return EFI_INVALID_PARAMETER;
319  }
320
321  if ((DevicePath->Type != MESSAGING_DEVICE_PATH) ||
322      (DevicePath->SubType != MSG_ISCSI_DP) ||
323      (DevicePathNodeLength (DevicePath) <= sizeof (ISCSI_DEVICE_PATH))
324      ) {
325    return EFI_UNSUPPORTED;
326  }
327
328  Private       = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);
329  ConfigNvData  = &Private->Session->ConfigData->SessionConfigData;
330
331  SetMem (*Target, TARGET_MAX_BYTES, 0xFF);
332  (*Target)[0] = 0;
333
334  if (AsciiStrCmp (ConfigNvData->TargetName, (CHAR8 *) DevicePath + sizeof (ISCSI_DEVICE_PATH)) != 0) {
335    return EFI_UNSUPPORTED;
336  }
337
338  CopyMem (Lun, ConfigNvData->BootLun, sizeof (UINT64));
339
340  return EFI_SUCCESS;
341}
342
343
344/**
345  Resets a SCSI channel. This operation resets all the SCSI devices connected to
346  the SCSI channel.
347
348  @param[in]  This               Protocol instance pointer.
349
350  @retval EFI_UNSUPPORTED        It is not supported.
351
352**/
353EFI_STATUS
354EFIAPI
355IScsiExtScsiPassThruResetChannel (
356  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This
357  )
358{
359  return EFI_UNSUPPORTED;
360}
361
362
363/**
364  Resets a SCSI device that is connected to a SCSI channel.
365
366  @param[in]  This               Protocol instance pointer.
367  @param[in]  Target             The Target ID of the SCSI device to reset.
368  @param[in]  Lun                The LUN of the SCSI device to reset.
369
370  @retval EFI_UNSUPPORTED        It is not supported.
371
372**/
373EFI_STATUS
374EFIAPI
375IScsiExtScsiPassThruResetTargetLun (
376  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,
377  IN UINT8                            *Target,
378  IN UINT64                           Lun
379  )
380{
381  return EFI_UNSUPPORTED;
382}
383
384/**
385  Retrieve the list of legal Target IDs for SCSI devices on a SCSI channel.
386
387  @param[in]       This         A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL
388                                instance.
389  @param[in, out]  Target       (TARGET_MAX_BYTES) of a SCSI device present on
390                                the SCSI channel. On output, a pointer to the
391                                Target ID (an array of TARGET_MAX_BYTES) of the
392                                next SCSI device present on a SCSI channel.
393                                An input value of 0xF(all bytes in the array are 0xF)
394                                in the Target array retrieves the Target ID of the
395                                first SCSI device present on a SCSI channel.
396
397  @retval EFI_SUCCESS           The Target ID of the next SCSI device on the SCSI
398                                channel was returned in Target.
399  @retval EFI_INVALID_PARAMETER Target or Lun is NULL.
400  @retval EFI_TIMEOUT           Target array is not all 0xF, and Target was not
401                                returned on a previous call to GetNextTarget().
402  @retval EFI_NOT_FOUND         There are no more SCSI devices on this SCSI channel.
403
404**/
405EFI_STATUS
406EFIAPI
407IScsiExtScsiPassThruGetNextTarget (
408  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,
409  IN OUT UINT8                        **Target
410  )
411{
412  UINT8 TargetId[TARGET_MAX_BYTES];
413
414  SetMem (TargetId, TARGET_MAX_BYTES, 0xFF);
415
416  if (CompareMem (*Target, TargetId, TARGET_MAX_BYTES) == 0) {
417    (*Target)[0] = 0;
418    return EFI_SUCCESS;
419  } else if ((*Target)[0] == 0) {
420    return EFI_NOT_FOUND;
421  } else {
422    return EFI_INVALID_PARAMETER;
423  }
424}
425
426