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