1 /** @file
2   Support for PxeBc dhcp functions.
3 
4 Copyright (c) 2013, Red Hat, Inc.
5 Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution.  The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10 
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT 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 //
22 UINT8 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 **/
42 VOID
PxeBcInitSeedPacket(IN EFI_DHCP4_PACKET * Seed,IN EFI_UDP4_PROTOCOL * Udp4)43 PxeBcInitSeedPacket (
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 **/
76 VOID
PxeBcCopyEfiDhcp4Packet(IN EFI_DHCP4_PACKET * Dst,IN EFI_DHCP4_PACKET * Src)77 PxeBcCopyEfiDhcp4Packet (
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 **/
97 VOID
PxeBcCopyProxyOffer(IN PXEBC_PRIVATE_DATA * Private,IN UINT32 OfferIndex)98 PxeBcCopyProxyOffer (
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 **/
129 BOOLEAN
PxeBcParseCachedDhcpPacket(IN PXEBC_CACHED_DHCP4_PACKET * CachedPacket)130 PxeBcParseCachedDhcpPacket (
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 **/
293 BOOLEAN
PxeBcTryBinl(IN PXEBC_PRIVATE_DATA * Private,IN UINT32 Index)294 PxeBcTryBinl (
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 **/
380 BOOLEAN
PxeBcTryBinlProxy(IN PXEBC_PRIVATE_DATA * Private,OUT UINT32 * OfferIndex)381 PxeBcTryBinlProxy (
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 **/
414 EFI_STATUS
PxeBcCheckSelectedOffer(IN PXEBC_PRIVATE_DATA * Private)415 PxeBcCheckSelectedOffer (
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 **/
566 VOID
PxeBcCacheDhcpOffer(IN PXEBC_PRIVATE_DATA * Private,IN EFI_DHCP4_PACKET * RcvdOffer)567 PxeBcCacheDhcpOffer (
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 **/
659 EFI_STATUS
PxeBcSetIp4Policy(IN PXEBC_PRIVATE_DATA * Private)660 PxeBcSetIp4Policy (
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 **/
705 VOID
PxeBcSelectOffer(IN PXEBC_PRIVATE_DATA * Private)706 PxeBcSelectOffer (
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 **/
846 EFI_STATUS
847 EFIAPI
PxeBcDhcpCallBack(IN EFI_DHCP4_PROTOCOL * This,IN VOID * Context,IN EFI_DHCP4_STATE CurrentState,IN EFI_DHCP4_EVENT Dhcp4Event,IN EFI_DHCP4_PACKET * Packet OPTIONAL,OUT EFI_DHCP4_PACKET ** NewPacket OPTIONAL)848 PxeBcDhcpCallBack (
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 **/
993 UINT32
PxeBcBuildDhcpOptions(IN PXEBC_PRIVATE_DATA * Private,IN EFI_DHCP4_PACKET_OPTION ** OptList,IN BOOLEAN IsDhcpDiscover)994 PxeBcBuildDhcpOptions (
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 **/
1165 EFI_STATUS
PxeBcDiscvBootService(IN PXEBC_PRIVATE_DATA * Private,IN UINT16 Type,IN UINT16 * Layer,IN BOOLEAN UseBis,IN EFI_IP_ADDRESS * DestIp,IN UINT16 IpCount,IN EFI_PXE_BASE_CODE_SRVLIST * SrvList,IN BOOLEAN IsDiscv,OUT EFI_DHCP4_PACKET * Reply OPTIONAL)1166 PxeBcDiscvBootService (
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 **/
1384 EFI_DHCP4_PACKET_OPTION *
PxeBcParseExtendOptions(IN UINT8 * Buffer,IN UINT32 Length,IN UINT8 OptTag)1385 PxeBcParseExtendOptions (
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 **/
1426 BOOLEAN
PxeBcParseVendorOptions(IN EFI_DHCP4_PACKET_OPTION * Dhcp4Option,IN PXEBC_VENDOR_OPTION * VendorOption)1427 PxeBcParseVendorOptions (
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 **/
1549 VOID
PxeBcDisplayBootItem(IN UINT8 * Str,IN UINT8 Len)1550 PxeBcDisplayBootItem (
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 **/
1577 EFI_STATUS
PxeBcSelectBootPrompt(IN PXEBC_PRIVATE_DATA * Private)1578 PxeBcSelectBootPrompt (
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 
1742 ON_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 **/
1768 EFI_STATUS
PxeBcSelectBootMenu(IN PXEBC_PRIVATE_DATA * Private,OUT UINT16 * Type,IN BOOLEAN UseDefaultItem)1769 PxeBcSelectBootMenu (
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