]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/IScsiDxe/IScsiMisc.c
Add IScsiDxe driver to NetworkPkg in order to support iSCSI over IPv6 stack and iSCSI...
[mirror_edk2.git] / NetworkPkg / IScsiDxe / IScsiMisc.c
1 /** @file
2 Miscellaneous routines for iSCSI driver.
3
4 Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "IScsiImpl.h"
16
17 GLOBAL_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 **/
28 VOID
29 IScsiStrTrim (
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 **/
81 UINT8
82 IScsiGetSubnetMaskPrefixLength (
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 **/
124 EFI_STATUS
125 IScsiAsciiStrToLun (
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 **/
198 VOID
199 IScsiLunToUnicodeStr (
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 **/
250 EFI_STATUS
251 IScsiAsciiStrToIp (
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 **/
286 VOID
287 IScsiMacAddrToStr (
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 **/
325 EFI_STATUS
326 IScsiBinToHex (
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 **/
374 EFI_STATUS
375 IScsiHexToBin (
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 **/
427 UINTN
428 IScsiNetNtoi (
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 **/
449 VOID
450 IScsiGenRandom (
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 **/
475 EFI_STATUS
476 IScsiAddNic (
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 **/
559 EFI_STATUS
560 IScsiRemoveNic (
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 return EFI_SUCCESS;
644 }
645
646
647 /**
648 Get the recorded NIC info from global structure by the Index.
649
650 @param[in] NicIndex The index indicates the position of NIC info.
651
652 @return Pointer to the NIC info, or NULL if not found.
653
654 **/
655 ISCSI_NIC_INFO *
656 IScsiGetNicInfoByIndex (
657 IN UINT8 NicIndex
658 )
659 {
660 LIST_ENTRY *Entry;
661 ISCSI_NIC_INFO *NicInfo;
662
663 NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {
664 NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);
665 if (NicInfo->NicIndex == NicIndex) {
666 return NicInfo;
667 }
668 }
669
670 return NULL;
671 }
672
673
674 /**
675 Get the NIC's PCI location and return it accroding to the composited
676 format defined in iSCSI Boot Firmware Table.
677
678 @param[in] Controller The handle of the controller.
679 @param[out] Bus The bus number.
680 @param[out] Device The device number.
681 @param[out] Function The function number.
682
683 @return The composited representation of the NIC PCI location.
684
685 **/
686 UINT16
687 IScsiGetNICPciLocation (
688 IN EFI_HANDLE Controller,
689 OUT UINTN *Bus,
690 OUT UINTN *Device,
691 OUT UINTN *Function
692 )
693 {
694 EFI_STATUS Status;
695 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
696 EFI_HANDLE PciIoHandle;
697 EFI_PCI_IO_PROTOCOL *PciIo;
698 UINTN Segment;
699
700 Status = gBS->HandleProtocol (
701 Controller,
702 &gEfiDevicePathProtocolGuid,
703 (VOID **) &DevicePath
704 );
705 if (EFI_ERROR (Status)) {
706 return 0;
707 }
708
709 Status = gBS->LocateDevicePath (
710 &gEfiPciIoProtocolGuid,
711 &DevicePath,
712 &PciIoHandle
713 );
714 if (EFI_ERROR (Status)) {
715 return 0;
716 }
717
718 Status = gBS->HandleProtocol (PciIoHandle, &gEfiPciIoProtocolGuid, (VOID **) &PciIo);
719 if (EFI_ERROR (Status)) {
720 return 0;
721 }
722
723 Status = PciIo->GetLocation (PciIo, &Segment, Bus, Device, Function);
724 if (EFI_ERROR (Status)) {
725 return 0;
726 }
727
728 return (UINT16) ((*Bus << 8) | (*Device << 3) | *Function);
729 }
730
731
732 /**
733 Read the EFI variable (VendorGuid/Name) and return a dynamically allocated
734 buffer, and the size of the buffer. If failure, return NULL.
735
736 @param[in] Name String part of EFI variable name.
737 @param[in] VendorGuid GUID part of EFI variable name.
738 @param[out] VariableSize Returns the size of the EFI variable that was read.
739
740 @return Dynamically allocated memory that contains a copy of the EFI variable.
741 @return Caller is responsible freeing the buffer.
742 @retval NULL Variable was not read.
743
744 **/
745 VOID *
746 IScsiGetVariableAndSize (
747 IN CHAR16 *Name,
748 IN EFI_GUID *VendorGuid,
749 OUT UINTN *VariableSize
750 )
751 {
752 EFI_STATUS Status;
753 UINTN BufferSize;
754 VOID *Buffer;
755
756 Buffer = NULL;
757
758 //
759 // Pass in a zero size buffer to find the required buffer size.
760 //
761 BufferSize = 0;
762 Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
763 if (Status == EFI_BUFFER_TOO_SMALL) {
764 //
765 // Allocate the buffer to return
766 //
767 Buffer = AllocateZeroPool (BufferSize);
768 if (Buffer == NULL) {
769 return NULL;
770 }
771 //
772 // Read variable into the allocated buffer.
773 //
774 Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
775 if (EFI_ERROR (Status)) {
776 BufferSize = 0;
777 }
778 }
779
780 *VariableSize = BufferSize;
781 return Buffer;
782 }
783
784
785 /**
786 Create the iSCSI driver data.
787
788 @param[in] Image The handle of the driver image.
789 @param[in] Controller The handle of the controller.
790
791 @return The iSCSI driver data created.
792 @retval NULL Other errors as indicated.
793
794 **/
795 ISCSI_DRIVER_DATA *
796 IScsiCreateDriverData (
797 IN EFI_HANDLE Image,
798 IN EFI_HANDLE Controller
799 )
800 {
801 ISCSI_DRIVER_DATA *Private;
802 EFI_STATUS Status;
803
804 Private = AllocateZeroPool (sizeof (ISCSI_DRIVER_DATA));
805 if (Private == NULL) {
806 return NULL;
807 }
808
809 Private->Signature = ISCSI_DRIVER_DATA_SIGNATURE;
810 Private->Image = Image;
811 Private->Controller = Controller;
812 Private->Session = NULL;
813
814 //
815 // Create an event to be signaled when the BS to RT transition is triggerd so
816 // as to abort the iSCSI session.
817 //
818 Status = gBS->CreateEventEx (
819 EVT_NOTIFY_SIGNAL,
820 TPL_CALLBACK,
821 IScsiOnExitBootService,
822 Private,
823 &gEfiEventExitBootServicesGuid,
824 &Private->ExitBootServiceEvent
825 );
826 if (EFI_ERROR (Status)) {
827 FreePool (Private);
828 return NULL;
829 }
830
831 Private->ExtScsiPassThruHandle = NULL;
832 CopyMem(&Private->IScsiExtScsiPassThru, &gIScsiExtScsiPassThruProtocolTemplate, sizeof(EFI_EXT_SCSI_PASS_THRU_PROTOCOL));
833
834 //
835 // 0 is designated to the TargetId, so use another value for the AdapterId.
836 //
837 Private->ExtScsiPassThruMode.AdapterId = 2;
838 Private->ExtScsiPassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
839 Private->ExtScsiPassThruMode.IoAlign = 4;
840 Private->IScsiExtScsiPassThru.Mode = &Private->ExtScsiPassThruMode;
841
842 return Private;
843 }
844
845
846 /**
847 Clean the iSCSI driver data.
848
849 @param[in] Private The iSCSI driver data.
850
851 **/
852 VOID
853 IScsiCleanDriverData (
854 IN ISCSI_DRIVER_DATA *Private
855 )
856 {
857 EFI_STATUS Status;
858
859 if (Private->DevicePath != NULL) {
860 gBS->UninstallProtocolInterface (
861 Private->ExtScsiPassThruHandle,
862 &gEfiDevicePathProtocolGuid,
863 Private->DevicePath
864 );
865
866 FreePool (Private->DevicePath);
867 }
868
869 if (Private->ExtScsiPassThruHandle != NULL) {
870 Status = gBS->UninstallProtocolInterface (
871 Private->ExtScsiPassThruHandle,
872 &gEfiExtScsiPassThruProtocolGuid,
873 &Private->IScsiExtScsiPassThru
874 );
875 if (!EFI_ERROR (Status)) {
876 mPrivate->OneSessionEstablished = FALSE;
877 }
878 }
879
880 gBS->CloseEvent (Private->ExitBootServiceEvent);
881
882 FreePool (Private);
883 }
884
885
886 /**
887 Get the various configuration data.
888
889 @param[in] Private The iSCSI driver data.
890
891 @retval EFI_SUCCESS The configuration data is retrieved.
892 @retval EFI_NOT_FOUND This iSCSI driver is not configured yet.
893
894 **/
895 EFI_STATUS
896 IScsiGetConfigData (
897 IN ISCSI_DRIVER_DATA *Private
898 )
899 {
900 EFI_STATUS Status;
901 CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN];
902 UINTN Index;
903 ISCSI_NIC_INFO *NicInfo;
904 ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
905 ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptTmp;
906 UINT8 *AttemptConfigOrder;
907 UINTN AttemptConfigOrderSize;
908 CHAR16 IScsiMode[64];
909 CHAR16 IpMode[64];
910
911 //
912 // There should be at least one attempt configured.
913 //
914 AttemptConfigOrder = IScsiGetVariableAndSize (
915 L"AttemptOrder",
916 &mVendorGuid,
917 &AttemptConfigOrderSize
918 );
919 if (AttemptConfigOrder == NULL || AttemptConfigOrderSize == 0) {
920 return EFI_NOT_FOUND;
921 }
922
923 //
924 // Get the iSCSI Initiator Name.
925 //
926 mPrivate->InitiatorNameLength = ISCSI_NAME_MAX_SIZE;
927 Status = gIScsiInitiatorName.Get (
928 &gIScsiInitiatorName,
929 &mPrivate->InitiatorNameLength,
930 mPrivate->InitiatorName
931 );
932 if (EFI_ERROR (Status)) {
933 return Status;
934 }
935
936 //
937 // Get the normal configuration.
938 //
939 for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {
940
941 //
942 // Check whether the attempt exists in AttemptConfig.
943 //
944 AttemptTmp = IScsiConfigGetAttemptByConfigIndex (AttemptConfigOrder[Index]);
945 if (AttemptTmp != NULL && AttemptTmp->SessionConfigData.Enabled == ISCSI_DISABLED) {
946 continue;
947 } else if (AttemptTmp != NULL && AttemptTmp->SessionConfigData.Enabled != ISCSI_DISABLED) {
948 //
949 // Check the autoconfig path to see whether it should be retried.
950 //
951 if (AttemptTmp->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG &&
952 AttemptTmp->AutoConfigureMode != IP_MODE_AUTOCONFIG_SUCCESS) {
953 if (mPrivate->Ipv6Flag &&
954 AttemptTmp->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6) {
955 //
956 // Autoconfigure for IP6 already attempted but failed. Do not try again.
957 //
958 continue;
959 } else if (!mPrivate->Ipv6Flag &&
960 AttemptTmp->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4) {
961 //
962 // Autoconfigure for IP4 already attempted but failed. Do not try again.
963 //
964 continue;
965 } else {
966 //
967 // Try another approach for this autoconfigure path.
968 //
969 AttemptTmp->AutoConfigureMode =
970 (UINT8) (mPrivate->Ipv6Flag ? IP_MODE_AUTOCONFIG_IP6 : IP_MODE_AUTOCONFIG_IP4);
971 AttemptTmp->SessionConfigData.InitiatorInfoFromDhcp = TRUE;
972 AttemptTmp->SessionConfigData.TargetInfoFromDhcp = TRUE;
973 AttemptTmp->DhcpSuccess = FALSE;
974
975 //
976 // Get some information from the dhcp server.
977 //
978 if (!mPrivate->Ipv6Flag) {
979 Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptTmp);
980 if (!EFI_ERROR (Status)) {
981 AttemptTmp->DhcpSuccess = TRUE;
982 }
983 } else {
984 Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptTmp);
985 if (!EFI_ERROR (Status)) {
986 AttemptTmp->DhcpSuccess = TRUE;
987 }
988 }
989
990 //
991 // Refresh the state of this attempt to NVR.
992 //
993 AsciiStrToUnicodeStr (AttemptTmp->MacString, MacString);
994 UnicodeSPrint (
995 mPrivate->PortString,
996 (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
997 L"%s%d",
998 MacString,
999 (UINTN) AttemptTmp->AttemptConfigIndex
1000 );
1001
1002 gRT->SetVariable (
1003 mPrivate->PortString,
1004 &gEfiIScsiInitiatorNameProtocolGuid,
1005 ISCSI_CONFIG_VAR_ATTR,
1006 sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
1007 AttemptTmp
1008 );
1009
1010 continue;
1011 }
1012 } else if (AttemptTmp->SessionConfigData.InitiatorInfoFromDhcp && !AttemptTmp->ValidPath) {
1013 //
1014 // Get DHCP information for already added, but failed, attempt.
1015 //
1016 AttemptTmp->DhcpSuccess = FALSE;
1017 if (!mPrivate->Ipv6Flag && (AttemptTmp->SessionConfigData.IpMode == IP_MODE_IP4)) {
1018 Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptTmp);
1019 if (!EFI_ERROR (Status)) {
1020 AttemptTmp->DhcpSuccess = TRUE;
1021 }
1022 } else if (mPrivate->Ipv6Flag && (AttemptTmp->SessionConfigData.IpMode == IP_MODE_IP6)) {
1023 Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptTmp);
1024 if (!EFI_ERROR (Status)) {
1025 AttemptTmp->DhcpSuccess = TRUE;
1026 }
1027 }
1028
1029 //
1030 // Refresh the state of this attempt to NVR.
1031 //
1032 AsciiStrToUnicodeStr (AttemptTmp->MacString, MacString);
1033 UnicodeSPrint (
1034 mPrivate->PortString,
1035 (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
1036 L"%s%d",
1037 MacString,
1038 (UINTN) AttemptTmp->AttemptConfigIndex
1039 );
1040
1041 gRT->SetVariable (
1042 mPrivate->PortString,
1043 &gEfiIScsiInitiatorNameProtocolGuid,
1044 ISCSI_CONFIG_VAR_ATTR,
1045 sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
1046 AttemptTmp
1047 );
1048
1049 continue;
1050
1051 } else {
1052 continue;
1053 }
1054 }
1055
1056 //
1057 // This attempt does not exist in AttemptConfig. Try to add a new one.
1058 //
1059
1060 NicInfo = IScsiGetNicInfoByIndex (mPrivate->CurrentNic);
1061 ASSERT (NicInfo != NULL);
1062 IScsiMacAddrToStr (&NicInfo->PermanentAddress, NicInfo->HwAddressSize, NicInfo->VlanId, MacString);
1063 UnicodeSPrint (
1064 mPrivate->PortString,
1065 (UINTN) 128,
1066 L"%s%d",
1067 MacString,
1068 (UINTN) AttemptConfigOrder[Index]
1069 );
1070
1071 AttemptConfigData = (ISCSI_ATTEMPT_CONFIG_NVDATA *) GetVariable (
1072 mPrivate->PortString,
1073 &gEfiIScsiInitiatorNameProtocolGuid
1074 );
1075
1076 if (AttemptConfigData == NULL) {
1077 continue;
1078 }
1079
1080 ASSERT (AttemptConfigOrder[Index] == AttemptConfigData->AttemptConfigIndex);
1081
1082 AttemptConfigData->NicIndex = NicInfo->NicIndex;
1083 AttemptConfigData->DhcpSuccess = FALSE;
1084 AttemptConfigData->ValidiBFTPath = (BOOLEAN) (mPrivate->EnableMpio ? TRUE : FALSE);
1085 AttemptConfigData->ValidPath = FALSE;
1086
1087 if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) {
1088 AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp = TRUE;
1089 AttemptConfigData->SessionConfigData.TargetInfoFromDhcp = TRUE;
1090
1091 AttemptConfigData->AutoConfigureMode =
1092 (UINT8) (mPrivate->Ipv6Flag ? IP_MODE_AUTOCONFIG_IP6 : IP_MODE_AUTOCONFIG_IP4);
1093 }
1094
1095 //
1096 // Get some information from dhcp server.
1097 //
1098 if (AttemptConfigData->SessionConfigData.Enabled != ISCSI_DISABLED &&
1099 AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp) {
1100
1101 if (!mPrivate->Ipv6Flag &&
1102 (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4 ||
1103 AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4)) {
1104 Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptConfigData);
1105 if (!EFI_ERROR (Status)) {
1106 AttemptConfigData->DhcpSuccess = TRUE;
1107 }
1108 } else if (mPrivate->Ipv6Flag &&
1109 (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6 ||
1110 AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6)) {
1111 Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptConfigData);
1112 if (!EFI_ERROR (Status)) {
1113 AttemptConfigData->DhcpSuccess = TRUE;
1114 }
1115 }
1116
1117 //
1118 // Refresh the state of this attempt to NVR.
1119 //
1120 AsciiStrToUnicodeStr (AttemptConfigData->MacString, MacString);
1121 UnicodeSPrint (
1122 mPrivate->PortString,
1123 (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
1124 L"%s%d",
1125 MacString,
1126 (UINTN) AttemptConfigData->AttemptConfigIndex
1127 );
1128
1129 gRT->SetVariable (
1130 mPrivate->PortString,
1131 &gEfiIScsiInitiatorNameProtocolGuid,
1132 ISCSI_CONFIG_VAR_ATTR,
1133 sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
1134 AttemptConfigData
1135 );
1136 }
1137
1138 //
1139 // Update Attempt Help Info.
1140 //
1141
1142 if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_DISABLED) {
1143 UnicodeSPrint (IScsiMode, 64, L"Disabled");
1144 } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) {
1145 UnicodeSPrint (IScsiMode, 64, L"Enabled");
1146 } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
1147 UnicodeSPrint (IScsiMode, 64, L"Enabled for MPIO");
1148 }
1149
1150 if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4) {
1151 UnicodeSPrint (IpMode, 64, L"IP4");
1152 } else if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6) {
1153 UnicodeSPrint (IpMode, 64, L"IP6");
1154 } else if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) {
1155 UnicodeSPrint (IpMode, 64, L"Autoconfigure");
1156 }
1157
1158 UnicodeSPrint (
1159 mPrivate->PortString,
1160 (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
1161 L"MAC: %s, PFA: Bus %d | Dev %d | Func %d, iSCSI mode: %s, IP version: %s",
1162 MacString,
1163 NicInfo->BusNumber,
1164 NicInfo->DeviceNumber,
1165 NicInfo->FunctionNumber,
1166 IScsiMode,
1167 IpMode
1168 );
1169
1170 AttemptConfigData->AttemptTitleHelpToken = HiiSetString (
1171 mCallbackInfo->RegisteredHandle,
1172 0,
1173 mPrivate->PortString,
1174 NULL
1175 );
1176 ASSERT (AttemptConfigData->AttemptTitleHelpToken != 0);
1177
1178 //
1179 // Record the attempt in global link list.
1180 //
1181 InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);
1182 mPrivate->AttemptCount++;
1183
1184 if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
1185 mPrivate->MpioCount++;
1186 mPrivate->EnableMpio = TRUE;
1187
1188 if (AttemptConfigData->AuthenticationType == ISCSI_AUTH_TYPE_KRB) {
1189 mPrivate->Krb5MpioCount++;
1190 }
1191 } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) {
1192 mPrivate->SinglePathCount++;
1193 }
1194 }
1195
1196 //
1197 // Reorder the AttemptConfig by the configured order.
1198 //
1199 for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {
1200 AttemptConfigData = IScsiConfigGetAttemptByConfigIndex (AttemptConfigOrder[Index]);
1201 if (AttemptConfigData == NULL) {
1202 continue;
1203 }
1204
1205 RemoveEntryList (&AttemptConfigData->Link);
1206 InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);
1207 }
1208
1209 //
1210 // Update the Main Form.
1211 //
1212 IScsiConfigUpdateAttempt ();
1213
1214 FreePool (AttemptConfigOrder);
1215
1216 //
1217 // There should be at least one attempt configuration.
1218 //
1219 if (!mPrivate->EnableMpio) {
1220 if (mPrivate->SinglePathCount == 0) {
1221 return EFI_NOT_FOUND;
1222 }
1223 mPrivate->ValidSinglePathCount = mPrivate->SinglePathCount;
1224 }
1225
1226 return EFI_SUCCESS;
1227 }
1228
1229
1230 /**
1231 Get the device path of the iSCSI tcp connection and update it.
1232
1233 @param Session The iSCSI session.
1234
1235 @return The updated device path.
1236 @retval NULL Other errors as indicated.
1237
1238 **/
1239 EFI_DEVICE_PATH_PROTOCOL *
1240 IScsiGetTcpConnDevicePath (
1241 IN ISCSI_SESSION *Session
1242 )
1243 {
1244 ISCSI_CONNECTION *Conn;
1245 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
1246 EFI_STATUS Status;
1247 EFI_DEV_PATH *DPathNode;
1248
1249 if (Session->State != SESSION_STATE_LOGGED_IN) {
1250 return NULL;
1251 }
1252
1253 Conn = NET_LIST_USER_STRUCT_S (
1254 Session->Conns.ForwardLink,
1255 ISCSI_CONNECTION,
1256 Link,
1257 ISCSI_CONNECTION_SIGNATURE
1258 );
1259
1260 Status = gBS->HandleProtocol (
1261 Conn->TcpIo.Handle,
1262 &gEfiDevicePathProtocolGuid,
1263 (VOID **) &DevicePath
1264 );
1265 if (EFI_ERROR (Status)) {
1266 return NULL;
1267 }
1268 //
1269 // Duplicate it.
1270 //
1271 DevicePath = DuplicateDevicePath (DevicePath);
1272 if (DevicePath == NULL) {
1273 return NULL;
1274 }
1275
1276 DPathNode = (EFI_DEV_PATH *) DevicePath;
1277
1278 while (!IsDevicePathEnd (&DPathNode->DevPath)) {
1279 if (DevicePathType (&DPathNode->DevPath) == MESSAGING_DEVICE_PATH) {
1280 if (!Conn->Ipv6Flag && DevicePathSubType (&DPathNode->DevPath) == MSG_IPv4_DP) {
1281 DPathNode->Ipv4.LocalPort = 0;
1282 DPathNode->Ipv4.StaticIpAddress = (BOOLEAN) !Session->ConfigData->SessionConfigData.InitiatorInfoFromDhcp;
1283 break;
1284 } else if (Conn->Ipv6Flag && DevicePathSubType (&DPathNode->DevPath) == MSG_IPv6_DP) {
1285 DPathNode->Ipv6.LocalPort = 0;
1286 DPathNode->Ipv6.StaticIpAddress = (BOOLEAN) !Session->ConfigData->SessionConfigData.InitiatorInfoFromDhcp;
1287 break;
1288 }
1289 }
1290
1291 DPathNode = (EFI_DEV_PATH *) NextDevicePathNode (&DPathNode->DevPath);
1292 }
1293
1294 return DevicePath;
1295 }
1296
1297
1298 /**
1299 Abort the session when the transition from BS to RT is initiated.
1300
1301 @param[in] Event The event signaled.
1302 @param[in] Context The iSCSI driver data.
1303
1304 **/
1305 VOID
1306 EFIAPI
1307 IScsiOnExitBootService (
1308 IN EFI_EVENT Event,
1309 IN VOID *Context
1310 )
1311 {
1312 ISCSI_DRIVER_DATA *Private;
1313
1314 Private = (ISCSI_DRIVER_DATA *) Context;
1315 gBS->CloseEvent (Private->ExitBootServiceEvent);
1316
1317 if (Private->Session != NULL) {
1318 IScsiSessionAbort (Private->Session);
1319 }
1320 }