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