1/** @file
2  Support for PxeBc dhcp functions.
3
4Copyright (c) 2013, Red Hat, Inc.
5Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.<BR>
6This program and the accompanying materials
7are licensed and made available under the terms and conditions of the BSD License
8which accompanies this distribution.  The full text of the license may be found at
9http://opensource.org/licenses/bsd-license.php
10
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14**/
15
16
17#include "PxeBcImpl.h"
18
19//
20// This is a map from the interested DHCP4 option tags' index to the tag value.
21//
22UINT8 mInterestedDhcp4Tags[PXEBC_DHCP4_TAG_INDEX_MAX] = {
23  PXEBC_DHCP4_TAG_BOOTFILE_LEN,
24  PXEBC_DHCP4_TAG_VENDOR,
25  PXEBC_DHCP4_TAG_OVERLOAD,
26  PXEBC_DHCP4_TAG_MSG_TYPE,
27  PXEBC_DHCP4_TAG_SERVER_ID,
28  PXEBC_DHCP4_TAG_CLASS_ID,
29  PXEBC_DHCP4_TAG_BOOTFILE
30};
31
32
33/**
34  This function initialize the DHCP4 message instance.
35
36  This function will pad each item of dhcp4 message packet.
37
38  @param  Seed    Pointer to the message instance of the DHCP4 packet.
39  @param  Udp4    Pointer to the EFI_UDP4_PROTOCOL instance.
40
41**/
42VOID
43PxeBcInitSeedPacket (
44  IN EFI_DHCP4_PACKET  *Seed,
45  IN EFI_UDP4_PROTOCOL *Udp4
46  )
47{
48  EFI_SIMPLE_NETWORK_MODE Mode;
49  EFI_DHCP4_HEADER        *Header;
50
51  Udp4->GetModeData (Udp4, NULL, NULL, NULL, &Mode);
52
53  Seed->Size    = sizeof (EFI_DHCP4_PACKET);
54  Seed->Length  = sizeof (Seed->Dhcp4);
55
56  Header        = &Seed->Dhcp4.Header;
57
58  ZeroMem (Header, sizeof (EFI_DHCP4_HEADER));
59  Header->OpCode    = PXEBC_DHCP4_OPCODE_REQUEST;
60  Header->HwType    = Mode.IfType;
61  Header->HwAddrLen = (UINT8) Mode.HwAddressSize;
62  CopyMem (Header->ClientHwAddr, &Mode.CurrentAddress, Header->HwAddrLen);
63
64  Seed->Dhcp4.Magik     = PXEBC_DHCP4_MAGIC;
65  Seed->Dhcp4.Option[0] = PXEBC_DHCP4_TAG_EOP;
66}
67
68
69/**
70  Copy the DCHP4 packet from srouce to destination.
71
72  @param  Dst   Pointer to the EFI_DHCP4_PROTOCOL instance.
73  @param  Src   Pointer to the EFI_DHCP4_PROTOCOL instance.
74
75**/
76VOID
77PxeBcCopyEfiDhcp4Packet (
78  IN EFI_DHCP4_PACKET  *Dst,
79  IN EFI_DHCP4_PACKET  *Src
80  )
81{
82  ASSERT (Dst->Size >= Src->Length);
83
84  CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length);
85  Dst->Length = Src->Length;
86}
87
88
89/**
90  Copy the dhcp4 packet to the PxeBc private data and parse the dhcp4 packet.
91
92  @param  Private       Pointer to PxeBc private data.
93  @param  OfferIndex    Index of cached packets as complements of pxe mode data,
94                        the index is maximum offer number.
95
96**/
97VOID
98PxeBcCopyProxyOffer (
99  IN PXEBC_PRIVATE_DATA  *Private,
100  IN UINT32              OfferIndex
101  )
102{
103  EFI_PXE_BASE_CODE_MODE  *Mode;
104  EFI_DHCP4_PACKET        *Offer;
105
106  ASSERT (OfferIndex < Private->NumOffers);
107  ASSERT (OfferIndex < PXEBC_MAX_OFFER_NUM);
108
109  Mode  = Private->PxeBc.Mode;
110  Offer = &Private->Dhcp4Offers[OfferIndex].Packet.Offer;
111
112  PxeBcCopyEfiDhcp4Packet (&Private->ProxyOffer.Packet.Offer, Offer);
113  CopyMem (&Mode->ProxyOffer, &Offer->Dhcp4, Offer->Length);
114  Mode->ProxyOfferReceived = TRUE;
115
116  PxeBcParseCachedDhcpPacket (&Private->ProxyOffer);
117}
118
119
120/**
121  Parse the cached dhcp packet.
122
123  @param  CachedPacket  Pointer to cached dhcp packet.
124
125  @retval TRUE          Succeed to parse and validation.
126  @retval FALSE         Fail to parse or validation.
127
128**/
129BOOLEAN
130PxeBcParseCachedDhcpPacket (
131  IN PXEBC_CACHED_DHCP4_PACKET  *CachedPacket
132  )
133{
134  EFI_DHCP4_PACKET        *Offer;
135  EFI_DHCP4_PACKET_OPTION **Options;
136  EFI_DHCP4_PACKET_OPTION *Option;
137  UINT8                   OfferType;
138  UINTN                   Index;
139  UINT8                   *Ptr8;
140
141  CachedPacket->IsPxeOffer = FALSE;
142  ZeroMem (CachedPacket->Dhcp4Option, sizeof (CachedPacket->Dhcp4Option));
143  ZeroMem (&CachedPacket->PxeVendorOption, sizeof (CachedPacket->PxeVendorOption));
144
145  Offer   = &CachedPacket->Packet.Offer;
146  Options = CachedPacket->Dhcp4Option;
147
148  //
149  // Parse interested dhcp options and store their pointers in CachedPacket->Dhcp4Option.
150  // First, try to parse DHCPv4 options from the DHCP optional parameters field.
151  //
152  for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {
153    Options[Index] = PxeBcParseExtendOptions (
154                       Offer->Dhcp4.Option,
155                       GET_OPTION_BUFFER_LEN (Offer),
156                       mInterestedDhcp4Tags[Index]
157                       );
158  }
159  //
160  // Second, Check if bootfilename and serverhostname is overloaded to carry DHCP options refers to rfc-2132.
161  // If yes, try to parse options from the BootFileName field, then ServerName field.
162  //
163  Option = Options[PXEBC_DHCP4_TAG_INDEX_OVERLOAD];
164  if (Option != NULL) {
165    if ((Option->Data[0] & PXEBC_DHCP4_OVERLOAD_FILE) != 0) {
166      for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {
167        if (Options[Index] == NULL) {
168          Options[Index] = PxeBcParseExtendOptions (
169                             (UINT8 *) Offer->Dhcp4.Header.BootFileName,
170                             sizeof (Offer->Dhcp4.Header.BootFileName),
171                             mInterestedDhcp4Tags[Index]
172                             );
173        }
174      }
175    }
176    if ((Option->Data[0] & PXEBC_DHCP4_OVERLOAD_SERVER_NAME) != 0) {
177      for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {
178        if (Options[Index] == NULL) {
179          Options[Index] = PxeBcParseExtendOptions (
180                             (UINT8 *) Offer->Dhcp4.Header.ServerName,
181                             sizeof (Offer->Dhcp4.Header.ServerName),
182                             mInterestedDhcp4Tags[Index]
183                             );
184        }
185      }
186    }
187  }
188
189  //
190  // Check whether is an offer with PXEClient or not.
191  //
192  Option = Options[PXEBC_DHCP4_TAG_INDEX_CLASS_ID];
193  if ((Option != NULL) && (Option->Length >= 9) &&
194    (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) {
195
196    CachedPacket->IsPxeOffer = TRUE;
197  }
198
199  //
200  // Parse pxe vendor options and store their content/pointers in CachedPacket->PxeVendorOption.
201  //
202  Option = Options[PXEBC_DHCP4_TAG_INDEX_VENDOR];
203  if (CachedPacket->IsPxeOffer && (Option != NULL)) {
204
205    if (!PxeBcParseVendorOptions (Option, &CachedPacket->PxeVendorOption)) {
206      return FALSE;
207    }
208  }
209
210
211  //
212  // Parse PXE boot file name:
213  // According to PXE spec, boot file name should be read from DHCP option 67 (bootfile name) if present.
214  // Otherwise, read from boot file field in DHCP header.
215  //
216  if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
217    //
218    // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null
219    // terminated string. So force to append null terminated character at the end of string.
220    //
221    Ptr8 =  (UINT8*)&Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data[0];
222    Ptr8 += Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Length;
223    if (*(Ptr8 - 1) != '\0') {
224      *Ptr8 = '\0';
225    }
226  } else if (Offer->Dhcp4.Header.BootFileName[0] != 0) {
227    //
228    // If the bootfile is not present and bootfilename is present in dhcp packet, just parse it.
229    // And do not count dhcp option header, or else will destroy the serverhostname.
230    //
231    Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *) (&Offer->Dhcp4.Header.BootFileName[0] -
232                                            OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0]));
233
234  }
235
236  //
237  // Determine offer type of the dhcp packet.
238  //
239  Option = Options[PXEBC_DHCP4_TAG_INDEX_MSG_TYPE];
240  if ((Option == NULL) || (Option->Data[0] == 0)) {
241    //
242    // It's a bootp offer
243    //
244    Option = CachedPacket->Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE];
245    if (Option == NULL) {
246      //
247      // bootp offer without bootfilename, discard it.
248      //
249      return FALSE;
250    }
251
252    OfferType = DHCP4_PACKET_TYPE_BOOTP;
253
254  } else {
255
256    if (IS_VALID_DISCOVER_VENDOR_OPTION (CachedPacket->PxeVendorOption.BitMap)) {
257      //
258      // It's a pxe10 offer with PXEClient and discover vendor option.
259      //
260      OfferType = DHCP4_PACKET_TYPE_PXE10;
261    } else if (IS_VALID_MTFTP_VENDOR_OPTION (CachedPacket->PxeVendorOption.BitMap)) {
262      //
263      // It's a wfm11a offer with PXEClient and mtftp vendor option, and
264      // return false since mtftp not supported currently.
265      //
266      return FALSE;
267    } else {
268      //
269      // If the binl offer with only PXEClient.
270      //
271      OfferType = (UINT8) ((CachedPacket->IsPxeOffer) ? DHCP4_PACKET_TYPE_BINL : DHCP4_PACKET_TYPE_DHCP_ONLY);
272    }
273  }
274
275  CachedPacket->OfferType = OfferType;
276
277  return TRUE;
278}
279
280
281/**
282  Offer dhcp service with a BINL dhcp offer.
283
284  @param  Private   Pointer to PxeBc private data.
285  @param  Index     Index of cached packets as complements of pxe mode data,
286                    the index is maximum offer number.
287
288  @retval TRUE      Offer the service successfully under priority BINL.
289  @retval FALSE     Boot Service failed, parse cached dhcp packet failed or this
290                    BINL ack cannot find options set or bootfile name specified.
291
292**/
293BOOLEAN
294PxeBcTryBinl (
295  IN PXEBC_PRIVATE_DATA  *Private,
296  IN UINT32              Index
297  )
298{
299  EFI_DHCP4_PACKET          *Offer;
300  EFI_IP_ADDRESS            ServerIp;
301  EFI_STATUS                Status;
302  PXEBC_CACHED_DHCP4_PACKET *CachedPacket;
303  EFI_DHCP4_PACKET          *Reply;
304
305  ASSERT (Index < PXEBC_MAX_OFFER_NUM);
306  ASSERT (Private->Dhcp4Offers[Index].OfferType == DHCP4_PACKET_TYPE_BINL);
307
308  Offer = &Private->Dhcp4Offers[Index].Packet.Offer;
309
310  //
311  // Use siaddr(next server) in DHCPOFFER packet header, if zero, use option 54(server identifier)
312  // in DHCPOFFER packet.
313  // (It does not comply with PXE Spec, Ver2.1)
314  //
315  if (EFI_IP4_EQUAL (&Offer->Dhcp4.Header.ServerAddr.Addr, &mZeroIp4Addr)) {
316    CopyMem (
317      &ServerIp.Addr[0],
318      Private->Dhcp4Offers[Index].Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,
319      sizeof (EFI_IPv4_ADDRESS)
320      );
321  } else {
322    CopyMem (
323      &ServerIp.Addr[0],
324      &Offer->Dhcp4.Header.ServerAddr,
325      sizeof (EFI_IPv4_ADDRESS)
326      );
327  }
328  if (ServerIp.Addr[0] == 0) {
329    return FALSE;
330  }
331
332  CachedPacket = &Private->ProxyOffer;
333  Reply        = &CachedPacket->Packet.Offer;
334
335  Status = PxeBcDiscvBootService (
336            Private,
337            0,
338            NULL,
339            FALSE,
340            &ServerIp,
341            0,
342            NULL,
343            FALSE,
344            Reply
345            );
346  if (EFI_ERROR (Status)) {
347    return FALSE;
348  }
349
350  if (!PxeBcParseCachedDhcpPacket (CachedPacket)) {
351    return FALSE;
352  }
353
354  if ((CachedPacket->OfferType != DHCP4_PACKET_TYPE_PXE10) &&
355      (CachedPacket->Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL)) {
356    //
357    // This BINL ack doesn't have discovery options set or bootfile name
358    // specified.
359    //
360    return FALSE;
361  }
362
363  Private->PxeBc.Mode->ProxyOfferReceived = TRUE;
364  CopyMem (&Private->PxeBc.Mode->ProxyOffer, &Reply->Dhcp4, Reply->Length);
365
366  return TRUE;
367}
368
369
370/**
371  Offer dhcp service for each proxy with a BINL dhcp offer.
372
373  @param  Private     Pointer to PxeBc private data
374  @param  OfferIndex  Pointer to the index of cached packets as complements of
375                      pxe mode data, the index is maximum offer number.
376
377  @return If there is no service needed offer return FALSE, otherwise TRUE.
378
379**/
380BOOLEAN
381PxeBcTryBinlProxy (
382  IN  PXEBC_PRIVATE_DATA  *Private,
383  OUT UINT32              *OfferIndex
384  )
385{
386  UINT32  Index;
387
388  for (Index = 0; Index < Private->ProxyIndex[DHCP4_PACKET_TYPE_BINL]; Index++) {
389
390    *OfferIndex = Private->BinlIndex[Index];
391    //
392    // Try this BINL proxy offer
393    //
394    if (PxeBcTryBinl (Private, *OfferIndex)) {
395      return TRUE;
396    }
397  }
398
399  return FALSE;
400}
401
402
403/**
404  This function is to check the selected proxy offer (include BINL dhcp offer and
405  DHCP_ONLY offer ) and set the flag and copy the DHCP packets to the Pxe base code
406  mode structure.
407
408  @param  Private          Pointer to PxeBc private data.
409
410  @retval EFI_SUCCESS      Operational successful.
411  @retval EFI_NO_RESPONSE  Offer dhcp service failed.
412
413**/
414EFI_STATUS
415PxeBcCheckSelectedOffer (
416  IN PXEBC_PRIVATE_DATA  *Private
417  )
418{
419  PXEBC_CACHED_DHCP4_PACKET *SelectedOffer;
420  EFI_DHCP4_PACKET_OPTION   **Options;
421  UINT32                    Index;
422  EFI_DHCP4_PACKET          *Offer;
423  UINT32                    ProxyOfferIndex;
424  EFI_STATUS                Status;
425  EFI_PXE_BASE_CODE_MODE    *Mode;
426  EFI_DHCP4_PACKET          *Ack;
427
428  ASSERT (Private->SelectedOffer != 0);
429
430  Status        = EFI_SUCCESS;
431  SelectedOffer = &Private->Dhcp4Offers[Private->SelectedOffer - 1];
432  Options       = SelectedOffer->Dhcp4Option;
433
434  if (SelectedOffer->OfferType == DHCP4_PACKET_TYPE_BINL) {
435    //
436    // The addresses are acquired from a BINL dhcp offer, try BINL to get
437    // the bootfile name
438    //
439    if (!PxeBcTryBinl (Private, Private->SelectedOffer - 1)) {
440      Status = EFI_NO_RESPONSE;
441    }
442  } else if (SelectedOffer->OfferType == DHCP4_PACKET_TYPE_DHCP_ONLY) {
443    //
444    // The selected offer to finish the D.O.R.A. is a DHCP only offer, we need
445    // try proxy offers if there are some, othewise the bootfile name must be
446    // set in this DHCP only offer.
447    //
448    if (Private->GotProxyOffer) {
449      //
450      // Get rid of the compiler warning.
451      //
452      ProxyOfferIndex = 0;
453      if (Private->SortOffers) {
454        //
455        // The offers are sorted before selecting, the proxy offer type must be
456        // already determined.
457        //
458        ASSERT (Private->ProxyIndex[Private->ProxyOfferType] > 0);
459
460        if (Private->ProxyOfferType == DHCP4_PACKET_TYPE_BINL) {
461          //
462          // We buffer all received BINL proxy offers, try them all one by one
463          //
464          if (!PxeBcTryBinlProxy (Private, &ProxyOfferIndex)) {
465            Status = EFI_NO_RESPONSE;
466          }
467        } else {
468          //
469          // For other types, only one proxy offer is buffered.
470          //
471          ProxyOfferIndex = Private->ProxyIndex[Private->ProxyOfferType] - 1;
472        }
473      } else {
474        //
475        // The proxy offer type is not determined, choose proxy offer in the
476        // received order.
477        //
478        Status = EFI_NO_RESPONSE;
479
480        ASSERT (Private->NumOffers < PXEBC_MAX_OFFER_NUM);
481        for (Index = 0; Index < Private->NumOffers; Index++) {
482
483          Offer = &Private->Dhcp4Offers[Index].Packet.Offer;
484          if (!IS_PROXY_DHCP_OFFER (Offer)) {
485            //
486            // Skip non proxy dhcp offers.
487            //
488            continue;
489          }
490
491          if (Private->Dhcp4Offers[Index].OfferType == DHCP4_PACKET_TYPE_BINL) {
492            //
493            // Try BINL
494            //
495            if (!PxeBcTryBinl (Private, Index)) {
496              //
497              // Failed, skip to the next offer
498              //
499              continue;
500            }
501          }
502
503          Private->ProxyOfferType = Private->Dhcp4Offers[Index].OfferType;
504          ProxyOfferIndex         = Index;
505          Status                  = EFI_SUCCESS;
506          break;
507        }
508      }
509
510      if (!EFI_ERROR (Status) && (Private->ProxyOfferType != DHCP4_PACKET_TYPE_BINL)) {
511        //
512        // Copy the proxy offer to Mode and set the flag
513        //
514        PxeBcCopyProxyOffer (Private, ProxyOfferIndex);
515      }
516    } else {
517      //
518      // No proxy offer is received, the bootfile name MUST be set.
519      //
520      ASSERT (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);
521    }
522  }
523
524  if (!EFI_ERROR (Status)) {
525    //
526    // Everything is OK, set the flag and copy the DHCP packets.
527    //
528    Mode  = Private->PxeBc.Mode;
529    Offer = &SelectedOffer->Packet.Offer;
530
531    //
532    // The discover packet is already copied, just set flag here.
533    //
534    Mode->DhcpDiscoverValid = TRUE;
535
536    Ack                     = &Private->Dhcp4Ack.Packet.Ack;
537    if (SelectedOffer->OfferType == DHCP4_PACKET_TYPE_BOOTP) {
538      //
539      // Other type of ACK is already cached. Bootp is special that we should
540      // use the bootp reply as the ACK and put it into the DHCP_ONLY buffer.
541      //
542      PxeBcCopyEfiDhcp4Packet (&Private->Dhcp4Ack.Packet.Ack, Offer);
543    }
544
545    PxeBcParseCachedDhcpPacket (&Private->Dhcp4Ack);
546
547    Mode->DhcpAckReceived = TRUE;
548
549    //
550    // Copy the dhcp ack.
551    //
552    CopyMem (&Mode->DhcpAck, &Ack->Dhcp4, Ack->Length);
553  }
554
555  return Status;
556}
557
558
559/**
560  Cache the Dhcp4 packet offer, Parse and validate each option of the packet.
561
562  @param  Private    Pointer to PxeBc private data.
563  @param  RcvdOffer  Pointer to the received Dhcp proxy offer packet.
564
565**/
566VOID
567PxeBcCacheDhcpOffer (
568  IN PXEBC_PRIVATE_DATA  *Private,
569  IN EFI_DHCP4_PACKET    *RcvdOffer
570  )
571{
572  PXEBC_CACHED_DHCP4_PACKET *CachedOffer;
573  EFI_DHCP4_PACKET          *Offer;
574  UINT8                     OfferType;
575
576  CachedOffer = &Private->Dhcp4Offers[Private->NumOffers];
577  Offer       = &CachedOffer->Packet.Offer;
578
579  //
580  // Cache the orignal dhcp packet
581  //
582  PxeBcCopyEfiDhcp4Packet (Offer, RcvdOffer);
583
584  //
585  // Parse and validate the options (including dhcp option and vendor option)
586  //
587  if (!PxeBcParseCachedDhcpPacket (CachedOffer)) {
588    return ;
589  }
590
591  OfferType = CachedOffer->OfferType;
592  if (OfferType >= DHCP4_PACKET_TYPE_MAX) {
593    return ;
594  }
595
596  if (OfferType == DHCP4_PACKET_TYPE_BOOTP) {
597
598    if (Private->BootpIndex != 0) {
599      //
600      // Only cache the first bootp offer, discard others.
601      //
602      return ;
603    } else {
604      //
605      // Take as a dhcp only offer, but record index specifically.
606      //
607      Private->BootpIndex = Private->NumOffers + 1;
608    }
609  } else {
610
611    if (IS_PROXY_DHCP_OFFER (Offer)) {
612      //
613      // It's a proxy dhcp offer with no your address, including pxe10, wfm11a or binl offer.
614      //
615      Private->GotProxyOffer = TRUE;
616
617      if (OfferType == DHCP4_PACKET_TYPE_BINL) {
618        //
619        // Cache all binl offers.
620        //
621        Private->BinlIndex[Private->ProxyIndex[DHCP4_PACKET_TYPE_BINL]] = Private->NumOffers;
622        Private->ProxyIndex[DHCP4_PACKET_TYPE_BINL]++;
623      } else if (Private->ProxyIndex[OfferType] != 0) {
624        //
625        // Only cache the first pxe10/wfm11a offers each, discard the others.
626        //
627        return ;
628      } else {
629        //
630        // Record index of the proxy dhcp offer with type other than binl.
631        //
632        Private->ProxyIndex[OfferType] = Private->NumOffers + 1;
633      }
634    } else {
635      //
636      // It's a dhcp offer with your address.
637      //
638      ASSERT (Private->ServerCount[OfferType] < PXEBC_MAX_OFFER_NUM);
639      Private->OfferIndex[OfferType][Private->ServerCount[OfferType]] = Private->NumOffers;
640      Private->ServerCount[OfferType]++;
641    }
642  }
643
644  //
645  // Count the accepted offers.
646  //
647  Private->NumOffers++;
648}
649
650/**
651  Switch the Ip4 policy to static.
652
653  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
654
655  @retval     EFI_SUCCESS         The policy is already configured to static.
656  @retval     Others              Other error as indicated..
657
658**/
659EFI_STATUS
660PxeBcSetIp4Policy (
661  IN PXEBC_PRIVATE_DATA            *Private
662  )
663{
664  EFI_STATUS                   Status;
665  EFI_IP4_CONFIG2_PROTOCOL     *Ip4Config2;
666  EFI_IP4_CONFIG2_POLICY       Policy;
667  UINTN                        DataSize;
668
669  Ip4Config2 = Private->Ip4Config2;
670  DataSize = sizeof (EFI_IP4_CONFIG2_POLICY);
671  Status = Ip4Config2->GetData (
672                       Ip4Config2,
673                       Ip4Config2DataTypePolicy,
674                       &DataSize,
675                       &Policy
676                       );
677  if (EFI_ERROR (Status)) {
678    return Status;
679  }
680
681  if (Policy != Ip4Config2PolicyStatic) {
682    Policy = Ip4Config2PolicyStatic;
683    Status= Ip4Config2->SetData (
684                          Ip4Config2,
685                          Ip4Config2DataTypePolicy,
686                          sizeof (EFI_IP4_CONFIG2_POLICY),
687                          &Policy
688                          );
689    if (EFI_ERROR (Status)) {
690      return Status;
691    }
692  }
693
694  return  EFI_SUCCESS;
695}
696
697
698/**
699  Select the specified proxy offer, such as BINL, DHCP_ONLY and so on.
700  If the proxy does not exist, try offers with bootfile.
701
702  @param  Private   Pointer to PxeBc private data.
703
704**/
705VOID
706PxeBcSelectOffer (
707  IN PXEBC_PRIVATE_DATA  *Private
708  )
709{
710  UINT32            Index;
711  UINT32            OfferIndex;
712  EFI_DHCP4_PACKET  *Offer;
713
714  Private->SelectedOffer = 0;
715
716  if (Private->SortOffers) {
717    //
718    // Select offer according to the priority
719    //
720    if (Private->ServerCount[DHCP4_PACKET_TYPE_PXE10] > 0) {
721      //
722      // DHCP with PXE10
723      //
724      Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_PXE10][0] + 1;
725
726    } else if (Private->ServerCount[DHCP4_PACKET_TYPE_WFM11A] > 0) {
727      //
728      // DHCP with WfM
729      //
730      Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_WFM11A][0] + 1;
731
732    } else if ((Private->ProxyIndex[DHCP4_PACKET_TYPE_PXE10] > 0) &&
733             (Private->ServerCount[DHCP4_PACKET_TYPE_DHCP_ONLY] > 0)
734            ) {
735      //
736      // DHCP only and proxy DHCP with PXE10
737      //
738      Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_DHCP_ONLY][0] + 1;
739      Private->ProxyOfferType     = DHCP4_PACKET_TYPE_PXE10;
740
741    } else if ((Private->ProxyIndex[DHCP4_PACKET_TYPE_WFM11A] > 0) &&
742             (Private->ServerCount[DHCP4_PACKET_TYPE_DHCP_ONLY] > 0)
743            ) {
744      //
745      // DHCP only and proxy DHCP with WfM
746      //
747      Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_DHCP_ONLY][0] + 1;
748      Private->ProxyOfferType     = DHCP4_PACKET_TYPE_WFM11A;
749
750    } else if (Private->ServerCount[DHCP4_PACKET_TYPE_BINL] > 0) {
751      //
752      // DHCP with BINL
753      //
754      Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_BINL][0] + 1;
755
756    } else if ((Private->ProxyIndex[DHCP4_PACKET_TYPE_BINL] > 0) &&
757             (Private->ServerCount[DHCP4_PACKET_TYPE_DHCP_ONLY] > 0)
758            ) {
759      //
760      // DHCP only and proxy DHCP with BINL
761      //
762      Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_DHCP_ONLY][0] + 1;
763      Private->ProxyOfferType     = DHCP4_PACKET_TYPE_BINL;
764
765    } else {
766      //
767      // Try offers with bootfile
768      //
769      for (Index = 0; Index < Private->ServerCount[DHCP4_PACKET_TYPE_DHCP_ONLY]; Index++) {
770        //
771        // Select the first DHCP only offer with bootfile
772        //
773        OfferIndex = Private->OfferIndex[DHCP4_PACKET_TYPE_DHCP_ONLY][Index];
774        if (Private->Dhcp4Offers[OfferIndex].Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
775          Private->SelectedOffer = OfferIndex + 1;
776          break;
777        }
778      }
779
780      if (Private->SelectedOffer == 0) {
781        //
782        // Select the Bootp reply with bootfile if any
783        //
784        Private->SelectedOffer = Private->BootpIndex;
785      }
786    }
787  } else {
788    //
789    // Try the offers in the received order.
790    //
791    for (Index = 0; Index < Private->NumOffers; Index++) {
792
793      Offer = &Private->Dhcp4Offers[Index].Packet.Offer;
794
795      if (IS_PROXY_DHCP_OFFER (Offer)) {
796        //
797        // Skip proxy offers
798        //
799        continue;
800      }
801
802      if ((Private->Dhcp4Offers[Index].OfferType == DHCP4_PACKET_TYPE_DHCP_ONLY) &&
803          ((!Private->GotProxyOffer) && (Private->Dhcp4Offers[Index].Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL))) {
804        //
805        // DHCP only offer but no proxy offer received and no bootfile option in this offer
806        //
807        continue;
808      }
809
810      Private->SelectedOffer = Index + 1;
811      break;
812    }
813  }
814}
815
816
817/**
818  Callback routine.
819
820  EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver
821  to intercept events that occurred in the configuration process. This structure
822  provides advanced control of each state transition of the DHCP process. The
823  returned status code determines the behavior of the EFI DHCPv4 Protocol driver.
824  There are three possible returned values, which are described in the following
825  table.
826
827  @param  This                  Pointer to the EFI DHCPv4 Protocol instance that is used to
828                                configure this callback function.
829  @param  Context               Pointer to the context that is initialized by
830                                EFI_DHCP4_PROTOCOL.Configure().
831  @param  CurrentState          The current operational state of the EFI DHCPv4 Protocol
832                                driver.
833  @param  Dhcp4Event            The event that occurs in the current state, which usually means a
834                                state transition.
835  @param  Packet                The DHCP packet that is going to be sent or already received.
836  @param  NewPacket             The packet that is used to replace the above Packet.
837
838  @retval EFI_SUCCESS           Tells the EFI DHCPv4 Protocol driver to continue the DHCP process.
839  @retval EFI_NOT_READY         Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol
840                                driver will continue to wait for more DHCPOFFER packets until the retry
841                                timeout expires.
842  @retval EFI_ABORTED           Tells the EFI DHCPv4 Protocol driver to abort the current process and
843                                return to the Dhcp4Init or Dhcp4InitReboot state.
844
845**/
846EFI_STATUS
847EFIAPI
848PxeBcDhcpCallBack (
849  IN EFI_DHCP4_PROTOCOL                * This,
850  IN VOID                              *Context,
851  IN EFI_DHCP4_STATE                   CurrentState,
852  IN EFI_DHCP4_EVENT                   Dhcp4Event,
853  IN EFI_DHCP4_PACKET                  * Packet OPTIONAL,
854  OUT EFI_DHCP4_PACKET                 **NewPacket OPTIONAL
855  )
856{
857  PXEBC_PRIVATE_DATA                  *Private;
858  EFI_PXE_BASE_CODE_MODE              *Mode;
859  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;
860  EFI_DHCP4_PACKET_OPTION             *MaxMsgSize;
861  UINT16                              Value;
862  EFI_STATUS                          Status;
863  BOOLEAN                             Received;
864  EFI_DHCP4_HEADER                    *DhcpHeader;
865
866  if ((Dhcp4Event != Dhcp4RcvdOffer) &&
867      (Dhcp4Event != Dhcp4SelectOffer) &&
868      (Dhcp4Event != Dhcp4SendDiscover) &&
869      (Dhcp4Event != Dhcp4RcvdAck) &&
870      (Dhcp4Event != Dhcp4SendRequest)) {
871    return EFI_SUCCESS;
872  }
873
874  Private   = (PXEBC_PRIVATE_DATA *) Context;
875  Mode      = Private->PxeBc.Mode;
876  Callback  = Private->PxeBcCallback;
877
878  //
879  // Override the Maximum DHCP Message Size.
880  //
881  MaxMsgSize = PxeBcParseExtendOptions (
882                Packet->Dhcp4.Option,
883                GET_OPTION_BUFFER_LEN (Packet),
884                PXEBC_DHCP4_TAG_MAXMSG
885                );
886  if (MaxMsgSize != NULL) {
887    Value = HTONS (PXEBC_DHCP4_MAX_PACKET_SIZE);
888    CopyMem (MaxMsgSize->Data, &Value, sizeof (Value));
889  }
890
891  if ((Dhcp4Event != Dhcp4SelectOffer) && (Callback != NULL)) {
892    Received = (BOOLEAN) ((Dhcp4Event == Dhcp4RcvdOffer) || (Dhcp4Event == Dhcp4RcvdAck));
893    Status = Callback->Callback (
894                        Callback,
895                        Private->Function,
896                        Received,
897                        Packet->Length,
898                        (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp4
899                        );
900    if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
901      return EFI_ABORTED;
902    }
903  }
904
905  Status = EFI_SUCCESS;
906
907  switch (Dhcp4Event) {
908
909  case Dhcp4SendDiscover:
910  case Dhcp4SendRequest:
911    if (Mode->SendGUID) {
912      //
913      // send the system GUID instead of the MAC address as the hardware address
914      // in the DHCP packet header.
915      //
916      DhcpHeader = &Packet->Dhcp4.Header;
917
918      if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) DhcpHeader->ClientHwAddr))) {
919        //
920        // GUID not yet set - send all 0xff's to show programable (via SetVariable)
921        // SetMem(DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof(EFI_GUID), 0xff);
922        // GUID not yet set - send all 0's to show not programable
923        //
924        ZeroMem (DhcpHeader->ClientHwAddr, sizeof (EFI_GUID));
925      }
926
927      DhcpHeader->HwAddrLen = (UINT8) sizeof (EFI_GUID);
928    }
929
930    if (Dhcp4Event == Dhcp4SendDiscover) {
931      //
932      // Cache the dhcp discover packet, of which some information will be used later.
933      //
934      CopyMem (Mode->DhcpDiscover.Raw, &Packet->Dhcp4, Packet->Length);
935    }
936
937    break;
938
939  case Dhcp4RcvdOffer:
940    Status = EFI_NOT_READY;
941    if (Private->NumOffers < PXEBC_MAX_OFFER_NUM) {
942      //
943      // Cache the dhcp offers in Private->Dhcp4Offers[]
944      //
945      PxeBcCacheDhcpOffer (Private, Packet);
946    }
947
948    break;
949
950  case Dhcp4SelectOffer:
951    //
952    // Select an offer, if succeeded, Private->SelectedOffer points to
953    // the index of the selected one.
954    //
955    PxeBcSelectOffer (Private);
956
957    if (Private->SelectedOffer == 0) {
958      Status = EFI_ABORTED;
959    } else {
960      *NewPacket = &Private->Dhcp4Offers[Private->SelectedOffer - 1].Packet.Offer;
961    }
962
963    break;
964
965  case Dhcp4RcvdAck:
966    //
967    // Cache Ack
968    //
969    ASSERT (Private->SelectedOffer != 0);
970
971    PxeBcCopyEfiDhcp4Packet (&Private->Dhcp4Ack.Packet.Ack, Packet);
972    break;
973
974  default:
975    break;
976  }
977
978  return Status;
979}
980
981
982/**
983  Initialize the DHCP options and build the option list.
984
985  @param  Private          Pointer to PxeBc private data.
986  @param  OptList          Pointer to a DHCP option list.
987
988  @param  IsDhcpDiscover   Discover dhcp option or not.
989
990  @return The index item number of the option list.
991
992**/
993UINT32
994PxeBcBuildDhcpOptions (
995  IN PXEBC_PRIVATE_DATA            *Private,
996  IN EFI_DHCP4_PACKET_OPTION       **OptList,
997  IN BOOLEAN                       IsDhcpDiscover
998  )
999{
1000  UINT32                    Index;
1001  PXEBC_DHCP4_OPTION_ENTRY  OptEnt;
1002  UINT16                    Value;
1003
1004  Index       = 0;
1005  OptList[0]  = (EFI_DHCP4_PACKET_OPTION *) Private->OptionBuffer;
1006
1007  if (!IsDhcpDiscover) {
1008    //
1009    // Append message type.
1010    //
1011    OptList[Index]->OpCode  = PXEBC_DHCP4_TAG_MSG_TYPE;
1012    OptList[Index]->Length  = 1;
1013    OptEnt.Mesg             = (PXEBC_DHCP4_OPTION_MESG *) OptList[Index]->Data;
1014    OptEnt.Mesg->Type       = PXEBC_DHCP4_MSG_TYPE_REQUEST;
1015    Index++;
1016    OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
1017
1018    //
1019    // Append max message size.
1020    //
1021    OptList[Index]->OpCode  = PXEBC_DHCP4_TAG_MAXMSG;
1022    OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE);
1023    OptEnt.MaxMesgSize      = (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *) OptList[Index]->Data;
1024    Value                   = NTOHS (PXEBC_DHCP4_MAX_PACKET_SIZE);
1025    CopyMem (&OptEnt.MaxMesgSize->Size, &Value, sizeof (UINT16));
1026    Index++;
1027    OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
1028  }
1029  //
1030  // Parameter request list option.
1031  //
1032  OptList[Index]->OpCode    = PXEBC_DHCP4_TAG_PARA_LIST;
1033  OptList[Index]->Length    = 35;
1034  OptEnt.Para               = (PXEBC_DHCP4_OPTION_PARA *) OptList[Index]->Data;
1035  OptEnt.Para->ParaList[0]  = PXEBC_DHCP4_TAG_NETMASK;
1036  OptEnt.Para->ParaList[1]  = PXEBC_DHCP4_TAG_TIME_OFFSET;
1037  OptEnt.Para->ParaList[2]  = PXEBC_DHCP4_TAG_ROUTER;
1038  OptEnt.Para->ParaList[3]  = PXEBC_DHCP4_TAG_TIME_SERVER;
1039  OptEnt.Para->ParaList[4]  = PXEBC_DHCP4_TAG_NAME_SERVER;
1040  OptEnt.Para->ParaList[5]  = PXEBC_DHCP4_TAG_DNS_SERVER;
1041  OptEnt.Para->ParaList[6]  = PXEBC_DHCP4_TAG_HOSTNAME;
1042  OptEnt.Para->ParaList[7]  = PXEBC_DHCP4_TAG_BOOTFILE_LEN;
1043  OptEnt.Para->ParaList[8]  = PXEBC_DHCP4_TAG_DOMAINNAME;
1044  OptEnt.Para->ParaList[9]  = PXEBC_DHCP4_TAG_ROOTPATH;
1045  OptEnt.Para->ParaList[10] = PXEBC_DHCP4_TAG_EXTEND_PATH;
1046  OptEnt.Para->ParaList[11] = PXEBC_DHCP4_TAG_EMTU;
1047  OptEnt.Para->ParaList[12] = PXEBC_DHCP4_TAG_TTL;
1048  OptEnt.Para->ParaList[13] = PXEBC_DHCP4_TAG_BROADCAST;
1049  OptEnt.Para->ParaList[14] = PXEBC_DHCP4_TAG_NIS_DOMAIN;
1050  OptEnt.Para->ParaList[15] = PXEBC_DHCP4_TAG_NIS_SERVER;
1051  OptEnt.Para->ParaList[16] = PXEBC_DHCP4_TAG_NTP_SERVER;
1052  OptEnt.Para->ParaList[17] = PXEBC_DHCP4_TAG_VENDOR;
1053  OptEnt.Para->ParaList[18] = PXEBC_DHCP4_TAG_REQUEST_IP;
1054  OptEnt.Para->ParaList[19] = PXEBC_DHCP4_TAG_LEASE;
1055  OptEnt.Para->ParaList[20] = PXEBC_DHCP4_TAG_SERVER_ID;
1056  OptEnt.Para->ParaList[21] = PXEBC_DHCP4_TAG_T1;
1057  OptEnt.Para->ParaList[22] = PXEBC_DHCP4_TAG_T2;
1058  OptEnt.Para->ParaList[23] = PXEBC_DHCP4_TAG_CLASS_ID;
1059  OptEnt.Para->ParaList[24] = PXEBC_DHCP4_TAG_TFTP;
1060  OptEnt.Para->ParaList[25] = PXEBC_DHCP4_TAG_BOOTFILE;
1061  OptEnt.Para->ParaList[26] = PXEBC_PXE_DHCP4_TAG_UUID;
1062  OptEnt.Para->ParaList[27] = 0x80;
1063  OptEnt.Para->ParaList[28] = 0x81;
1064  OptEnt.Para->ParaList[29] = 0x82;
1065  OptEnt.Para->ParaList[30] = 0x83;
1066  OptEnt.Para->ParaList[31] = 0x84;
1067  OptEnt.Para->ParaList[32] = 0x85;
1068  OptEnt.Para->ParaList[33] = 0x86;
1069  OptEnt.Para->ParaList[34] = 0x87;
1070  Index++;
1071  OptList[Index]            = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
1072
1073  //
1074  // Append UUID/Guid-based client identifier option
1075  //
1076  OptList[Index]->OpCode  = PXEBC_PXE_DHCP4_TAG_UUID;
1077  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UUID);
1078  OptEnt.Uuid             = (PXEBC_DHCP4_OPTION_UUID *) OptList[Index]->Data;
1079  OptEnt.Uuid->Type       = 0;
1080  Index++;
1081  OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
1082
1083  if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) {
1084    //
1085    // GUID not yet set - send all 0xff's to show programable (via SetVariable)
1086    // SetMem(DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof(EFI_GUID), 0xff);
1087    // GUID not yet set - send all 0's to show not programable
1088    //
1089    ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID));
1090  }
1091
1092  //
1093  // Append client network device interface option
1094  //
1095  OptList[Index]->OpCode  = PXEBC_PXE_DHCP4_TAG_UNDI;
1096  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UNDI);
1097  OptEnt.Undi             = (PXEBC_DHCP4_OPTION_UNDI *) OptList[Index]->Data;
1098  if (Private->Nii != NULL) {
1099    OptEnt.Undi->Type       = Private->Nii->Type;
1100    OptEnt.Undi->MajorVer   = Private->Nii->MajorVer;
1101    OptEnt.Undi->MinorVer   = Private->Nii->MinorVer;
1102  } else {
1103    OptEnt.Undi->Type       = DEFAULT_UNDI_TYPE;
1104    OptEnt.Undi->MajorVer   = DEFAULT_UNDI_MAJOR;
1105    OptEnt.Undi->MinorVer   = DEFAULT_UNDI_MINOR;
1106  }
1107
1108  Index++;
1109  OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
1110
1111  //
1112  // Append client system architecture option
1113  //
1114  OptList[Index]->OpCode  = PXEBC_PXE_DHCP4_TAG_ARCH;
1115  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_ARCH);
1116  OptEnt.Arch             = (PXEBC_DHCP4_OPTION_ARCH *) OptList[Index]->Data;
1117  Value                   = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);
1118  CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
1119  Index++;
1120  OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
1121
1122  //
1123  // Append client system architecture option
1124  //
1125  OptList[Index]->OpCode  = PXEBC_DHCP4_TAG_CLASS_ID;
1126  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_CLID);
1127  OptEnt.Clid             = (PXEBC_DHCP4_OPTION_CLID *) OptList[Index]->Data;
1128  CopyMem (OptEnt.Clid, DEFAULT_CLASS_ID_DATA, sizeof (PXEBC_DHCP4_OPTION_CLID));
1129  CvtNum (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE, OptEnt.Clid->ArchitectureType, sizeof (OptEnt.Clid->ArchitectureType));
1130
1131  if (Private->Nii != NULL) {
1132    //
1133    // If NII protocol exists, update DHCP option data
1134    //
1135    CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName));
1136    CvtNum (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor));
1137    CvtNum (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor));
1138  }
1139
1140  Index++;
1141
1142  return Index;
1143}
1144
1145
1146/**
1147  Discover the boot of service and initialize the vendor option if exists.
1148
1149  @param  Private               Pointer to PxeBc private data.
1150  @param  Type                  PxeBc option boot item type
1151  @param  Layer                 PxeBc option boot item layer
1152  @param  UseBis                Use BIS or not
1153  @param  DestIp                Ip address for server
1154  @param  IpCount               The total count of the server ip address
1155  @param  SrvList               Server list
1156  @param  IsDiscv               Discover the vendor or not
1157  @param  Reply                 The dhcp4 packet of Pxe reply
1158
1159  @retval EFI_SUCCESS           Operation succeeds.
1160  @retval EFI_OUT_OF_RESOURCES  Allocate memory pool failed.
1161  @retval EFI_NOT_FOUND         There is no vendor option exists.
1162  @retval EFI_TIMEOUT           Send Pxe Discover time out.
1163
1164**/
1165EFI_STATUS
1166PxeBcDiscvBootService (
1167  IN PXEBC_PRIVATE_DATA                * Private,
1168  IN UINT16                            Type,
1169  IN UINT16                            *Layer,
1170  IN BOOLEAN                           UseBis,
1171  IN EFI_IP_ADDRESS                    * DestIp,
1172  IN UINT16                            IpCount,
1173  IN EFI_PXE_BASE_CODE_SRVLIST         * SrvList,
1174  IN BOOLEAN                           IsDiscv,
1175  OUT EFI_DHCP4_PACKET                 * Reply OPTIONAL
1176  )
1177{
1178  EFI_PXE_BASE_CODE_UDP_PORT          Sport;
1179  EFI_PXE_BASE_CODE_MODE              *Mode;
1180  EFI_DHCP4_PROTOCOL                  *Dhcp4;
1181  EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN    Token;
1182  BOOLEAN                             IsBCast;
1183  EFI_STATUS                          Status;
1184  UINT16                              RepIndex;
1185  UINT16                              SrvIndex;
1186  UINT16                              TryIndex;
1187  EFI_DHCP4_LISTEN_POINT              ListenPoint;
1188  EFI_DHCP4_PACKET                    *Response;
1189  EFI_DHCP4_PACKET_OPTION             *OptList[PXEBC_DHCP4_MAX_OPTION_NUM];
1190  UINT32                              OptCount;
1191  EFI_DHCP4_PACKET_OPTION             *PxeOpt;
1192  PXEBC_OPTION_BOOT_ITEM              *PxeBootItem;
1193  UINT8                               VendorOptLen;
1194  EFI_DHCP4_HEADER                    *DhcpHeader;
1195  UINT32                              Xid;
1196
1197  Mode      = Private->PxeBc.Mode;
1198  Dhcp4     = Private->Dhcp4;
1199  Status    = EFI_SUCCESS;
1200
1201  ZeroMem (&Token, sizeof (EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN));
1202
1203  if (DestIp == NULL) {
1204    Sport   = PXEBC_DHCP4_S_PORT;
1205    IsBCast = TRUE;
1206  } else {
1207    Sport   = PXEBC_BS_DISCOVER_PORT;
1208    IsBCast = FALSE;
1209  }
1210
1211  if (!UseBis && Layer != NULL) {
1212    *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;
1213  }
1214
1215  OptCount = PxeBcBuildDhcpOptions (Private, OptList, FALSE);
1216
1217  if (IsDiscv) {
1218    ASSERT (Layer != NULL);
1219    //
1220    // Add vendor option of PXE_BOOT_ITEM
1221    //
1222    VendorOptLen = (UINT8) ((sizeof (EFI_DHCP4_PACKET_OPTION) - 1) * 2 + sizeof (PXEBC_OPTION_BOOT_ITEM) + 1);
1223    OptList[OptCount] = AllocatePool (VendorOptLen);
1224    if (OptList[OptCount] == NULL) {
1225      return EFI_OUT_OF_RESOURCES;
1226    }
1227
1228    OptList[OptCount]->OpCode     = PXEBC_DHCP4_TAG_VENDOR;
1229    OptList[OptCount]->Length     = (UINT8) (VendorOptLen - 2);
1230    PxeOpt                        = (EFI_DHCP4_PACKET_OPTION *) OptList[OptCount]->Data;
1231    PxeOpt->OpCode                = PXEBC_VENDOR_TAG_BOOT_ITEM;
1232    PxeOpt->Length                = (UINT8) sizeof (PXEBC_OPTION_BOOT_ITEM);
1233    PxeBootItem                   = (PXEBC_OPTION_BOOT_ITEM *) PxeOpt->Data;
1234    PxeBootItem->Type             = HTONS (Type);
1235    PxeBootItem->Layer            = HTONS (*Layer);
1236    PxeOpt->Data[PxeOpt->Length]  = PXEBC_DHCP4_TAG_EOP;
1237
1238    OptCount++;
1239  }
1240
1241  Status = Dhcp4->Build (Dhcp4, &Private->SeedPacket, 0, NULL, OptCount, OptList, &Token.Packet);
1242
1243  if (IsDiscv) {
1244    FreePool (OptList[OptCount - 1]);
1245  }
1246
1247  if (EFI_ERROR (Status)) {
1248    return Status;
1249  }
1250
1251  DhcpHeader = &Token.Packet->Dhcp4.Header;
1252  if (Mode->SendGUID) {
1253    if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) DhcpHeader->ClientHwAddr))) {
1254      //
1255      // GUID not yet set - send all 0's to show not programable
1256      //
1257      ZeroMem (DhcpHeader->ClientHwAddr, sizeof (EFI_GUID));
1258    }
1259
1260    DhcpHeader->HwAddrLen = (UINT8) sizeof (EFI_GUID);
1261  }
1262
1263  Xid                                 = NET_RANDOM (NetRandomInitSeed ());
1264  Token.Packet->Dhcp4.Header.Xid      = HTONL(Xid);
1265  Token.Packet->Dhcp4.Header.Reserved = HTONS((UINT16) ((IsBCast) ? 0x8000 : 0));
1266  CopyMem (&Token.Packet->Dhcp4.Header.ClientAddr, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
1267
1268  Token.RemotePort = Sport;
1269
1270  if (IsBCast) {
1271    SetMem (&Token.RemoteAddress, sizeof (EFI_IPv4_ADDRESS), 0xff);
1272  } else {
1273    CopyMem (&Token.RemoteAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));
1274  }
1275
1276  CopyMem (&Token.GatewayAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));
1277
1278  if (!IsBCast) {
1279    Token.ListenPointCount            = 1;
1280    Token.ListenPoints                = &ListenPoint;
1281    Token.ListenPoints[0].ListenPort  = PXEBC_BS_DISCOVER_PORT;
1282    CopyMem (&Token.ListenPoints[0].ListenAddress, &Private->StationIp, sizeof(EFI_IPv4_ADDRESS));
1283    CopyMem (&Token.ListenPoints[0].SubnetMask, &Private->SubnetMask, sizeof(EFI_IPv4_ADDRESS));
1284  }
1285  //
1286  // Send Pxe Discover
1287  //
1288  for (TryIndex = 1; TryIndex <= PXEBC_BOOT_REQUEST_RETRIES; TryIndex++) {
1289
1290    Token.TimeoutValue                  = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * TryIndex);
1291    Token.Packet->Dhcp4.Header.Seconds  = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * (TryIndex - 1));
1292
1293    Status              = Dhcp4->TransmitReceive (Dhcp4, &Token);
1294
1295    if (Token.Status != EFI_TIMEOUT) {
1296      break;
1297    }
1298  }
1299
1300  if (TryIndex > PXEBC_BOOT_REQUEST_RETRIES) {
1301    //
1302    // No server response our PXE request
1303    //
1304    Status = EFI_TIMEOUT;
1305  }
1306
1307  if (!EFI_ERROR (Status)) {
1308    //
1309    // Find Pxe Reply
1310    //
1311    RepIndex  = 0;
1312    SrvIndex  = 0;
1313    Response  = Token.ResponseList;
1314
1315    while (RepIndex < Token.ResponseCount) {
1316
1317      while (SrvIndex < IpCount) {
1318
1319        if (SrvList[SrvIndex].AcceptAnyResponse) {
1320          break;
1321        }
1322
1323        if ((SrvList[SrvIndex].Type == Type) && EFI_IP4_EQUAL (&(Response->Dhcp4.Header.ServerAddr), &(Private->ServerIp))) {
1324          break;
1325        }
1326
1327        SrvIndex++;
1328      }
1329
1330      if ((IpCount != SrvIndex) || (IpCount == 0)) {
1331        break;
1332      }
1333
1334      SrvIndex = 0;
1335      RepIndex++;
1336
1337      Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size);
1338    }
1339
1340    if (RepIndex < Token.ResponseCount) {
1341
1342      if (Reply != NULL) {
1343        PxeBcCopyEfiDhcp4Packet (Reply, Response);
1344      }
1345
1346      if (IsDiscv) {
1347        CopyMem (&(Mode->PxeDiscover), &(Token.Packet->Dhcp4), Token.Packet->Length);
1348        Mode->PxeDiscoverValid = TRUE;
1349
1350        CopyMem (Mode->PxeReply.Raw, &Response->Dhcp4, Response->Length);
1351        Mode->PxeReplyReceived = TRUE;
1352      }
1353    } else {
1354      Status = EFI_NOT_FOUND;
1355    }
1356
1357    //
1358    // free the responselist
1359    //
1360    if (Token.ResponseList != NULL) {
1361      FreePool (Token.ResponseList);
1362    }
1363  }
1364  //
1365  // Free the dhcp packet
1366  //
1367  FreePool (Token.Packet);
1368
1369  return Status;
1370}
1371
1372
1373/**
1374  Parse interested dhcp options.
1375
1376  @param  Buffer     Pointer to the dhcp options packet.
1377  @param  Length     The length of the dhcp options.
1378  @param  OptTag     The option OpCode.
1379
1380  @return NULL if the buffer length is 0 and OpCode is not
1381          PXEBC_DHCP4_TAG_EOP, or the pointer to the buffer.
1382
1383**/
1384EFI_DHCP4_PACKET_OPTION *
1385PxeBcParseExtendOptions (
1386  IN UINT8                         *Buffer,
1387  IN UINT32                        Length,
1388  IN UINT8                         OptTag
1389  )
1390{
1391  EFI_DHCP4_PACKET_OPTION *Option;
1392  UINT32                  Offset;
1393
1394  Option  = (EFI_DHCP4_PACKET_OPTION *) Buffer;
1395  Offset  = 0;
1396
1397  while (Offset < Length && Option->OpCode != PXEBC_DHCP4_TAG_EOP) {
1398
1399    if (Option->OpCode == OptTag) {
1400
1401      return Option;
1402    }
1403
1404    if (Option->OpCode == PXEBC_DHCP4_TAG_PAD) {
1405      Offset++;
1406    } else {
1407      Offset += Option->Length + 2;
1408    }
1409
1410    Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset);
1411  }
1412
1413  return NULL;
1414}
1415
1416
1417/**
1418  This function is to parse and check vendor options.
1419
1420  @param  Dhcp4Option           Pointer to dhcp options
1421  @param  VendorOption          Pointer to vendor options
1422
1423  @return TRUE if valid for vendor options, or FALSE.
1424
1425**/
1426BOOLEAN
1427PxeBcParseVendorOptions (
1428  IN EFI_DHCP4_PACKET_OPTION       *Dhcp4Option,
1429  IN PXEBC_VENDOR_OPTION           *VendorOption
1430  )
1431{
1432  UINT32                  *BitMap;
1433  UINT8                   VendorOptionLen;
1434  EFI_DHCP4_PACKET_OPTION *PxeOption;
1435  UINT8                   Offset;
1436
1437  BitMap          = VendorOption->BitMap;
1438  VendorOptionLen = Dhcp4Option->Length;
1439  PxeOption       = (EFI_DHCP4_PACKET_OPTION *) &Dhcp4Option->Data[0];
1440  Offset          = 0;
1441
1442  while ((Offset < VendorOptionLen) && (PxeOption->OpCode != PXEBC_DHCP4_TAG_EOP)) {
1443    //
1444    // Parse every Vendor Option and set its BitMap
1445    //
1446    switch (PxeOption->OpCode) {
1447
1448    case PXEBC_VENDOR_TAG_MTFTP_IP:
1449
1450      CopyMem (&VendorOption->MtftpIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
1451      break;
1452
1453    case PXEBC_VENDOR_TAG_MTFTP_CPORT:
1454
1455      CopyMem (&VendorOption->MtftpCPort, PxeOption->Data, sizeof (VendorOption->MtftpCPort));
1456      break;
1457
1458    case PXEBC_VENDOR_TAG_MTFTP_SPORT:
1459
1460      CopyMem (&VendorOption->MtftpSPort, PxeOption->Data, sizeof (VendorOption->MtftpSPort));
1461      break;
1462
1463    case PXEBC_VENDOR_TAG_MTFTP_TIMEOUT:
1464
1465      VendorOption->MtftpTimeout = *PxeOption->Data;
1466      break;
1467
1468    case PXEBC_VENDOR_TAG_MTFTP_DELAY:
1469
1470      VendorOption->MtftpDelay = *PxeOption->Data;
1471      break;
1472
1473    case PXEBC_VENDOR_TAG_DISCOVER_CTRL:
1474
1475      VendorOption->DiscoverCtrl = *PxeOption->Data;
1476      break;
1477
1478    case PXEBC_VENDOR_TAG_DISCOVER_MCAST:
1479
1480      CopyMem (&VendorOption->DiscoverMcastIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
1481      break;
1482
1483    case PXEBC_VENDOR_TAG_BOOT_SERVERS:
1484
1485      VendorOption->BootSvrLen  = PxeOption->Length;
1486      VendorOption->BootSvr     = (PXEBC_BOOT_SVR_ENTRY *) PxeOption->Data;
1487      break;
1488
1489    case PXEBC_VENDOR_TAG_BOOT_MENU:
1490
1491      VendorOption->BootMenuLen = PxeOption->Length;
1492      VendorOption->BootMenu    = (PXEBC_BOOT_MENU_ENTRY *) PxeOption->Data;
1493      break;
1494
1495    case PXEBC_VENDOR_TAG_MENU_PROMPT:
1496
1497      VendorOption->MenuPromptLen = PxeOption->Length;
1498      VendorOption->MenuPrompt    = (PXEBC_MENU_PROMPT *) PxeOption->Data;
1499      break;
1500
1501    case PXEBC_VENDOR_TAG_MCAST_ALLOC:
1502
1503      CopyMem (&VendorOption->McastIpBase, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
1504      CopyMem (&VendorOption->McastIpBlock, PxeOption->Data + 4, sizeof (VendorOption->McastIpBlock));
1505      CopyMem (&VendorOption->McastIpRange, PxeOption->Data + 6, sizeof (VendorOption->McastIpRange));
1506      break;
1507
1508    case PXEBC_VENDOR_TAG_CREDENTIAL_TYPES:
1509
1510      VendorOption->CredTypeLen = PxeOption->Length;
1511      VendorOption->CredType    = (UINT32 *) PxeOption->Data;
1512      break;
1513
1514    case PXEBC_VENDOR_TAG_BOOT_ITEM:
1515
1516      CopyMem (&VendorOption->BootSrvType, PxeOption->Data, sizeof (VendorOption->BootSrvType));
1517      CopyMem (&VendorOption->BootSrvLayer, PxeOption->Data + 2, sizeof (VendorOption->BootSrvLayer));
1518      break;
1519    }
1520
1521    SET_VENDOR_OPTION_BIT_MAP (BitMap, PxeOption->OpCode);
1522
1523    if (PxeOption->OpCode == PXEBC_DHCP4_TAG_PAD) {
1524      Offset++;
1525    } else {
1526      Offset = (UINT8) (Offset + PxeOption->Length + 2);
1527    }
1528
1529    PxeOption = (EFI_DHCP4_PACKET_OPTION *) (Dhcp4Option->Data + Offset);
1530  }
1531
1532  //
1533  // FixMe, return falas if invalid of any vendor option
1534  //
1535
1536  return TRUE;
1537}
1538
1539
1540/**
1541  This function display boot item detail.
1542
1543  If the length of the boot item string over 70 Char, just display 70 Char.
1544
1545  @param  Str     Pointer to a string (boot item string).
1546  @param  Len     The length of string.
1547
1548**/
1549VOID
1550PxeBcDisplayBootItem (
1551  IN UINT8                 *Str,
1552  IN UINT8                 Len
1553  )
1554{
1555  UINT8 Tmp;
1556
1557  Len       = (UINT8) MIN (70, Len);
1558  Tmp       = Str[Len];
1559  Str[Len]  = 0;
1560  AsciiPrint ("%a \n", Str);
1561  Str[Len] = Tmp;
1562}
1563
1564
1565/**
1566  Choose the boot prompt.
1567
1568  @param  Private              Pointer to PxeBc private data.
1569
1570  @retval EFI_SUCCESS          Select boot prompt done.
1571  @retval EFI_TIMEOUT          Select boot prompt time out.
1572  @retval EFI_NOT_FOUND        The proxy offer is not Pxe10.
1573  @retval EFI_ABORTED          User cancel the operation.
1574  @retval EFI_NOT_READY        Read the input key from the keybroad has not finish.
1575
1576**/
1577EFI_STATUS
1578PxeBcSelectBootPrompt (
1579  IN PXEBC_PRIVATE_DATA              *Private
1580  )
1581{
1582  PXEBC_CACHED_DHCP4_PACKET  *Packet;
1583  PXEBC_VENDOR_OPTION       *VendorOpt;
1584  EFI_EVENT                  TimeoutEvent;
1585  EFI_EVENT                  DescendEvent;
1586  EFI_INPUT_KEY              InputKey;
1587  EFI_STATUS                 Status;
1588  UINT8                      Timeout;
1589  UINT8                      *Prompt;
1590  UINT8                      PromptLen;
1591  INT32                      SecCol;
1592  INT32                      SecRow;
1593
1594  TimeoutEvent  = NULL;
1595  DescendEvent  = NULL;
1596
1597  if (Private->PxeBc.Mode->ProxyOfferReceived) {
1598
1599    Packet  = &Private->ProxyOffer;
1600  } else {
1601
1602    Packet  = &Private->Dhcp4Ack;
1603  }
1604
1605  if (Packet->OfferType != DHCP4_PACKET_TYPE_PXE10) {
1606    return EFI_NOT_FOUND;
1607  }
1608
1609  VendorOpt = &Packet->PxeVendorOption;
1610  //
1611  // According to the PXE specification 2.1, Table 2-1 PXE DHCP Options  (Full
1612  // List), we must not consider a boot prompt or boot menu if all of the
1613  // following hold:
1614  // - the PXE_DISCOVERY_CONTROL PXE tag is present inside the Vendor Options
1615  //   (=43) DHCP tag, and
1616  // - the PXE_DISCOVERY_CONTROL PXE tag has bit 3 set, and
1617  // - a boot file name has been presented with DHCP option 67.
1618  //
1619  if (IS_DISABLE_PROMPT_MENU (VendorOpt->DiscoverCtrl) &&
1620      Packet->Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
1621    return EFI_ABORTED;
1622  }
1623
1624  if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) {
1625    return EFI_SUCCESS;
1626  }
1627
1628  Timeout   = VendorOpt->MenuPrompt->Timeout;
1629  Prompt    = VendorOpt->MenuPrompt->Prompt;
1630  PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1);
1631
1632  if (Timeout == 0) {
1633    return EFI_SUCCESS;
1634  }
1635
1636  if (Timeout == 255) {
1637    return EFI_TIMEOUT;
1638  }
1639
1640  Status = gBS->CreateEvent (
1641                  EVT_TIMER,
1642                  TPL_CALLBACK,
1643                  NULL,
1644                  NULL,
1645                  &TimeoutEvent
1646                  );
1647
1648  if (EFI_ERROR (Status)) {
1649    return Status;
1650  }
1651
1652  Status = gBS->SetTimer (
1653                  TimeoutEvent,
1654                  TimerRelative,
1655                  Timeout * TICKS_PER_SECOND
1656                  );
1657
1658  if (EFI_ERROR (Status)) {
1659    goto ON_EXIT;
1660  }
1661
1662  Status = gBS->CreateEvent (
1663                  EVT_TIMER,
1664                  TPL_CALLBACK,
1665                  NULL,
1666                  NULL,
1667                  &DescendEvent
1668                  );
1669
1670  if (EFI_ERROR (Status)) {
1671    goto ON_EXIT;
1672  }
1673
1674  Status = gBS->SetTimer (
1675                  DescendEvent,
1676                  TimerPeriodic,
1677                  TICKS_PER_SECOND
1678                  );
1679
1680  if (EFI_ERROR (Status)) {
1681    goto ON_EXIT;
1682  }
1683
1684  SecCol = gST->ConOut->Mode->CursorColumn;
1685  SecRow = gST->ConOut->Mode->CursorRow;
1686
1687  PxeBcDisplayBootItem (Prompt, PromptLen);
1688
1689  gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
1690  AsciiPrint ("(%d) ", Timeout--);
1691
1692  while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
1693
1694    if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) {
1695      gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
1696      AsciiPrint ("(%d) ", Timeout--);
1697    }
1698
1699    if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
1700
1701      gBS->Stall (10 * TICKS_PER_MS);
1702      continue;
1703    }
1704
1705    if (InputKey.ScanCode == 0) {
1706
1707      switch (InputKey.UnicodeChar) {
1708      case CTRL ('c'):
1709        Status = EFI_ABORTED;
1710        break;
1711
1712      case CTRL ('m'):
1713      case 'm':
1714      case 'M':
1715        Status = EFI_TIMEOUT;
1716        break;
1717
1718      default:
1719        continue;
1720      }
1721    } else {
1722
1723      switch (InputKey.ScanCode) {
1724      case SCAN_F8:
1725        Status = EFI_TIMEOUT;
1726        break;
1727
1728      case SCAN_ESC:
1729        Status = EFI_ABORTED;
1730        break;
1731
1732      default:
1733        continue;
1734      }
1735    }
1736
1737    break;
1738  }
1739
1740  gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1);
1741
1742ON_EXIT:
1743
1744  if (DescendEvent != NULL) {
1745    gBS->CloseEvent (DescendEvent);
1746  }
1747
1748  if (TimeoutEvent != NULL) {
1749    gBS->CloseEvent (TimeoutEvent);
1750  }
1751
1752  return Status;
1753}
1754
1755
1756/**
1757  Select the boot menu.
1758
1759  @param  Private         Pointer to PxeBc private data.
1760  @param  Type            The type of the menu.
1761  @param  UseDefaultItem  Use default item or not.
1762
1763  @retval EFI_ABORTED     User cancel operation.
1764  @retval EFI_SUCCESS     Select the boot menu success.
1765  @retval EFI_NOT_READY   Read the input key from the keybroad has not finish.
1766
1767**/
1768EFI_STATUS
1769PxeBcSelectBootMenu (
1770  IN  PXEBC_PRIVATE_DATA              *Private,
1771  OUT UINT16                          *Type,
1772  IN  BOOLEAN                         UseDefaultItem
1773  )
1774{
1775  PXEBC_CACHED_DHCP4_PACKET  *Packet;
1776  PXEBC_VENDOR_OPTION        *VendorOpt;
1777  EFI_INPUT_KEY              InputKey;
1778  UINT8                      MenuSize;
1779  UINT8                      MenuNum;
1780  INT32                      TopRow;
1781  UINT16                     Select;
1782  UINT16                     LastSelect;
1783  UINT8                      Index;
1784  BOOLEAN                    Finish;
1785  CHAR8                      Blank[70];
1786  PXEBC_BOOT_MENU_ENTRY      *MenuItem;
1787  PXEBC_BOOT_MENU_ENTRY      *MenuArray[PXEBC_MAX_MENU_NUM];
1788
1789  Finish  = FALSE;
1790  Select  = 1;
1791  Index   = 0;
1792  *Type   = 0;
1793
1794  if (Private->PxeBc.Mode->ProxyOfferReceived) {
1795
1796    Packet  = &Private->ProxyOffer;
1797  } else {
1798
1799    Packet  = &Private->Dhcp4Ack;
1800  }
1801
1802  ASSERT (Packet->OfferType == DHCP4_PACKET_TYPE_PXE10);
1803
1804  VendorOpt = &Packet->PxeVendorOption;
1805
1806  if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) {
1807    return EFI_SUCCESS;
1808  }
1809
1810  SetMem (Blank, sizeof(Blank), ' ');
1811
1812  MenuSize  = VendorOpt->BootMenuLen;
1813  MenuItem  = VendorOpt->BootMenu;
1814
1815  if (MenuSize == 0) {
1816    return EFI_NOT_READY;
1817  }
1818
1819  while (MenuSize > 0) {
1820    MenuArray[Index++]  = MenuItem;
1821    MenuSize          = (UINT8) (MenuSize - (MenuItem->DescLen + 3));
1822    MenuItem          = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3);
1823    if (Index >= PXEBC_MAX_MENU_NUM) {
1824      break;
1825    }
1826  }
1827
1828  if (UseDefaultItem) {
1829    *Type = MenuArray[0]->Type;
1830    *Type = NTOHS (*Type);
1831    return EFI_SUCCESS;
1832  }
1833
1834  MenuNum = Index;
1835
1836  for (Index = 0; Index < MenuNum; Index++) {
1837    PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen);
1838  }
1839
1840  TopRow  = gST->ConOut->Mode->CursorRow - MenuNum;
1841
1842  do {
1843    ASSERT (Select < PXEBC_MAX_MENU_NUM);
1844    //
1845    // highlight selected row
1846    //
1847    gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
1848    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select);
1849    Blank[MenuArray[Select]->DescLen] = 0;
1850    AsciiPrint ("%a\r", Blank);
1851    PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen);
1852    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
1853    LastSelect = Select;
1854
1855    while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
1856      gBS->Stall (10 * TICKS_PER_MS);
1857    }
1858
1859    if (InputKey.ScanCode != 0) {
1860      switch (InputKey.UnicodeChar) {
1861      case CTRL ('c'):
1862        InputKey.ScanCode = SCAN_ESC;
1863        break;
1864
1865      case CTRL ('j'):  /* linefeed */
1866      case CTRL ('m'):  /* return */
1867        Finish = TRUE;
1868        break;
1869
1870      case CTRL ('i'):  /* tab */
1871      case ' ':
1872      case 'd':
1873      case 'D':
1874        InputKey.ScanCode = SCAN_DOWN;
1875        break;
1876
1877      case CTRL ('h'):  /* backspace */
1878      case 'u':
1879      case 'U':
1880        InputKey.ScanCode = SCAN_UP;
1881        break;
1882
1883      default:
1884        InputKey.ScanCode = 0;
1885      }
1886    }
1887
1888    switch (InputKey.ScanCode) {
1889    case SCAN_LEFT:
1890    case SCAN_UP:
1891      if (Select > 0) {
1892        --Select;
1893      }
1894
1895      break;
1896
1897    case SCAN_DOWN:
1898    case SCAN_RIGHT:
1899      if (++Select == MenuNum) {
1900        --Select;
1901      }
1902
1903      break;
1904
1905    case SCAN_PAGE_UP:
1906    case SCAN_HOME:
1907      Select = 0;
1908      break;
1909
1910    case SCAN_PAGE_DOWN:
1911    case SCAN_END:
1912      Select = (UINT16) (MenuNum - 1);
1913      break;
1914
1915    case SCAN_ESC:
1916      return EFI_ABORTED;
1917    }
1918
1919    /* unhighlight last selected row */
1920    gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
1921    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect);
1922    Blank[MenuArray[LastSelect]->DescLen] = 0;
1923    AsciiPrint ("%a\r", Blank);
1924    PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen);
1925    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
1926  } while (!Finish);
1927
1928   ASSERT (Select < PXEBC_MAX_MENU_NUM);
1929
1930  //
1931  // Swap the byte order
1932  //
1933  CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16));
1934  *Type = NTOHS (*Type);
1935
1936  return EFI_SUCCESS;
1937}
1938
1939