IScsiMisc.c revision c0d494b5a7e14f007a425c7c18f24a739d596bce
1/** @file
2  Miscellaneous routines for iSCSI driver.
3
4Copyright (c) 2004 - 2011, 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
17GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8  IScsiHexString[] = "0123456789ABCDEFabcdef";
18
19/**
20  Removes (trims) specified leading and trailing characters from a string.
21
22  @param[in, out] Str   Pointer to the null-terminated string to be trimmed.
23                        On return, Str will hold the trimmed string.
24
25  @param[in]      CharC Character will be trimmed from str.
26
27**/
28VOID
29IScsiStrTrim (
30  IN OUT CHAR16   *Str,
31  IN     CHAR16   CharC
32  )
33{
34  CHAR16  *Pointer1;
35  CHAR16  *Pointer2;
36
37  if (*Str == 0) {
38    return ;
39  }
40
41  //
42  // Trim off the leading and trailing characters c
43  //
44  for (Pointer1 = Str; (*Pointer1 != 0) && (*Pointer1 == CharC); Pointer1++) {
45    ;
46  }
47
48  Pointer2 = Str;
49  if (Pointer2 == Pointer1) {
50    while (*Pointer1 != 0) {
51      Pointer2++;
52      Pointer1++;
53    }
54  } else {
55    while (*Pointer1 != 0) {
56    *Pointer2 = *Pointer1;
57    Pointer1++;
58    Pointer2++;
59    }
60    *Pointer2 = 0;
61  }
62
63
64  for (Pointer1 = Str + StrLen(Str) - 1; Pointer1 >= Str && *Pointer1 == CharC; Pointer1--) {
65    ;
66  }
67  if  (Pointer1 !=  Str + StrLen(Str) - 1) {
68    *(Pointer1 + 1) = 0;
69  }
70}
71
72/**
73  Calculate the prefix length of the IPv4 subnet mask.
74
75  @param[in]  SubnetMask The IPv4 subnet mask.
76
77  @return     The prefix length of the subnet mask.
78  @retval 0   Other errors as indicated.
79
80**/
81UINT8
82IScsiGetSubnetMaskPrefixLength (
83  IN EFI_IPv4_ADDRESS  *SubnetMask
84  )
85{
86  UINT8   Len;
87  UINT32  ReverseMask;
88
89  //
90  // The SubnetMask is in network byte order.
91  //
92  ReverseMask = (SubnetMask->Addr[0] << 24) | (SubnetMask->Addr[1] << 16) | (SubnetMask->Addr[2] << 8) | (SubnetMask->Addr[3]);
93
94  //
95  // Reverse it.
96  //
97  ReverseMask = ~ReverseMask;
98
99  if ((ReverseMask & (ReverseMask + 1)) != 0) {
100    return 0;
101  }
102
103  Len = 0;
104
105  while (ReverseMask != 0) {
106    ReverseMask = ReverseMask >> 1;
107    Len++;
108  }
109
110  return (UINT8) (32 - Len);
111}
112
113
114/**
115  Convert the hexadecimal encoded LUN string into the 64-bit LUN.
116
117  @param[in]   Str             The hexadecimal encoded LUN string.
118  @param[out]  Lun             Storage to return the 64-bit LUN.
119
120  @retval EFI_SUCCESS            The 64-bit LUN is stored in Lun.
121  @retval EFI_INVALID_PARAMETER  The string is malformatted.
122
123**/
124EFI_STATUS
125IScsiAsciiStrToLun (
126  IN  CHAR8  *Str,
127  OUT UINT8  *Lun
128  )
129{
130  UINTN   Index, IndexValue, IndexNum, SizeStr;
131  CHAR8   TemStr[2];
132  UINT8   TemValue;
133  UINT16  Value[4];
134
135  ZeroMem (Lun, 8);
136  ZeroMem (TemStr, 2);
137  ZeroMem ((UINT8 *) Value, sizeof (Value));
138  SizeStr    = AsciiStrLen (Str);
139  IndexValue = 0;
140  IndexNum   = 0;
141
142  for (Index = 0; Index < SizeStr; Index ++) {
143    TemStr[0] = Str[Index];
144    TemValue = (UINT8) AsciiStrHexToUint64 (TemStr);
145    if (TemValue == 0 && TemStr[0] != '0') {
146      if ((TemStr[0] != '-') || (IndexNum == 0)) {
147        //
148        // Invalid Lun Char.
149        //
150        return EFI_INVALID_PARAMETER;
151      }
152    }
153
154    if ((TemValue == 0) && (TemStr[0] == '-')) {
155      //
156      // Next Lun value.
157      //
158      if (++IndexValue >= 4) {
159        //
160        // Max 4 Lun value.
161        //
162        return EFI_INVALID_PARAMETER;
163      }
164      //
165      // Restart str index for the next lun value.
166      //
167      IndexNum = 0;
168      continue;
169    }
170
171    if (++IndexNum > 4) {
172      //
173      // Each Lun Str can't exceed size 4, because it will be as UINT16 value.
174      //
175      return EFI_INVALID_PARAMETER;
176    }
177
178    //
179    // Combine UINT16 value.
180    //
181    Value[IndexValue] = (UINT16) ((Value[IndexValue] << 4) + TemValue);
182  }
183
184  for (Index = 0; Index <= IndexValue; Index ++) {
185    *((UINT16 *) &Lun[Index * 2]) =  HTONS (Value[Index]);
186  }
187
188  return EFI_SUCCESS;
189}
190
191/**
192  Convert the 64-bit LUN into the hexadecimal encoded LUN string.
193
194  @param[in]   Lun The 64-bit LUN.
195  @param[out]  Str The storage to return the hexadecimal encoded LUN string.
196
197**/
198VOID
199IScsiLunToUnicodeStr (
200  IN UINT8    *Lun,
201  OUT CHAR16  *Str
202  )
203{
204  UINTN   Index;
205  CHAR16  *TempStr;
206
207  TempStr = Str;
208
209  for (Index = 0; Index < 4; Index++) {
210
211    if ((Lun[2 * Index] | Lun[2 * Index + 1]) == 0) {
212      StrCpy (TempStr, L"0-");
213    } else {
214      TempStr[0]  = (CHAR16) IScsiHexString[Lun[2 * Index] >> 4];
215      TempStr[1]  = (CHAR16) IScsiHexString[Lun[2 * Index] & 0x0F];
216      TempStr[2]  = (CHAR16) IScsiHexString[Lun[2 * Index + 1] >> 4];
217      TempStr[3]  = (CHAR16) IScsiHexString[Lun[2 * Index + 1] & 0x0F];
218      TempStr[4]  = L'-';
219      TempStr[5]  = 0;
220
221      IScsiStrTrim (TempStr, L'0');
222    }
223
224    TempStr += StrLen (TempStr);
225  }
226
227  Str[StrLen (Str) - 1] = 0;
228
229  for (Index = StrLen (Str) - 1; Index > 1; Index = Index - 2) {
230    if ((Str[Index] == L'0') && (Str[Index - 1] == L'-')) {
231      Str[Index - 1] = 0;
232    } else {
233      break;
234    }
235  }
236}
237
238/**
239  Convert the formatted IP address into the binary IP address.
240
241  @param[in]   Str               The UNICODE string.
242  @param[in]   IpMode            Indicates whether the IP address is v4 or v6.
243  @param[out]  Ip                The storage to return the ASCII string.
244
245  @retval EFI_SUCCESS            The binary IP address is returned in Ip.
246  @retval EFI_INVALID_PARAMETER  The IP string is malformatted or IpMode is
247                                 invalid.
248
249**/
250EFI_STATUS
251IScsiAsciiStrToIp (
252  IN  CHAR8             *Str,
253  IN  UINT8             IpMode,
254  OUT EFI_IP_ADDRESS    *Ip
255  )
256{
257  EFI_STATUS            Status;
258
259  if (IpMode == IP_MODE_IP4 || IpMode == IP_MODE_AUTOCONFIG_IP4) {
260    return NetLibAsciiStrToIp4 (Str, &Ip->v4);
261
262  } else if (IpMode == IP_MODE_IP6 || IpMode == IP_MODE_AUTOCONFIG_IP6) {
263    return NetLibAsciiStrToIp6 (Str, &Ip->v6);
264
265  } else if (IpMode == IP_MODE_AUTOCONFIG) {
266    Status = NetLibAsciiStrToIp4 (Str, &Ip->v4);
267    if (!EFI_ERROR (Status)) {
268      return Status;
269    }
270    return NetLibAsciiStrToIp6 (Str, &Ip->v6);
271
272  }
273
274  return EFI_INVALID_PARAMETER;
275}
276
277/**
278  Convert the mac address into a hexadecimal encoded "-" seperated string.
279
280  @param[in]  Mac     The mac address.
281  @param[in]  Len     Length in bytes of the mac address.
282  @param[in]  VlanId  VLAN ID of the network device.
283  @param[out] Str     The storage to return the mac string.
284
285**/
286VOID
287IScsiMacAddrToStr (
288  IN  EFI_MAC_ADDRESS  *Mac,
289  IN  UINT32           Len,
290  IN  UINT16           VlanId,
291  OUT CHAR16           *Str
292  )
293{
294  UINT32  Index;
295  CHAR16  *String;
296
297  for (Index = 0; Index < Len; Index++) {
298    Str[3 * Index]      = (CHAR16) IScsiHexString[(Mac->Addr[Index] >> 4) & 0x0F];
299    Str[3 * Index + 1]  = (CHAR16) IScsiHexString[Mac->Addr[Index] & 0x0F];
300    Str[3 * Index + 2]  = L':';
301  }
302
303  String = &Str[3 * Index - 1] ;
304  if (VlanId != 0) {
305    String += UnicodeSPrint (String, 6 * sizeof (CHAR16), L"\\%04x", (UINTN) VlanId);
306  }
307
308  *String = L'\0';
309}
310
311/**
312  Convert the binary encoded buffer into a hexadecimal encoded string.
313
314  @param[in]       BinBuffer   The buffer containing the binary data.
315  @param[in]       BinLength   Length of the binary buffer.
316  @param[in, out]  HexStr      Pointer to the string.
317  @param[in, out]  HexLength   The length of the string.
318
319  @retval EFI_SUCCESS          The binary data is converted to the hexadecimal string
320                               and the length of the string is updated.
321  @retval EFI_BUFFER_TOO_SMALL The string is too small.
322  @retval EFI_INVALID_PARAMETER The IP string is malformatted.
323
324**/
325EFI_STATUS
326IScsiBinToHex (
327  IN     UINT8  *BinBuffer,
328  IN     UINT32 BinLength,
329  IN OUT CHAR8  *HexStr,
330  IN OUT UINT32 *HexLength
331  )
332{
333  UINTN Index;
334
335  if ((HexStr == NULL) || (BinBuffer == NULL) || (BinLength == 0)) {
336    return EFI_INVALID_PARAMETER;
337  }
338
339  if (((*HexLength) - 3) < BinLength * 2) {
340    *HexLength = BinLength * 2 + 3;
341    return EFI_BUFFER_TOO_SMALL;
342  }
343
344  *HexLength = BinLength * 2 + 3;
345  //
346  // Prefix for Hex String.
347  //
348  HexStr[0] = '0';
349  HexStr[1] = 'x';
350
351  for (Index = 0; Index < BinLength; Index++) {
352    HexStr[Index * 2 + 2] = IScsiHexString[BinBuffer[Index] >> 4];
353    HexStr[Index * 2 + 3] = IScsiHexString[BinBuffer[Index] & 0xf];
354  }
355
356  HexStr[Index * 2 + 2] = '\0';
357
358  return EFI_SUCCESS;
359}
360
361
362/**
363  Convert the hexadecimal string into a binary encoded buffer.
364
365  @param[in, out]  BinBuffer   The binary buffer.
366  @param[in, out]  BinLength   Length of the binary buffer.
367  @param[in]       HexStr      The hexadecimal string.
368
369  @retval EFI_SUCCESS          The hexadecimal string is converted into a binary
370                               encoded buffer.
371  @retval EFI_BUFFER_TOO_SMALL The binary buffer is too small to hold the converted data.
372
373**/
374EFI_STATUS
375IScsiHexToBin (
376  IN OUT UINT8  *BinBuffer,
377  IN OUT UINT32 *BinLength,
378  IN     CHAR8  *HexStr
379  )
380{
381  UINTN   Index;
382  UINTN   Length;
383  UINT8   Digit;
384  CHAR8   TemStr[2];
385
386  ZeroMem (TemStr, sizeof (TemStr));
387
388  //
389  // Find out how many hex characters the string has.
390  //
391  if ((HexStr[0] == '0') && ((HexStr[1] == 'x') || (HexStr[1] == 'X'))) {
392    HexStr += 2;
393  }
394
395  Length = AsciiStrLen (HexStr);
396
397  for (Index = 0; Index < Length; Index ++) {
398    TemStr[0] = HexStr[Index];
399    Digit = (UINT8) AsciiStrHexToUint64 (TemStr);
400    if (Digit == 0 && TemStr[0] != '0') {
401      //
402      // Invalid Lun Char.
403      //
404      break;
405    }
406    if ((Index & 1) == 0) {
407      BinBuffer [Index/2] = Digit;
408    } else {
409      BinBuffer [Index/2] = (UINT8) ((BinBuffer [Index/2] << 4) + Digit);
410    }
411  }
412
413  *BinLength = (UINT32) ((Index + 1)/2);
414
415  return EFI_SUCCESS;
416}
417
418
419/**
420  Convert the decimal-constant string or hex-constant string into a numerical value.
421
422  @param[in] Str                    String in decimal or hex.
423
424  @return The numerical value.
425
426**/
427UINTN
428IScsiNetNtoi (
429  IN     CHAR8  *Str
430  )
431{
432  if ((Str[0] == '0') && ((Str[1] == 'x') || (Str[1] == 'X'))) {
433    Str += 2;
434
435    return AsciiStrHexToUintn (Str);
436  }
437
438  return AsciiStrDecimalToUintn (Str);
439}
440
441
442/**
443  Generate random numbers.
444
445  @param[in, out]  Rand       The buffer to contain random numbers.
446  @param[in]       RandLength The length of the Rand buffer.
447
448**/
449VOID
450IScsiGenRandom (
451  IN OUT UINT8  *Rand,
452  IN     UINTN  RandLength
453  )
454{
455  UINT32  Random;
456
457  while (RandLength > 0) {
458    Random  = NET_RANDOM (NetRandomInitSeed ());
459    *Rand++ = (UINT8) (Random);
460    RandLength--;
461  }
462}
463
464
465/**
466  Record the NIC info in global structure.
467
468  @param[in]  Controller         The handle of the controller.
469
470  @retval EFI_SUCCESS            The operation is completed.
471  @retval EFI_OUT_OF_RESOURCES   Do not have sufficient resources to finish this
472                                 operation.
473
474**/
475EFI_STATUS
476IScsiAddNic (
477  IN EFI_HANDLE  Controller
478  )
479{
480  EFI_STATUS                  Status;
481  ISCSI_NIC_INFO              *NicInfo;
482  LIST_ENTRY                  *Entry;
483  EFI_MAC_ADDRESS             MacAddr;
484  UINTN                       HwAddressSize;
485  UINT16                      VlanId;
486
487  //
488  // Get MAC address of this network device.
489  //
490  Status = NetLibGetMacAddress (Controller, &MacAddr, &HwAddressSize);
491  if (EFI_ERROR (Status)) {
492    return Status;
493  }
494
495  //
496  // Get VLAN ID of this network device.
497  //
498  VlanId = NetLibGetVlanId (Controller);
499
500  //
501  // Check whether the NIC info already exists. Return directly if so.
502  //
503  NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {
504    NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);
505    if (NicInfo->HwAddressSize == HwAddressSize &&
506        CompareMem (&NicInfo->PermanentAddress, MacAddr.Addr, HwAddressSize) == 0 &&
507        NicInfo->VlanId == VlanId) {
508      mPrivate->CurrentNic = NicInfo->NicIndex;
509      return EFI_SUCCESS;
510    }
511
512    if (mPrivate->MaxNic < NicInfo->NicIndex) {
513      mPrivate->MaxNic = NicInfo->NicIndex;
514    }
515  }
516
517  //
518  // Record the NIC info in private structure.
519  //
520  NicInfo = AllocateZeroPool (sizeof (ISCSI_NIC_INFO));
521  if (NicInfo == NULL) {
522    return EFI_OUT_OF_RESOURCES;
523  }
524
525  CopyMem (&NicInfo->PermanentAddress, MacAddr.Addr, HwAddressSize);
526  NicInfo->HwAddressSize  = (UINT32) HwAddressSize;
527  NicInfo->VlanId         = VlanId;
528  NicInfo->NicIndex       = (UINT8) (mPrivate->MaxNic + 1);
529  mPrivate->MaxNic        = NicInfo->NicIndex;
530
531  //
532  // Get the PCI location.
533  //
534  IScsiGetNICPciLocation (
535    Controller,
536    &NicInfo->BusNumber,
537    &NicInfo->DeviceNumber,
538    &NicInfo->FunctionNumber
539    );
540
541  InsertTailList (&mPrivate->NicInfoList, &NicInfo->Link);
542  mPrivate->NicCount++;
543
544  mPrivate->CurrentNic = NicInfo->NicIndex;
545  return EFI_SUCCESS;
546}
547
548
549/**
550  Delete the recorded NIC info from global structure. Also delete corresponding
551  attempts.
552
553  @param[in]  Controller         The handle of the controller.
554
555  @retval EFI_SUCCESS            The operation is completed.
556  @retval EFI_NOT_FOUND          The NIC info to be deleted is not recorded.
557
558**/
559EFI_STATUS
560IScsiRemoveNic (
561  IN EFI_HANDLE  Controller
562  )
563{
564  EFI_STATUS                  Status;
565  ISCSI_NIC_INFO              *NicInfo;
566  LIST_ENTRY                  *Entry;
567  LIST_ENTRY                  *NextEntry;
568  ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
569  ISCSI_NIC_INFO              *ThisNic;
570  EFI_MAC_ADDRESS             MacAddr;
571  UINTN                       HwAddressSize;
572  UINT16                      VlanId;
573
574  //
575  // Get MAC address of this network device.
576  //
577  Status = NetLibGetMacAddress (Controller, &MacAddr, &HwAddressSize);
578  if (EFI_ERROR (Status)) {
579    return Status;
580  }
581
582  //
583  // Get VLAN ID of this network device.
584  //
585  VlanId = NetLibGetVlanId (Controller);
586
587  //
588  // Check whether the NIC information exists.
589  //
590  ThisNic = NULL;
591
592  NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {
593    NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);
594    if (NicInfo->HwAddressSize == HwAddressSize &&
595        CompareMem (&NicInfo->PermanentAddress, MacAddr.Addr, HwAddressSize) == 0 &&
596        NicInfo->VlanId == VlanId) {
597
598      ThisNic = NicInfo;
599      break;
600    }
601  }
602
603  if (ThisNic == NULL) {
604    return EFI_NOT_FOUND;
605  }
606
607  mPrivate->CurrentNic = ThisNic->NicIndex;
608
609  RemoveEntryList (&ThisNic->Link);
610  FreePool (ThisNic);
611  mPrivate->NicCount--;
612
613  //
614  // Remove all attempts related to this NIC.
615  //
616  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mPrivate->AttemptConfigs) {
617    AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
618    if (AttemptConfigData->NicIndex == mPrivate->CurrentNic) {
619      RemoveEntryList (&AttemptConfigData->Link);
620      mPrivate->AttemptCount--;
621
622      if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO && mPrivate->MpioCount > 0) {
623        if (--mPrivate->MpioCount == 0) {
624          mPrivate->EnableMpio = FALSE;
625        }
626
627        if (AttemptConfigData->AuthenticationType == ISCSI_AUTH_TYPE_KRB && mPrivate->Krb5MpioCount > 0) {
628          mPrivate->Krb5MpioCount--;
629        }
630
631      } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED && mPrivate->SinglePathCount > 0) {
632        mPrivate->SinglePathCount--;
633
634        if (mPrivate->ValidSinglePathCount > 0) {
635          mPrivate->ValidSinglePathCount--;
636        }
637      }
638
639      FreePool (AttemptConfigData);
640    }
641  }
642
643  //
644  // Free attempt is created but not saved to system.
645  //
646  if (mPrivate->NewAttempt != NULL) {
647    FreePool (mPrivate->NewAttempt);
648    mPrivate->NewAttempt = NULL;
649  }
650
651  return EFI_SUCCESS;
652}
653
654
655/**
656  Get the recorded NIC info from global structure by the Index.
657
658  @param[in]  NicIndex          The index indicates the position of NIC info.
659
660  @return Pointer to the NIC info, or NULL if not found.
661
662**/
663ISCSI_NIC_INFO *
664IScsiGetNicInfoByIndex (
665  IN UINT8      NicIndex
666  )
667{
668  LIST_ENTRY        *Entry;
669  ISCSI_NIC_INFO    *NicInfo;
670
671  NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {
672    NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);
673    if (NicInfo->NicIndex == NicIndex) {
674      return NicInfo;
675    }
676  }
677
678  return NULL;
679}
680
681
682/**
683  Get the NIC's PCI location and return it accroding to the composited
684  format defined in iSCSI Boot Firmware Table.
685
686  @param[in]   Controller        The handle of the controller.
687  @param[out]  Bus               The bus number.
688  @param[out]  Device            The device number.
689  @param[out]  Function          The function number.
690
691  @return      The composited representation of the NIC PCI location.
692
693**/
694UINT16
695IScsiGetNICPciLocation (
696  IN EFI_HANDLE  Controller,
697  OUT UINTN      *Bus,
698  OUT UINTN      *Device,
699  OUT UINTN      *Function
700  )
701{
702  EFI_STATUS                Status;
703  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
704  EFI_HANDLE                PciIoHandle;
705  EFI_PCI_IO_PROTOCOL       *PciIo;
706  UINTN                     Segment;
707
708  Status = gBS->HandleProtocol (
709                  Controller,
710                  &gEfiDevicePathProtocolGuid,
711                  (VOID **) &DevicePath
712                  );
713  if (EFI_ERROR (Status)) {
714    return 0;
715  }
716
717  Status = gBS->LocateDevicePath (
718                  &gEfiPciIoProtocolGuid,
719                  &DevicePath,
720                  &PciIoHandle
721                  );
722  if (EFI_ERROR (Status)) {
723    return 0;
724  }
725
726  Status = gBS->HandleProtocol (PciIoHandle, &gEfiPciIoProtocolGuid, (VOID **) &PciIo);
727  if (EFI_ERROR (Status)) {
728    return 0;
729  }
730
731  Status = PciIo->GetLocation (PciIo, &Segment, Bus, Device, Function);
732  if (EFI_ERROR (Status)) {
733    return 0;
734  }
735
736  return (UINT16) ((*Bus << 8) | (*Device << 3) | *Function);
737}
738
739
740/**
741  Read the EFI variable (VendorGuid/Name) and return a dynamically allocated
742  buffer, and the size of the buffer. If failure, return NULL.
743
744  @param[in]   Name                   String part of EFI variable name.
745  @param[in]   VendorGuid             GUID part of EFI variable name.
746  @param[out]  VariableSize           Returns the size of the EFI variable that was read.
747
748  @return Dynamically allocated memory that contains a copy of the EFI variable.
749  @return Caller is responsible freeing the buffer.
750  @retval NULL                   Variable was not read.
751
752**/
753VOID *
754IScsiGetVariableAndSize (
755  IN  CHAR16              *Name,
756  IN  EFI_GUID            *VendorGuid,
757  OUT UINTN               *VariableSize
758  )
759{
760  EFI_STATUS  Status;
761  UINTN       BufferSize;
762  VOID        *Buffer;
763
764  Buffer = NULL;
765
766  //
767  // Pass in a zero size buffer to find the required buffer size.
768  //
769  BufferSize  = 0;
770  Status      = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
771  if (Status == EFI_BUFFER_TOO_SMALL) {
772    //
773    // Allocate the buffer to return
774    //
775    Buffer = AllocateZeroPool (BufferSize);
776    if (Buffer == NULL) {
777      return NULL;
778    }
779    //
780    // Read variable into the allocated buffer.
781    //
782    Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
783    if (EFI_ERROR (Status)) {
784      BufferSize = 0;
785    }
786  }
787
788  *VariableSize = BufferSize;
789  return Buffer;
790}
791
792
793/**
794  Create the iSCSI driver data.
795
796  @param[in] Image      The handle of the driver image.
797  @param[in] Controller The handle of the controller.
798
799  @return The iSCSI driver data created.
800  @retval NULL Other errors as indicated.
801
802**/
803ISCSI_DRIVER_DATA *
804IScsiCreateDriverData (
805  IN EFI_HANDLE  Image,
806  IN EFI_HANDLE  Controller
807  )
808{
809  ISCSI_DRIVER_DATA *Private;
810  EFI_STATUS        Status;
811
812  Private = AllocateZeroPool (sizeof (ISCSI_DRIVER_DATA));
813  if (Private == NULL) {
814    return NULL;
815  }
816
817  Private->Signature  = ISCSI_DRIVER_DATA_SIGNATURE;
818  Private->Image      = Image;
819  Private->Controller = Controller;
820  Private->Session    = NULL;
821
822  //
823  // Create an event to be signaled when the BS to RT transition is triggerd so
824  // as to abort the iSCSI session.
825  //
826  Status = gBS->CreateEventEx (
827                  EVT_NOTIFY_SIGNAL,
828                  TPL_CALLBACK,
829                  IScsiOnExitBootService,
830                  Private,
831                  &gEfiEventExitBootServicesGuid,
832                  &Private->ExitBootServiceEvent
833                  );
834  if (EFI_ERROR (Status)) {
835    FreePool (Private);
836    return NULL;
837  }
838
839  Private->ExtScsiPassThruHandle = NULL;
840  CopyMem(&Private->IScsiExtScsiPassThru, &gIScsiExtScsiPassThruProtocolTemplate, sizeof(EFI_EXT_SCSI_PASS_THRU_PROTOCOL));
841
842  //
843  // 0 is designated to the TargetId, so use another value for the AdapterId.
844  //
845  Private->ExtScsiPassThruMode.AdapterId  = 2;
846  Private->ExtScsiPassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
847  Private->ExtScsiPassThruMode.IoAlign    = 4;
848  Private->IScsiExtScsiPassThru.Mode      = &Private->ExtScsiPassThruMode;
849
850  return Private;
851}
852
853
854/**
855  Clean the iSCSI driver data.
856
857  @param[in]  Private The iSCSI driver data.
858
859**/
860VOID
861IScsiCleanDriverData (
862  IN ISCSI_DRIVER_DATA  *Private
863  )
864{
865  EFI_STATUS            Status;
866
867  if (Private->DevicePath != NULL) {
868    gBS->UninstallProtocolInterface (
869           Private->ExtScsiPassThruHandle,
870           &gEfiDevicePathProtocolGuid,
871           Private->DevicePath
872           );
873
874    FreePool (Private->DevicePath);
875  }
876
877  if (Private->ExtScsiPassThruHandle != NULL) {
878    Status = gBS->UninstallProtocolInterface (
879                    Private->ExtScsiPassThruHandle,
880                    &gEfiExtScsiPassThruProtocolGuid,
881                    &Private->IScsiExtScsiPassThru
882                    );
883    if (!EFI_ERROR (Status)) {
884      mPrivate->OneSessionEstablished = FALSE;
885    }
886  }
887
888  gBS->CloseEvent (Private->ExitBootServiceEvent);
889
890  FreePool (Private);
891}
892
893
894/**
895  Get the various configuration data.
896
897  @param[in]  Private   The iSCSI driver data.
898
899  @retval EFI_SUCCESS            The configuration data is retrieved.
900  @retval EFI_NOT_FOUND          This iSCSI driver is not configured yet.
901
902**/
903EFI_STATUS
904IScsiGetConfigData (
905  IN ISCSI_DRIVER_DATA  *Private
906  )
907{
908  EFI_STATUS                  Status;
909  CHAR16                      MacString[ISCSI_MAX_MAC_STRING_LEN];
910  UINTN                       Index;
911  ISCSI_NIC_INFO              *NicInfo;
912  ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
913  ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptTmp;
914  UINT8                       *AttemptConfigOrder;
915  UINTN                       AttemptConfigOrderSize;
916  CHAR16                      IScsiMode[64];
917  CHAR16                      IpMode[64];
918
919  //
920  // There should be at least one attempt configured.
921  //
922  AttemptConfigOrder = IScsiGetVariableAndSize (
923                         L"AttemptOrder",
924                         &mVendorGuid,
925                         &AttemptConfigOrderSize
926                         );
927  if (AttemptConfigOrder == NULL || AttemptConfigOrderSize == 0) {
928    return EFI_NOT_FOUND;
929  }
930
931  //
932  // Get the iSCSI Initiator Name.
933  //
934  mPrivate->InitiatorNameLength  = ISCSI_NAME_MAX_SIZE;
935  Status = gIScsiInitiatorName.Get (
936                                 &gIScsiInitiatorName,
937                                 &mPrivate->InitiatorNameLength,
938                                 mPrivate->InitiatorName
939                                 );
940  if (EFI_ERROR (Status)) {
941    return Status;
942  }
943
944  //
945  // Get the normal configuration.
946  //
947  for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {
948
949    //
950    // Check whether the attempt exists in AttemptConfig.
951    //
952    AttemptTmp = IScsiConfigGetAttemptByConfigIndex (AttemptConfigOrder[Index]);
953    if (AttemptTmp != NULL && AttemptTmp->SessionConfigData.Enabled == ISCSI_DISABLED) {
954      continue;
955    } else if (AttemptTmp != NULL && AttemptTmp->SessionConfigData.Enabled != ISCSI_DISABLED) {
956      //
957      // Check the autoconfig path to see whether it should be retried.
958      //
959      if (AttemptTmp->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG &&
960          AttemptTmp->AutoConfigureMode != IP_MODE_AUTOCONFIG_SUCCESS) {
961        if (mPrivate->Ipv6Flag &&
962            AttemptTmp->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6) {
963          //
964          // Autoconfigure for IP6 already attempted but failed. Do not try again.
965          //
966          continue;
967        } else if (!mPrivate->Ipv6Flag &&
968                   AttemptTmp->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4) {
969          //
970          // Autoconfigure for IP4  already attempted but failed. Do not try again.
971          //
972          continue;
973        } else {
974          //
975          // Try another approach for this autoconfigure path.
976          //
977          AttemptTmp->AutoConfigureMode =
978            (UINT8) (mPrivate->Ipv6Flag ? IP_MODE_AUTOCONFIG_IP6 : IP_MODE_AUTOCONFIG_IP4);
979          AttemptTmp->SessionConfigData.InitiatorInfoFromDhcp = TRUE;
980          AttemptTmp->SessionConfigData.TargetInfoFromDhcp    = TRUE;
981          AttemptTmp->DhcpSuccess                             = FALSE;
982
983          //
984          // Get some information from the dhcp server.
985          //
986          if (!mPrivate->Ipv6Flag) {
987            Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptTmp);
988            if (!EFI_ERROR (Status)) {
989              AttemptTmp->DhcpSuccess = TRUE;
990            }
991          } else {
992            Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptTmp);
993            if (!EFI_ERROR (Status)) {
994              AttemptTmp->DhcpSuccess = TRUE;
995            }
996          }
997
998          //
999          // Refresh the state of this attempt to NVR.
1000          //
1001          AsciiStrToUnicodeStr (AttemptTmp->MacString, MacString);
1002          UnicodeSPrint (
1003            mPrivate->PortString,
1004            (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
1005            L"%s%d",
1006            MacString,
1007            (UINTN) AttemptTmp->AttemptConfigIndex
1008            );
1009
1010          gRT->SetVariable (
1011                 mPrivate->PortString,
1012                 &gEfiIScsiInitiatorNameProtocolGuid,
1013                 ISCSI_CONFIG_VAR_ATTR,
1014                 sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
1015                 AttemptTmp
1016                 );
1017
1018          continue;
1019        }
1020      } else if (AttemptTmp->SessionConfigData.InitiatorInfoFromDhcp && !AttemptTmp->ValidPath) {
1021        //
1022        // Get DHCP information for already added, but failed, attempt.
1023        //
1024        AttemptTmp->DhcpSuccess = FALSE;
1025        if (!mPrivate->Ipv6Flag && (AttemptTmp->SessionConfigData.IpMode == IP_MODE_IP4)) {
1026          Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptTmp);
1027          if (!EFI_ERROR (Status)) {
1028            AttemptTmp->DhcpSuccess = TRUE;
1029          }
1030        } else if (mPrivate->Ipv6Flag && (AttemptTmp->SessionConfigData.IpMode == IP_MODE_IP6)) {
1031          Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptTmp);
1032          if (!EFI_ERROR (Status)) {
1033            AttemptTmp->DhcpSuccess = TRUE;
1034          }
1035        }
1036
1037        //
1038        // Refresh the state of this attempt to NVR.
1039        //
1040        AsciiStrToUnicodeStr (AttemptTmp->MacString, MacString);
1041        UnicodeSPrint (
1042          mPrivate->PortString,
1043          (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
1044          L"%s%d",
1045          MacString,
1046          (UINTN) AttemptTmp->AttemptConfigIndex
1047          );
1048
1049        gRT->SetVariable (
1050               mPrivate->PortString,
1051               &gEfiIScsiInitiatorNameProtocolGuid,
1052               ISCSI_CONFIG_VAR_ATTR,
1053               sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
1054               AttemptTmp
1055               );
1056
1057        continue;
1058
1059      } else {
1060        continue;
1061      }
1062    }
1063
1064    //
1065    // This attempt does not exist in AttemptConfig. Try to add a new one.
1066    //
1067
1068    NicInfo = IScsiGetNicInfoByIndex (mPrivate->CurrentNic);
1069    ASSERT (NicInfo != NULL);
1070    IScsiMacAddrToStr (&NicInfo->PermanentAddress, NicInfo->HwAddressSize, NicInfo->VlanId, MacString);
1071    UnicodeSPrint (
1072      mPrivate->PortString,
1073      (UINTN) 128,
1074      L"%s%d",
1075      MacString,
1076      (UINTN) AttemptConfigOrder[Index]
1077      );
1078
1079    AttemptConfigData = (ISCSI_ATTEMPT_CONFIG_NVDATA *) GetVariable (
1080                                                          mPrivate->PortString,
1081                                                          &gEfiIScsiInitiatorNameProtocolGuid
1082                                                          );
1083
1084    if (AttemptConfigData == NULL) {
1085      continue;
1086    }
1087
1088    ASSERT (AttemptConfigOrder[Index] == AttemptConfigData->AttemptConfigIndex);
1089
1090    AttemptConfigData->NicIndex      = NicInfo->NicIndex;
1091    AttemptConfigData->DhcpSuccess   = FALSE;
1092    AttemptConfigData->ValidiBFTPath = (BOOLEAN) (mPrivate->EnableMpio ? TRUE : FALSE);
1093    AttemptConfigData->ValidPath     = FALSE;
1094
1095    if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) {
1096      AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp = TRUE;
1097      AttemptConfigData->SessionConfigData.TargetInfoFromDhcp    = TRUE;
1098
1099      AttemptConfigData->AutoConfigureMode =
1100        (UINT8) (mPrivate->Ipv6Flag ? IP_MODE_AUTOCONFIG_IP6 : IP_MODE_AUTOCONFIG_IP4);
1101    }
1102
1103    //
1104    // Get some information from dhcp server.
1105    //
1106    if (AttemptConfigData->SessionConfigData.Enabled != ISCSI_DISABLED &&
1107        AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp) {
1108
1109      if (!mPrivate->Ipv6Flag &&
1110          (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4 ||
1111           AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4)) {
1112        Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptConfigData);
1113        if (!EFI_ERROR (Status)) {
1114          AttemptConfigData->DhcpSuccess = TRUE;
1115        }
1116      } else if (mPrivate->Ipv6Flag &&
1117                (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6 ||
1118                 AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6)) {
1119        Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptConfigData);
1120        if (!EFI_ERROR (Status)) {
1121          AttemptConfigData->DhcpSuccess = TRUE;
1122        }
1123      }
1124
1125      //
1126      // Refresh the state of this attempt to NVR.
1127      //
1128      AsciiStrToUnicodeStr (AttemptConfigData->MacString, MacString);
1129      UnicodeSPrint (
1130        mPrivate->PortString,
1131        (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
1132        L"%s%d",
1133        MacString,
1134        (UINTN) AttemptConfigData->AttemptConfigIndex
1135        );
1136
1137      gRT->SetVariable (
1138             mPrivate->PortString,
1139             &gEfiIScsiInitiatorNameProtocolGuid,
1140             ISCSI_CONFIG_VAR_ATTR,
1141             sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
1142             AttemptConfigData
1143             );
1144    }
1145
1146    //
1147    // Update Attempt Help Info.
1148    //
1149
1150    if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_DISABLED) {
1151      UnicodeSPrint (IScsiMode, 64, L"Disabled");
1152    } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) {
1153      UnicodeSPrint (IScsiMode, 64, L"Enabled");
1154    } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
1155      UnicodeSPrint (IScsiMode, 64, L"Enabled for MPIO");
1156    }
1157
1158    if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4) {
1159      UnicodeSPrint (IpMode, 64, L"IP4");
1160    } else if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6) {
1161      UnicodeSPrint (IpMode, 64, L"IP6");
1162    } else if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) {
1163      UnicodeSPrint (IpMode, 64, L"Autoconfigure");
1164    }
1165
1166    UnicodeSPrint (
1167      mPrivate->PortString,
1168      (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
1169      L"MAC: %s, PFA: Bus %d | Dev %d | Func %d, iSCSI mode: %s, IP version: %s",
1170      MacString,
1171      NicInfo->BusNumber,
1172      NicInfo->DeviceNumber,
1173      NicInfo->FunctionNumber,
1174      IScsiMode,
1175      IpMode
1176      );
1177
1178    AttemptConfigData->AttemptTitleHelpToken = HiiSetString (
1179                                                 mCallbackInfo->RegisteredHandle,
1180                                                 0,
1181                                                 mPrivate->PortString,
1182                                                 NULL
1183                                                 );
1184    ASSERT (AttemptConfigData->AttemptTitleHelpToken != 0);
1185
1186    //
1187    // Record the attempt in global link list.
1188    //
1189    InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);
1190    mPrivate->AttemptCount++;
1191
1192    if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
1193      mPrivate->MpioCount++;
1194      mPrivate->EnableMpio = TRUE;
1195
1196      if (AttemptConfigData->AuthenticationType == ISCSI_AUTH_TYPE_KRB) {
1197        mPrivate->Krb5MpioCount++;
1198      }
1199    } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) {
1200      mPrivate->SinglePathCount++;
1201    }
1202  }
1203
1204  //
1205  // Reorder the AttemptConfig by the configured order.
1206  //
1207  for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {
1208    AttemptConfigData = IScsiConfigGetAttemptByConfigIndex (AttemptConfigOrder[Index]);
1209    if (AttemptConfigData == NULL) {
1210      continue;
1211    }
1212
1213    RemoveEntryList (&AttemptConfigData->Link);
1214    InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);
1215  }
1216
1217  //
1218  // Update the Main Form.
1219  //
1220  IScsiConfigUpdateAttempt ();
1221
1222  FreePool (AttemptConfigOrder);
1223
1224  //
1225  //  There should be at least one attempt configuration.
1226  //
1227  if (!mPrivate->EnableMpio) {
1228    if (mPrivate->SinglePathCount == 0) {
1229      return EFI_NOT_FOUND;
1230    }
1231    mPrivate->ValidSinglePathCount = mPrivate->SinglePathCount;
1232  }
1233
1234  return EFI_SUCCESS;
1235}
1236
1237
1238/**
1239  Get the device path of the iSCSI tcp connection and update it.
1240
1241  @param  Session                The iSCSI session.
1242
1243  @return The updated device path.
1244  @retval NULL Other errors as indicated.
1245
1246**/
1247EFI_DEVICE_PATH_PROTOCOL *
1248IScsiGetTcpConnDevicePath (
1249  IN ISCSI_SESSION      *Session
1250  )
1251{
1252  ISCSI_CONNECTION          *Conn;
1253  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
1254  EFI_STATUS                Status;
1255  EFI_DEV_PATH              *DPathNode;
1256
1257  if (Session->State != SESSION_STATE_LOGGED_IN) {
1258    return NULL;
1259  }
1260
1261  Conn = NET_LIST_USER_STRUCT_S (
1262           Session->Conns.ForwardLink,
1263           ISCSI_CONNECTION,
1264           Link,
1265           ISCSI_CONNECTION_SIGNATURE
1266           );
1267
1268  Status = gBS->HandleProtocol (
1269                  Conn->TcpIo.Handle,
1270                  &gEfiDevicePathProtocolGuid,
1271                  (VOID **) &DevicePath
1272                  );
1273  if (EFI_ERROR (Status)) {
1274    return NULL;
1275  }
1276  //
1277  // Duplicate it.
1278  //
1279  DevicePath  = DuplicateDevicePath (DevicePath);
1280  if (DevicePath == NULL) {
1281    return NULL;
1282  }
1283
1284  DPathNode   = (EFI_DEV_PATH *) DevicePath;
1285
1286  while (!IsDevicePathEnd (&DPathNode->DevPath)) {
1287    if (DevicePathType (&DPathNode->DevPath) == MESSAGING_DEVICE_PATH) {
1288      if (!Conn->Ipv6Flag && DevicePathSubType (&DPathNode->DevPath) == MSG_IPv4_DP) {
1289        DPathNode->Ipv4.LocalPort       = 0;
1290        DPathNode->Ipv4.StaticIpAddress = (BOOLEAN) !Session->ConfigData->SessionConfigData.InitiatorInfoFromDhcp;
1291        break;
1292      } else if (Conn->Ipv6Flag && DevicePathSubType (&DPathNode->DevPath) == MSG_IPv6_DP) {
1293        DPathNode->Ipv6.LocalPort       = 0;
1294        DPathNode->Ipv6.StaticIpAddress = (BOOLEAN) !Session->ConfigData->SessionConfigData.InitiatorInfoFromDhcp;
1295        break;
1296      }
1297    }
1298
1299    DPathNode = (EFI_DEV_PATH *) NextDevicePathNode (&DPathNode->DevPath);
1300  }
1301
1302  return DevicePath;
1303}
1304
1305
1306/**
1307  Abort the session when the transition from BS to RT is initiated.
1308
1309  @param[in]   Event  The event signaled.
1310  @param[in]  Context The iSCSI driver data.
1311
1312**/
1313VOID
1314EFIAPI
1315IScsiOnExitBootService (
1316  IN EFI_EVENT  Event,
1317  IN VOID       *Context
1318  )
1319{
1320  ISCSI_DRIVER_DATA *Private;
1321
1322  Private = (ISCSI_DRIVER_DATA *) Context;
1323  gBS->CloseEvent (Private->ExitBootServiceEvent);
1324
1325  if (Private->Session != NULL) {
1326    IScsiSessionAbort (Private->Session);
1327  }
1328}
1329