• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Functions implementation related with DHCPv4 for UefiPxeBc Driver.
3 
4   Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
5 
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 #include "PxeBcImpl.h"
17 
18 //
19 // This is a map from the interested DHCP4 option tags' index to the tag value.
20 //
21 UINT8 mInterestedDhcp4Tags[PXEBC_DHCP4_TAG_INDEX_MAX] = {
22   DHCP4_TAG_BOOTFILE_LEN,
23   DHCP4_TAG_VENDOR,
24   DHCP4_TAG_OVERLOAD,
25   DHCP4_TAG_MSG_TYPE,
26   DHCP4_TAG_SERVER_ID,
27   DHCP4_TAG_VENDOR_CLASS_ID,
28   DHCP4_TAG_BOOTFILE
29 };
30 
31 //
32 // There are 4 times retries with the value of 4, 8, 16 and 32, refers to PXE2.1 spec.
33 //
34 UINT32 mPxeDhcpTimeout[4] = {4, 8, 16, 32};
35 
36 
37 /**
38   Parse a certain dhcp4 option by OptTag in Buffer, and return with start pointer.
39 
40   @param[in]  Buffer              Pointer to the option buffer.
41   @param[in]  Length              Length of the option buffer.
42   @param[in]  OptTag              Tag of the required option.
43 
44   @retval     NULL                Failed to find the required option.
45   @retval     Others              The position of the required option.
46 
47 **/
48 EFI_DHCP4_PACKET_OPTION *
PxeBcParseDhcp4Options(IN UINT8 * Buffer,IN UINT32 Length,IN UINT8 OptTag)49 PxeBcParseDhcp4Options (
50   IN UINT8                      *Buffer,
51   IN UINT32                     Length,
52   IN UINT8                      OptTag
53   )
54 {
55   EFI_DHCP4_PACKET_OPTION       *Option;
56   UINT32                        Offset;
57 
58   Option  = (EFI_DHCP4_PACKET_OPTION *) Buffer;
59   Offset  = 0;
60 
61   while (Offset < Length && Option->OpCode != DHCP4_TAG_EOP) {
62 
63     if (Option->OpCode == OptTag) {
64       //
65       // Found the required option.
66       //
67       return Option;
68     }
69 
70     //
71     // Skip the current option to the next.
72     //
73     if (Option->OpCode == DHCP4_TAG_PAD) {
74       Offset++;
75     } else {
76       Offset += Option->Length + 2;
77     }
78 
79     Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset);
80   }
81 
82   return NULL;
83 }
84 
85 
86 /**
87   Parse the PXE vender options and extract the information from them.
88 
89   @param[in]  Dhcp4Option        Pointer to vendor options in buffer.
90   @param[in]  VendorOption       Pointer to structure to store information in vendor options.
91 
92 **/
93 VOID
PxeBcParseVendorOptions(IN EFI_DHCP4_PACKET_OPTION * Dhcp4Option,IN PXEBC_VENDOR_OPTION * VendorOption)94 PxeBcParseVendorOptions (
95   IN EFI_DHCP4_PACKET_OPTION    *Dhcp4Option,
96   IN PXEBC_VENDOR_OPTION        *VendorOption
97   )
98 {
99   UINT32                        *BitMap;
100   UINT8                         VendorOptionLen;
101   EFI_DHCP4_PACKET_OPTION       *PxeOption;
102   UINT8                         Offset;
103 
104   BitMap          = VendorOption->BitMap;
105   VendorOptionLen = Dhcp4Option->Length;
106   PxeOption       = (EFI_DHCP4_PACKET_OPTION *) &Dhcp4Option->Data[0];
107   Offset          = 0;
108 
109   ASSERT (PxeOption != NULL);
110 
111   while ((Offset < VendorOptionLen) && (PxeOption->OpCode != DHCP4_TAG_EOP)) {
112     //
113     // Parse all the interesting PXE vendor options one by one.
114     //
115     switch (PxeOption->OpCode) {
116 
117     case PXEBC_VENDOR_TAG_MTFTP_IP:
118 
119       CopyMem (&VendorOption->MtftpIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
120       break;
121 
122     case PXEBC_VENDOR_TAG_MTFTP_CPORT:
123 
124       CopyMem (&VendorOption->MtftpCPort, PxeOption->Data, sizeof (VendorOption->MtftpCPort));
125       break;
126 
127     case PXEBC_VENDOR_TAG_MTFTP_SPORT:
128 
129       CopyMem (&VendorOption->MtftpSPort, PxeOption->Data, sizeof (VendorOption->MtftpSPort));
130       break;
131 
132     case PXEBC_VENDOR_TAG_MTFTP_TIMEOUT:
133 
134       VendorOption->MtftpTimeout = *PxeOption->Data;
135       break;
136 
137     case PXEBC_VENDOR_TAG_MTFTP_DELAY:
138 
139       VendorOption->MtftpDelay = *PxeOption->Data;
140       break;
141 
142     case PXEBC_VENDOR_TAG_DISCOVER_CTRL:
143 
144       VendorOption->DiscoverCtrl = *PxeOption->Data;
145       break;
146 
147     case PXEBC_VENDOR_TAG_DISCOVER_MCAST:
148 
149       CopyMem (&VendorOption->DiscoverMcastIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
150       break;
151 
152     case PXEBC_VENDOR_TAG_BOOT_SERVERS:
153 
154       VendorOption->BootSvrLen  = PxeOption->Length;
155       VendorOption->BootSvr     = (PXEBC_BOOT_SVR_ENTRY *) PxeOption->Data;
156       break;
157 
158     case PXEBC_VENDOR_TAG_BOOT_MENU:
159 
160       VendorOption->BootMenuLen = PxeOption->Length;
161       VendorOption->BootMenu    = (PXEBC_BOOT_MENU_ENTRY *) PxeOption->Data;
162       break;
163 
164     case PXEBC_VENDOR_TAG_MENU_PROMPT:
165 
166       VendorOption->MenuPromptLen = PxeOption->Length;
167       VendorOption->MenuPrompt    = (PXEBC_MENU_PROMPT *) PxeOption->Data;
168       break;
169 
170     case PXEBC_VENDOR_TAG_MCAST_ALLOC:
171 
172       CopyMem (&VendorOption->McastIpBase, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
173       CopyMem (&VendorOption->McastIpBlock, PxeOption->Data + 4, sizeof (VendorOption->McastIpBlock));
174       CopyMem (&VendorOption->McastIpRange, PxeOption->Data + 6, sizeof (VendorOption->McastIpRange));
175       break;
176 
177     case PXEBC_VENDOR_TAG_CREDENTIAL_TYPES:
178 
179       VendorOption->CredTypeLen = PxeOption->Length;
180       VendorOption->CredType    = (UINT32 *) PxeOption->Data;
181       break;
182 
183     case PXEBC_VENDOR_TAG_BOOT_ITEM:
184 
185       CopyMem (&VendorOption->BootSrvType, PxeOption->Data, sizeof (VendorOption->BootSrvType));
186       CopyMem (&VendorOption->BootSrvLayer, PxeOption->Data + 2, sizeof (VendorOption->BootSrvLayer));
187       break;
188 
189     default:
190       //
191       // Not interesting PXE vendor options.
192       //
193       break;
194     }
195 
196     //
197     // Set the bit map for the special PXE options.
198     //
199     SET_VENDOR_OPTION_BIT_MAP (BitMap, PxeOption->OpCode);
200 
201     //
202     // Continue to the next option.
203     //
204     if (PxeOption->OpCode == DHCP4_TAG_PAD) {
205       Offset++;
206     } else {
207       Offset = (UINT8) (Offset + PxeOption->Length + 2);
208     }
209 
210     PxeOption = (EFI_DHCP4_PACKET_OPTION *) (Dhcp4Option->Data + Offset);
211   }
212 }
213 
214 
215 /**
216   Build the options buffer for the DHCPv4 request packet.
217 
218   @param[in]  Private             Pointer to PxeBc private data.
219   @param[out] OptList             Pointer to the option pointer array.
220   @param[in]  Buffer              Pointer to the buffer to contain the option list.
221   @param[in]  NeedMsgType         If TRUE, it is necessary to include the Msg type option.
222                                   Otherwise, it is not necessary.
223 
224   @return     Index               The count of the built-in options.
225 
226 **/
227 UINT32
PxeBcBuildDhcp4Options(IN PXEBC_PRIVATE_DATA * Private,OUT EFI_DHCP4_PACKET_OPTION ** OptList,IN UINT8 * Buffer,IN BOOLEAN NeedMsgType)228 PxeBcBuildDhcp4Options (
229   IN  PXEBC_PRIVATE_DATA       *Private,
230   OUT EFI_DHCP4_PACKET_OPTION  **OptList,
231   IN  UINT8                    *Buffer,
232   IN  BOOLEAN                  NeedMsgType
233   )
234 {
235   UINT32                       Index;
236   PXEBC_DHCP4_OPTION_ENTRY     OptEnt;
237   UINT16                       Value;
238 
239   Index      = 0;
240   OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer;
241 
242   if (NeedMsgType) {
243     //
244     // Append message type.
245     //
246     OptList[Index]->OpCode  = DHCP4_TAG_MSG_TYPE;
247     OptList[Index]->Length  = 1;
248     OptEnt.Mesg             = (PXEBC_DHCP4_OPTION_MESG *) OptList[Index]->Data;
249     OptEnt.Mesg->Type       = PXEBC_DHCP4_MSG_TYPE_REQUEST;
250     Index++;
251     OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
252 
253     //
254     // Append max message size.
255     //
256     OptList[Index]->OpCode  = DHCP4_TAG_MAXMSG;
257     OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE);
258     OptEnt.MaxMesgSize      = (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *) OptList[Index]->Data;
259     Value                   = NTOHS (PXEBC_DHCP4_PACKET_MAX_SIZE);
260     CopyMem (&OptEnt.MaxMesgSize->Size, &Value, sizeof (UINT16));
261     Index++;
262     OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
263   }
264 
265   //
266   // Append parameter request list option.
267   //
268   OptList[Index]->OpCode    = DHCP4_TAG_PARA_LIST;
269   OptList[Index]->Length    = 35;
270   OptEnt.Para               = (PXEBC_DHCP4_OPTION_PARA *) OptList[Index]->Data;
271   OptEnt.Para->ParaList[0]  = DHCP4_TAG_NETMASK;
272   OptEnt.Para->ParaList[1]  = DHCP4_TAG_TIME_OFFSET;
273   OptEnt.Para->ParaList[2]  = DHCP4_TAG_ROUTER;
274   OptEnt.Para->ParaList[3]  = DHCP4_TAG_TIME_SERVER;
275   OptEnt.Para->ParaList[4]  = DHCP4_TAG_NAME_SERVER;
276   OptEnt.Para->ParaList[5]  = DHCP4_TAG_DNS_SERVER;
277   OptEnt.Para->ParaList[6]  = DHCP4_TAG_HOSTNAME;
278   OptEnt.Para->ParaList[7]  = DHCP4_TAG_BOOTFILE_LEN;
279   OptEnt.Para->ParaList[8]  = DHCP4_TAG_DOMAINNAME;
280   OptEnt.Para->ParaList[9]  = DHCP4_TAG_ROOTPATH;
281   OptEnt.Para->ParaList[10] = DHCP4_TAG_EXTEND_PATH;
282   OptEnt.Para->ParaList[11] = DHCP4_TAG_EMTU;
283   OptEnt.Para->ParaList[12] = DHCP4_TAG_TTL;
284   OptEnt.Para->ParaList[13] = DHCP4_TAG_BROADCAST;
285   OptEnt.Para->ParaList[14] = DHCP4_TAG_NIS_DOMAIN;
286   OptEnt.Para->ParaList[15] = DHCP4_TAG_NIS_SERVER;
287   OptEnt.Para->ParaList[16] = DHCP4_TAG_NTP_SERVER;
288   OptEnt.Para->ParaList[17] = DHCP4_TAG_VENDOR;
289   OptEnt.Para->ParaList[18] = DHCP4_TAG_REQUEST_IP;
290   OptEnt.Para->ParaList[19] = DHCP4_TAG_LEASE;
291   OptEnt.Para->ParaList[20] = DHCP4_TAG_SERVER_ID;
292   OptEnt.Para->ParaList[21] = DHCP4_TAG_T1;
293   OptEnt.Para->ParaList[22] = DHCP4_TAG_T2;
294   OptEnt.Para->ParaList[23] = DHCP4_TAG_VENDOR_CLASS_ID;
295   OptEnt.Para->ParaList[24] = DHCP4_TAG_TFTP;
296   OptEnt.Para->ParaList[25] = DHCP4_TAG_BOOTFILE;
297   OptEnt.Para->ParaList[26] = DHCP4_TAG_UUID;
298   OptEnt.Para->ParaList[27] = 0x80;
299   OptEnt.Para->ParaList[28] = 0x81;
300   OptEnt.Para->ParaList[29] = 0x82;
301   OptEnt.Para->ParaList[30] = 0x83;
302   OptEnt.Para->ParaList[31] = 0x84;
303   OptEnt.Para->ParaList[32] = 0x85;
304   OptEnt.Para->ParaList[33] = 0x86;
305   OptEnt.Para->ParaList[34] = 0x87;
306   Index++;
307   OptList[Index]            = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
308 
309   //
310   // Append UUID/Guid-based client identifier option
311   //
312   OptList[Index]->OpCode  = DHCP4_TAG_UUID;
313   OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UUID);
314   OptEnt.Uuid             = (PXEBC_DHCP4_OPTION_UUID *) OptList[Index]->Data;
315   OptEnt.Uuid->Type       = 0;
316   Index++;
317   OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
318 
319   if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) {
320     //
321     // Zero the Guid to indicate NOT programable if failed to get system Guid.
322     //
323     ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID));
324   }
325 
326   //
327   // Append client network device interface option
328   //
329   OptList[Index]->OpCode  = DHCP4_TAG_UNDI;
330   OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UNDI);
331   OptEnt.Undi             = (PXEBC_DHCP4_OPTION_UNDI *) OptList[Index]->Data;
332 
333   if (Private->Nii != NULL) {
334     OptEnt.Undi->Type     = Private->Nii->Type;
335     OptEnt.Undi->MajorVer = Private->Nii->MajorVer;
336     OptEnt.Undi->MinorVer = Private->Nii->MinorVer;
337   } else {
338     OptEnt.Undi->Type     = DEFAULT_UNDI_TYPE;
339     OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;
340     OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;
341   }
342 
343   Index++;
344   OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
345 
346   //
347   // Append client system architecture option
348   //
349   OptList[Index]->OpCode  = DHCP4_TAG_ARCH;
350   OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_ARCH);
351   OptEnt.Arch             = (PXEBC_DHCP4_OPTION_ARCH *) OptList[Index]->Data;
352   Value                   = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);
353   CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
354   Index++;
355   OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
356 
357   //
358   // Append vendor class identify option
359   //
360   OptList[Index]->OpCode  = DHCP4_TAG_VENDOR_CLASS_ID;
361   OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_CLID);
362   OptEnt.Clid             = (PXEBC_DHCP4_OPTION_CLID *) OptList[Index]->Data;
363   CopyMem (
364     OptEnt.Clid,
365     DEFAULT_CLASS_ID_DATA,
366     sizeof (PXEBC_DHCP4_OPTION_CLID)
367     );
368   PxeBcUintnToAscDecWithFormat (
369     EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE,
370     OptEnt.Clid->ArchitectureType,
371     sizeof (OptEnt.Clid->ArchitectureType)
372     );
373 
374   if (Private->Nii != NULL) {
375     CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName));
376     PxeBcUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor));
377     PxeBcUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor));
378   }
379 
380   Index++;
381 
382   return Index;
383 }
384 
385 
386 /**
387   Create a template DHCPv4 packet as a seed.
388 
389   @param[out] Seed           Pointer to the seed packet.
390   @param[in]  Udp4           Pointer to EFI_UDP4_PROTOCOL.
391 
392 **/
393 VOID
PxeBcSeedDhcp4Packet(OUT EFI_DHCP4_PACKET * Seed,IN EFI_UDP4_PROTOCOL * Udp4)394 PxeBcSeedDhcp4Packet (
395   OUT EFI_DHCP4_PACKET       *Seed,
396   IN  EFI_UDP4_PROTOCOL      *Udp4
397   )
398 {
399   EFI_SIMPLE_NETWORK_MODE    Mode;
400   EFI_DHCP4_HEADER           *Header;
401 
402   //
403   // Get IfType and HwAddressSize from SNP mode data.
404   //
405   Udp4->GetModeData (Udp4, NULL, NULL, NULL, &Mode);
406 
407   Seed->Size            = sizeof (EFI_DHCP4_PACKET);
408   Seed->Length          = sizeof (Seed->Dhcp4);
409   Header                = &Seed->Dhcp4.Header;
410   ZeroMem (Header, sizeof (EFI_DHCP4_HEADER));
411   Header->OpCode        = PXEBC_DHCP4_OPCODE_REQUEST;
412   Header->HwType        = Mode.IfType;
413   Header->HwAddrLen     = (UINT8) Mode.HwAddressSize;
414   CopyMem (Header->ClientHwAddr, &Mode.CurrentAddress, Header->HwAddrLen);
415 
416   Seed->Dhcp4.Magik     = PXEBC_DHCP4_MAGIC;
417   Seed->Dhcp4.Option[0] = DHCP4_TAG_EOP;
418 }
419 
420 
421 /**
422   Cache the DHCPv4 packet.
423 
424   @param[in]  Dst          Pointer to the cache buffer for DHCPv4 packet.
425   @param[in]  Src          Pointer to the DHCPv4 packet to be cached.
426 
427   @retval     EFI_SUCCESS                Packet is copied.
428   @retval     EFI_BUFFER_TOO_SMALL       Cache buffer is not big enough to hold the packet.
429 
430 **/
431 EFI_STATUS
PxeBcCacheDhcp4Packet(IN EFI_DHCP4_PACKET * Dst,IN EFI_DHCP4_PACKET * Src)432 PxeBcCacheDhcp4Packet (
433   IN EFI_DHCP4_PACKET     *Dst,
434   IN EFI_DHCP4_PACKET     *Src
435   )
436 {
437   if (Dst->Size < Src->Length) {
438     return EFI_BUFFER_TOO_SMALL;
439   }
440 
441   CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length);
442   Dst->Length = Src->Length;
443 
444   return EFI_SUCCESS;
445 }
446 
447 
448 /**
449   Parse the cached DHCPv4 packet, including all the options.
450 
451   @param[in]  Cache4           Pointer to cached DHCPv4 packet.
452 
453   @retval     EFI_SUCCESS      Parsed the DHCPv4 packet successfully.
454   @retval     EFI_DEVICE_ERROR Failed to parse and invalid packet.
455 
456 **/
457 EFI_STATUS
PxeBcParseDhcp4Packet(IN PXEBC_DHCP4_PACKET_CACHE * Cache4)458 PxeBcParseDhcp4Packet (
459   IN PXEBC_DHCP4_PACKET_CACHE    *Cache4
460   )
461 {
462   EFI_DHCP4_PACKET               *Offer;
463   EFI_DHCP4_PACKET_OPTION        **Options;
464   EFI_DHCP4_PACKET_OPTION        *Option;
465   PXEBC_OFFER_TYPE               OfferType;
466   UINTN                          Index;
467   BOOLEAN                        IsProxyOffer;
468   BOOLEAN                        IsPxeOffer;
469   UINT8                          *Ptr8;
470   BOOLEAN                        FileFieldOverloaded;
471 
472   IsProxyOffer = FALSE;
473   IsPxeOffer   = FALSE;
474   FileFieldOverloaded = FALSE;
475 
476   ZeroMem (Cache4->OptList, sizeof (Cache4->OptList));
477   ZeroMem (&Cache4->VendorOpt, sizeof (Cache4->VendorOpt));
478 
479   Offer   = &Cache4->Packet.Offer;
480   Options = Cache4->OptList;
481 
482   //
483   // Parse DHCPv4 options in this offer, and store the pointers.
484   // First, try to parse DHCPv4 options from the DHCP optional parameters field.
485   //
486   for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {
487     Options[Index] = PxeBcParseDhcp4Options (
488                        Offer->Dhcp4.Option,
489                        GET_OPTION_BUFFER_LEN (Offer),
490                        mInterestedDhcp4Tags[Index]
491                        );
492   }
493   //
494   // Second, Check if bootfilename and serverhostname is overloaded to carry DHCP options refers to rfc-2132.
495   // If yes, try to parse options from the BootFileName field, then ServerName field.
496   //
497   Option = Options[PXEBC_DHCP4_TAG_INDEX_OVERLOAD];
498   if (Option != NULL) {
499     if ((Option->Data[0] & PXEBC_DHCP4_OVERLOAD_FILE) != 0) {
500       FileFieldOverloaded = TRUE;
501       for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {
502         if (Options[Index] == NULL) {
503           Options[Index] = PxeBcParseDhcp4Options (
504                              (UINT8 *) Offer->Dhcp4.Header.BootFileName,
505                              sizeof (Offer->Dhcp4.Header.BootFileName),
506                              mInterestedDhcp4Tags[Index]
507                              );
508         }
509       }
510     }
511     if ((Option->Data[0] & PXEBC_DHCP4_OVERLOAD_SERVER_NAME) != 0) {
512       for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {
513         if (Options[Index] == NULL) {
514           Options[Index] = PxeBcParseDhcp4Options (
515                              (UINT8 *) Offer->Dhcp4.Header.ServerName,
516                              sizeof (Offer->Dhcp4.Header.ServerName),
517                              mInterestedDhcp4Tags[Index]
518                              );
519         }
520       }
521     }
522   }
523 
524   //
525   // The offer with zero "yiaddr" is a proxy offer.
526   //
527   if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) {
528     IsProxyOffer = TRUE;
529   }
530 
531   //
532   // The offer with "PXEClient" is a PXE offer.
533   //
534   Option = Options[PXEBC_DHCP4_TAG_INDEX_CLASS_ID];
535   if ((Option != NULL) && (Option->Length >= 9) &&
536       (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) {
537     IsPxeOffer = TRUE;
538   }
539 
540   //
541   // Parse PXE vendor options in this offer, and store the contents/pointers.
542   //
543   Option = Options[PXEBC_DHCP4_TAG_INDEX_VENDOR];
544   if (IsPxeOffer && Option != NULL) {
545     PxeBcParseVendorOptions (Option, &Cache4->VendorOpt);
546   }
547 
548   //
549   // Parse PXE boot file name:
550   // According to PXE spec, boot file name should be read from DHCP option 67 (bootfile name) if present.
551   // Otherwise, read from boot file field in DHCP header.
552   //
553   if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
554     //
555     // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null
556     // terminated string. So force to append null terminated character at the end of string.
557     //
558     Ptr8 =  (UINT8*)&Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data[0];
559     Ptr8 += Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Length;
560     if (*(Ptr8 - 1) != '\0') {
561       *Ptr8 = '\0';
562     }
563   } else if (!FileFieldOverloaded && Offer->Dhcp4.Header.BootFileName[0] != 0) {
564     //
565     // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it.
566     // Do not count dhcp option header here, or else will destroy the serverhostname.
567     //
568     Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *)
569                                                 (&Offer->Dhcp4.Header.BootFileName[0] -
570                                                 OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0]));
571 
572   }
573 
574   //
575   // Determine offer type of the DHCPv4 packet.
576   //
577   Option = Options[PXEBC_DHCP4_TAG_INDEX_MSG_TYPE];
578   if (Option == NULL || Option->Data[0] == 0) {
579     //
580     // It's a Bootp offer.
581     //
582     OfferType = PxeOfferTypeBootp;
583 
584     Option    = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE];
585     if (Option == NULL) {
586       //
587       // If the Bootp offer without bootfilename, discard it.
588       //
589       return EFI_DEVICE_ERROR;
590     }
591   } else {
592 
593     if (IS_VALID_DISCOVER_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) {
594       //
595       // It's a PXE10 offer with PXEClient and discover vendor option.
596       //
597       OfferType = IsProxyOffer ? PxeOfferTypeProxyPxe10 : PxeOfferTypeDhcpPxe10;
598     } else if (IS_VALID_MTFTP_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) {
599       //
600       // It's a WFM11a offer with PXEClient and mtftp vendor option.
601       // But multi-cast download is not supported currently, so discard it.
602       //
603       return EFI_DEVICE_ERROR;
604     } else if (IsPxeOffer) {
605       //
606       // It's a BINL offer only with PXEClient.
607       //
608       OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;
609     } else {
610       //
611       // It's a DHCPv4 only offer, which is a pure DHCPv4 offer packet.
612       //
613       OfferType = PxeOfferTypeDhcpOnly;
614     }
615   }
616 
617   Cache4->OfferType = OfferType;
618 
619   return EFI_SUCCESS;
620 }
621 
622 
623 /**
624   Cache the DHCPv4 ack packet, and parse it on demand.
625 
626   @param[in]  Private             Pointer to PxeBc private data.
627   @param[in]  Ack                 Pointer to the DHCPv4 ack packet.
628   @param[in]  Verified            If TRUE, parse the ACK packet and store info into mode data.
629 
630   @retval     EFI_SUCCESS                Cache and parse the packet successfully.
631   @retval     EFI_BUFFER_TOO_SMALL       Cache buffer is not big enough to hold the packet.
632 
633 **/
634 EFI_STATUS
PxeBcCopyDhcp4Ack(IN PXEBC_PRIVATE_DATA * Private,IN EFI_DHCP4_PACKET * Ack,IN BOOLEAN Verified)635 PxeBcCopyDhcp4Ack (
636   IN PXEBC_PRIVATE_DATA   *Private,
637   IN EFI_DHCP4_PACKET     *Ack,
638   IN BOOLEAN              Verified
639   )
640 {
641   EFI_PXE_BASE_CODE_MODE  *Mode;
642   EFI_STATUS              Status;
643 
644   Mode = Private->PxeBc.Mode;
645 
646   Status = PxeBcCacheDhcp4Packet (&Private->DhcpAck.Dhcp4.Packet.Ack, Ack);
647   if (EFI_ERROR (Status)) {
648     return Status;
649   }
650 
651   if (Verified) {
652     //
653     // Parse the ack packet and store it into mode data if needed.
654     //
655     PxeBcParseDhcp4Packet (&Private->DhcpAck.Dhcp4);
656     CopyMem (&Mode->DhcpAck.Dhcpv4, &Ack->Dhcp4, Ack->Length);
657     Mode->DhcpAckReceived = TRUE;
658   }
659 
660   return EFI_SUCCESS;
661 }
662 
663 
664 /**
665   Cache the DHCPv4 proxy offer packet according to the received order.
666 
667   @param[in]  Private               Pointer to PxeBc private data.
668   @param[in]  OfferIndex            The received order of offer packets.
669 
670   @retval     EFI_SUCCESS                Cache and parse the packet successfully.
671   @retval     EFI_BUFFER_TOO_SMALL       Cache buffer is not big enough to hold the packet.
672 
673 **/
674 EFI_STATUS
PxeBcCopyProxyOffer(IN PXEBC_PRIVATE_DATA * Private,IN UINT32 OfferIndex)675 PxeBcCopyProxyOffer (
676   IN PXEBC_PRIVATE_DATA   *Private,
677   IN UINT32               OfferIndex
678   )
679 {
680   EFI_PXE_BASE_CODE_MODE  *Mode;
681   EFI_DHCP4_PACKET        *Offer;
682   EFI_STATUS              Status;
683 
684   ASSERT (OfferIndex < Private->OfferNum);
685   ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);
686 
687   Mode  = Private->PxeBc.Mode;
688   Offer = &Private->OfferBuffer[OfferIndex].Dhcp4.Packet.Offer;
689 
690   //
691   // Cache the proxy offer packet and parse it.
692   //
693   Status = PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Offer);
694   if (EFI_ERROR(Status)) {
695     return Status;
696   }
697 
698   PxeBcParseDhcp4Packet (&Private->ProxyOffer.Dhcp4);
699 
700   //
701   // Store this packet into mode data.
702   //
703   CopyMem (&Mode->ProxyOffer.Dhcpv4, &Offer->Dhcp4, Offer->Length);
704   Mode->ProxyOfferReceived = TRUE;
705 
706   return EFI_SUCCESS;
707 }
708 
709 
710 /**
711   Retry to request bootfile name by the BINL offer.
712 
713   @param[in]  Private              Pointer to PxeBc private data.
714   @param[in]  Index                The received order of offer packets.
715 
716   @retval     EFI_SUCCESS          Successfully retried to request bootfile name.
717   @retval     EFI_DEVICE_ERROR     Failed to retry bootfile name.
718 
719 **/
720 EFI_STATUS
PxeBcRetryBinlOffer(IN PXEBC_PRIVATE_DATA * Private,IN UINT32 Index)721 PxeBcRetryBinlOffer (
722   IN PXEBC_PRIVATE_DATA     *Private,
723   IN UINT32                 Index
724   )
725 {
726   EFI_DHCP4_PACKET          *Offer;
727   EFI_IP_ADDRESS            ServerIp;
728   EFI_STATUS                Status;
729   PXEBC_DHCP4_PACKET_CACHE  *Cache4;
730   EFI_DHCP4_PACKET          *Reply;
731 
732   ASSERT (Index < PXEBC_OFFER_MAX_NUM);
733   ASSERT (Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpBinl ||
734           Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeProxyBinl);
735 
736   Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;
737 
738   //
739   // Prefer to siaddr in header as next server address. If it's zero, then use option 54.
740   //
741   if (Offer->Dhcp4.Header.ServerAddr.Addr[0] == 0) {
742     CopyMem (
743       &ServerIp.Addr[0],
744       Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,
745       sizeof (EFI_IPv4_ADDRESS)
746       );
747   } else {
748     CopyMem (
749       &ServerIp.Addr[0],
750       &Offer->Dhcp4.Header.ServerAddr,
751       sizeof (EFI_IPv4_ADDRESS)
752       );
753   }
754 
755   Private->IsDoDiscover = FALSE;
756   Cache4                = &Private->ProxyOffer.Dhcp4;
757   Reply                 = &Cache4->Packet.Offer;
758 
759   //
760   // Send another request packet for bootfile name.
761   //
762   Status = PxeBcDhcp4Discover (
763              Private,
764              0,
765              NULL,
766              FALSE,
767              &ServerIp,
768              0,
769              NULL
770              );
771   if (EFI_ERROR (Status)) {
772     return Status;
773   }
774 
775   //
776   // Parse the reply for the last request packet.
777   //
778   Status = PxeBcParseDhcp4Packet (Cache4);
779   if (EFI_ERROR (Status)) {
780     return Status;
781   }
782 
783   if (Cache4->OfferType != PxeOfferTypeProxyPxe10 &&
784       Cache4->OfferType != PxeOfferTypeProxyWfm11a &&
785       Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {
786     //
787     // This BINL ack doesn't have discovery option set or multicast option set
788     // or bootfile name specified.
789     //
790     return EFI_DEVICE_ERROR;
791   }
792 
793   //
794   // Store the reply into mode data.
795   //
796   Private->PxeBc.Mode->ProxyOfferReceived = TRUE;
797   CopyMem (&Private->PxeBc.Mode->ProxyOffer.Dhcpv4, &Reply->Dhcp4, Reply->Length);
798 
799   return EFI_SUCCESS;
800 }
801 
802 
803 /**
804   Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount.
805 
806   @param[in]  Private               Pointer to PxeBc private data.
807   @param[in]  RcvdOffer             Pointer to the received offer packet.
808 
809   @retval     EFI_SUCCESS      Cache and parse the packet successfully.
810   @retval     Others           Operation failed.
811 
812 **/
813 EFI_STATUS
PxeBcCacheDhcp4Offer(IN PXEBC_PRIVATE_DATA * Private,IN EFI_DHCP4_PACKET * RcvdOffer)814 PxeBcCacheDhcp4Offer (
815   IN PXEBC_PRIVATE_DATA     *Private,
816   IN EFI_DHCP4_PACKET       *RcvdOffer
817   )
818 {
819   PXEBC_DHCP4_PACKET_CACHE  *Cache4;
820   EFI_DHCP4_PACKET          *Offer;
821   PXEBC_OFFER_TYPE          OfferType;
822   EFI_STATUS                Status;
823 
824   ASSERT (Private->OfferNum < PXEBC_OFFER_MAX_NUM);
825   Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4;
826   Offer  = &Cache4->Packet.Offer;
827 
828   //
829   // Cache the content of DHCPv4 packet firstly.
830   //
831   Status = PxeBcCacheDhcp4Packet (Offer, RcvdOffer);
832   if (EFI_ERROR(Status)) {
833     return Status;
834   }
835 
836   //
837   // Validate the DHCPv4 packet, and parse the options and offer type.
838   //
839   if (EFI_ERROR (PxeBcParseDhcp4Packet (Cache4))) {
840     return EFI_ABORTED;
841   }
842 
843   //
844   // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
845   //
846   OfferType = Cache4->OfferType;
847   ASSERT (OfferType < PxeOfferTypeMax);
848 
849   if (OfferType == PxeOfferTypeBootp) {
850     //
851     // It's a Bootp offer, only cache the first one, and discard the others.
852     //
853     if (Private->OfferCount[OfferType] == 0) {
854       Private->OfferIndex[OfferType][0] = Private->OfferNum;
855       Private->OfferCount[OfferType]    = 1;
856     } else {
857       return EFI_ABORTED;
858     }
859   } else {
860     ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);
861     if (IS_PROXY_DHCP_OFFER (Offer)) {
862       //
863       // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.
864       //
865       Private->IsProxyRecved = TRUE;
866 
867       if (OfferType == PxeOfferTypeProxyBinl) {
868         //
869         // Cache all proxy BINL offers.
870         //
871         Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
872         Private->OfferCount[OfferType]++;
873       } else if (Private->OfferCount[OfferType] > 0) {
874         //
875         // Only cache the first PXE10/WFM11a offer, and discard the others.
876         //
877         Private->OfferIndex[OfferType][0] = Private->OfferNum;
878         Private->OfferCount[OfferType]    = 1;
879       } else {
880         return EFI_ABORTED;
881       }
882     } else {
883       //
884       // It's a DHCPv4 offer with yiaddr, and cache them all.
885       //
886       Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
887       Private->OfferCount[OfferType]++;
888     }
889   }
890 
891   Private->OfferNum++;
892 
893   return EFI_SUCCESS;
894 }
895 
896 
897 /**
898   Select an DHCPv4 offer, and record SelectIndex and SelectProxyType.
899 
900   @param[in]  Private             Pointer to PxeBc private data.
901 
902 **/
903 VOID
PxeBcSelectDhcp4Offer(IN PXEBC_PRIVATE_DATA * Private)904 PxeBcSelectDhcp4Offer (
905   IN PXEBC_PRIVATE_DATA       *Private
906   )
907 {
908   UINT32                      Index;
909   UINT32                      OfferIndex;
910   EFI_DHCP4_PACKET            *Offer;
911 
912   Private->SelectIndex = 0;
913 
914   if (Private->IsOfferSorted) {
915     //
916     // Select offer by default policy.
917     //
918     if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {
919       //
920       // 1. DhcpPxe10 offer
921       //
922       Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;
923 
924     } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {
925       //
926       // 2. DhcpWfm11a offer
927       //
928       Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;
929 
930     } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
931                Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) {
932       //
933       // 3. DhcpOnly offer and ProxyPxe10 offer.
934       //
935       Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
936       Private->SelectProxyType = PxeOfferTypeProxyPxe10;
937 
938     } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
939                Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) {
940       //
941       // 4. DhcpOnly offer and ProxyWfm11a offer.
942       //
943       Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
944       Private->SelectProxyType = PxeOfferTypeProxyWfm11a;
945 
946     } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {
947       //
948       // 5. DhcpBinl offer.
949       //
950       Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;
951 
952     } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
953                Private->OfferCount[PxeOfferTypeProxyBinl] > 0) {
954       //
955       // 6. DhcpOnly offer and ProxyBinl offer.
956       //
957       Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
958       Private->SelectProxyType = PxeOfferTypeProxyBinl;
959 
960     } else {
961       //
962       // 7. DhcpOnly offer with bootfilename.
963       //
964       for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) {
965         OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index];
966         if (Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
967           Private->SelectIndex = OfferIndex + 1;
968           break;
969         }
970       }
971       //
972       // 8. Bootp offer with bootfilename.
973       //
974       OfferIndex = Private->OfferIndex[PxeOfferTypeBootp][0];
975       if (Private->SelectIndex == 0 &&
976           Private->OfferCount[PxeOfferTypeBootp] > 0 &&
977           Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
978         Private->SelectIndex = OfferIndex + 1;
979       }
980     }
981   } else {
982     //
983     // Select offer by received order.
984     //
985     for (Index = 0; Index < Private->OfferNum; Index++) {
986 
987       Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;
988 
989       if (IS_PROXY_DHCP_OFFER (Offer)) {
990         //
991         // Skip proxy offers
992         //
993         continue;
994       }
995 
996       if (!Private->IsProxyRecved &&
997           Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpOnly &&
998           Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {
999         //
1000         // Skip if DhcpOnly offer without any other proxy offers or bootfilename.
1001         //
1002         continue;
1003       }
1004 
1005       //
1006       // Record the index of the select offer.
1007       //
1008       Private->SelectIndex = Index + 1;
1009       break;
1010     }
1011   }
1012 }
1013 
1014 
1015 /**
1016   Handle the DHCPv4 offer packet.
1017 
1018   @param[in]  Private               Pointer to PxeBc private data.
1019 
1020   @retval     EFI_SUCCESS           Handled the DHCPv4 offer packet successfully.
1021   @retval     EFI_NO_RESPONSE       No response to the following request packet.
1022   @retval     EFI_NOT_FOUND         No boot filename received.
1023   @retval     EFI_BUFFER_TOO_SMALL  Can't cache the offer pacet.
1024 
1025 **/
1026 EFI_STATUS
PxeBcHandleDhcp4Offer(IN PXEBC_PRIVATE_DATA * Private)1027 PxeBcHandleDhcp4Offer (
1028   IN PXEBC_PRIVATE_DATA     *Private
1029   )
1030 {
1031   PXEBC_DHCP4_PACKET_CACHE  *Cache4;
1032   EFI_DHCP4_PACKET_OPTION   **Options;
1033   UINT32                    Index;
1034   EFI_DHCP4_PACKET          *Offer;
1035   PXEBC_OFFER_TYPE          OfferType;
1036   UINT32                    ProxyIndex;
1037   UINT32                    SelectIndex;
1038   EFI_STATUS                Status;
1039   EFI_PXE_BASE_CODE_MODE    *Mode;
1040   EFI_DHCP4_PACKET          *Ack;
1041 
1042   ASSERT (Private->SelectIndex > 0);
1043   SelectIndex = (UINT32) (Private->SelectIndex - 1);
1044   ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM);
1045   Cache4      = &Private->OfferBuffer[SelectIndex].Dhcp4;
1046   Options     = Cache4->OptList;
1047   Status      = EFI_SUCCESS;
1048 
1049   if (Cache4->OfferType == PxeOfferTypeDhcpBinl) {
1050     //
1051     // DhcpBinl offer is selected, so need try to request bootfilename by this offer.
1052     //
1053     if (EFI_ERROR (PxeBcRetryBinlOffer (Private, SelectIndex))) {
1054       Status = EFI_NO_RESPONSE;
1055     }
1056   } else if (Cache4->OfferType == PxeOfferTypeDhcpOnly) {
1057 
1058     if (Private->IsProxyRecved) {
1059       //
1060       // DhcpOnly offer is selected, so need try to request bootfile name.
1061       //
1062       ProxyIndex = 0;
1063       if (Private->IsOfferSorted) {
1064         //
1065         // The proxy offer should be determined if select by default policy.
1066         // IsOfferSorted means all offers are labeled by OfferIndex.
1067         //
1068         ASSERT (Private->SelectProxyType < PxeOfferTypeMax);
1069         ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);
1070 
1071         if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {
1072           //
1073           // Try all the cached ProxyBinl offer one by one to request bootfile name.
1074           //
1075           for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {
1076             ASSERT (Index < PXEBC_OFFER_MAX_NUM);
1077             ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];
1078             if (!EFI_ERROR (PxeBcRetryBinlOffer (Private, ProxyIndex))) {
1079               break;
1080             }
1081           }
1082           if (Index == Private->OfferCount[Private->SelectProxyType]) {
1083             Status = EFI_NO_RESPONSE;
1084           }
1085         } else {
1086           //
1087           // For other proxy offers, only one is buffered.
1088           //
1089           ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
1090         }
1091       } else {
1092         //
1093         // The proxy offer should not be determined if select by received order.
1094         //
1095         Status = EFI_NO_RESPONSE;
1096 
1097         for (Index = 0; Index < Private->OfferNum; Index++) {
1098           ASSERT (Index < PXEBC_OFFER_MAX_NUM);
1099           Offer     = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;
1100           OfferType = Private->OfferBuffer[Index].Dhcp4.OfferType;
1101           if (!IS_PROXY_DHCP_OFFER (Offer)) {
1102             //
1103             // Skip non proxy DHCPv4 offers.
1104             //
1105             continue;
1106           }
1107 
1108           if (OfferType == PxeOfferTypeProxyBinl) {
1109             //
1110             // Try all the cached ProxyBinl offer one by one to request bootfile name.
1111             //
1112             if (EFI_ERROR (PxeBcRetryBinlOffer (Private, Index))) {
1113               continue;
1114             }
1115           }
1116 
1117           Private->SelectProxyType = OfferType;
1118           ProxyIndex               = Index;
1119           Status                   = EFI_SUCCESS;
1120           break;
1121         }
1122       }
1123 
1124       if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) {
1125         //
1126         // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.
1127         //
1128         Status = PxeBcCopyProxyOffer (Private, ProxyIndex);
1129       }
1130     } else {
1131       //
1132       //  Othewise, the bootfile name must be included in DhcpOnly offer.
1133       //
1134       if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {
1135         Status = EFI_NOT_FOUND;
1136       }
1137     }
1138   }
1139 
1140   if (!EFI_ERROR (Status)) {
1141     //
1142     // All PXE boot information is ready by now.
1143     //
1144     Mode  = Private->PxeBc.Mode;
1145     Offer = &Cache4->Packet.Offer;
1146     Ack   = &Private->DhcpAck.Dhcp4.Packet.Ack;
1147     if (Cache4->OfferType == PxeOfferTypeBootp) {
1148       //
1149       // Bootp is a special case that only 2 packets involved instead of 4. So the bootp's reply
1150       // should be taken as ack.
1151       //
1152       Ack = Offer;
1153     }
1154 
1155     Status = PxeBcCopyDhcp4Ack (Private, Ack, TRUE);
1156     if (EFI_ERROR (Status)) {
1157       return Status;
1158     }
1159     Mode->DhcpDiscoverValid = TRUE;
1160   }
1161 
1162   return Status;
1163 }
1164 
1165 
1166 /**
1167   EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver
1168   to intercept events that occurred in the configuration process.
1169 
1170   @param[in]  This              Pointer to the EFI DHCPv4 Protocol.
1171   @param[in]  Context           Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure().
1172   @param[in]  CurrentState      The current operational state of the EFI DHCPv4 Protocol driver.
1173   @param[in]  Dhcp4Event        The event that occurs in the current state, which usually means a
1174                                 state transition.
1175   @param[in]  Packet            The DHCPv4 packet that is going to be sent or already received.
1176   @param[out] NewPacket         The packet that is used to replace the above Packet.
1177 
1178   @retval EFI_SUCCESS           Tells the EFI DHCPv4 Protocol driver to continue the DHCP process.
1179   @retval EFI_NOT_READY         Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol
1180                                 driver will continue to wait for more DHCPOFFER packets until the
1181                                 retry timeout expires.
1182   @retval EFI_ABORTED           Tells the EFI DHCPv4 Protocol driver to abort the current process
1183                                 and return to the Dhcp4Init or Dhcp4InitReboot state.
1184 
1185 **/
1186 EFI_STATUS
1187 EFIAPI
PxeBcDhcp4CallBack(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)1188 PxeBcDhcp4CallBack (
1189   IN  EFI_DHCP4_PROTOCOL               *This,
1190   IN  VOID                             *Context,
1191   IN  EFI_DHCP4_STATE                  CurrentState,
1192   IN  EFI_DHCP4_EVENT                  Dhcp4Event,
1193   IN  EFI_DHCP4_PACKET                 *Packet            OPTIONAL,
1194   OUT EFI_DHCP4_PACKET                 **NewPacket        OPTIONAL
1195   )
1196 {
1197   PXEBC_PRIVATE_DATA                   *Private;
1198   EFI_PXE_BASE_CODE_MODE               *Mode;
1199   EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  *Callback;
1200   EFI_DHCP4_PACKET_OPTION              *MaxMsgSize;
1201   UINT16                               Value;
1202   EFI_STATUS                           Status;
1203   BOOLEAN                              Received;
1204 
1205   if ((Dhcp4Event != Dhcp4RcvdOffer) &&
1206       (Dhcp4Event != Dhcp4SelectOffer) &&
1207       (Dhcp4Event != Dhcp4SendDiscover) &&
1208       (Dhcp4Event != Dhcp4RcvdAck)) {
1209     return EFI_SUCCESS;
1210   }
1211 
1212   Private   = (PXEBC_PRIVATE_DATA *) Context;
1213   Mode      = Private->PxeBc.Mode;
1214   Callback  = Private->PxeBcCallback;
1215 
1216   //
1217   // Override the Maximum DHCP Message Size.
1218   //
1219   MaxMsgSize = PxeBcParseDhcp4Options (
1220                  Packet->Dhcp4.Option,
1221                  GET_OPTION_BUFFER_LEN (Packet),
1222                  DHCP4_TAG_MAXMSG
1223                  );
1224   if (MaxMsgSize != NULL) {
1225     Value = HTONS (PXEBC_DHCP4_PACKET_MAX_SIZE);
1226     CopyMem (MaxMsgSize->Data, &Value, sizeof (Value));
1227   }
1228 
1229   //
1230   // Callback to user if any packets sent or received.
1231   //
1232   if (Dhcp4Event != Dhcp4SelectOffer && Callback != NULL) {
1233     Received = (BOOLEAN) (Dhcp4Event == Dhcp4RcvdOffer || Dhcp4Event == Dhcp4RcvdAck);
1234     Status = Callback->Callback (
1235                          Callback,
1236                          Private->Function,
1237                          Received,
1238                          Packet->Length,
1239                          (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp4
1240                          );
1241     if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
1242       return EFI_ABORTED;
1243     }
1244   }
1245 
1246   Status = EFI_SUCCESS;
1247 
1248   switch (Dhcp4Event) {
1249 
1250   case Dhcp4SendDiscover:
1251     if (Packet->Length > PXEBC_DHCP4_PACKET_MAX_SIZE) {
1252       //
1253       // If the to be sent packet exceeds the maximum length, abort the DHCP process.
1254       //
1255       Status = EFI_ABORTED;
1256       break;
1257     }
1258 
1259     //
1260     // Cache the DHCPv4 discover packet to mode data directly.
1261     // It need to check SendGuid as well as Dhcp4SendRequest.
1262     //
1263     CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp4, Packet->Length);
1264 
1265   case Dhcp4SendRequest:
1266     if (Packet->Length > PXEBC_DHCP4_PACKET_MAX_SIZE) {
1267       //
1268       // If the to be sent packet exceeds the maximum length, abort the DHCP process.
1269       //
1270       Status = EFI_ABORTED;
1271       break;
1272     }
1273 
1274     if (Mode->SendGUID) {
1275       //
1276       // Send the system Guid instead of the MAC address as the hardware address if required.
1277       //
1278       if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) Packet->Dhcp4.Header.ClientHwAddr))) {
1279         //
1280         // Zero the Guid to indicate NOT programable if failed to get system Guid.
1281         //
1282         ZeroMem (Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID));
1283       }
1284       Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID);
1285     }
1286     break;
1287 
1288   case Dhcp4RcvdOffer:
1289     Status = EFI_NOT_READY;
1290     if (Packet->Length > PXEBC_DHCP4_PACKET_MAX_SIZE) {
1291       //
1292       // Ignore the incoming packets which exceed the maximum length.
1293       //
1294       break;
1295     }
1296     if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {
1297       //
1298       // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record
1299       // the OfferIndex and OfferCount.
1300       // If error happens, just ignore this packet and continue to wait more offer.
1301       //
1302       PxeBcCacheDhcp4Offer (Private, Packet);
1303     }
1304     break;
1305 
1306   case Dhcp4SelectOffer:
1307     //
1308     // Select offer by the default policy or by order, and record the SelectIndex
1309     // and SelectProxyType.
1310     //
1311     PxeBcSelectDhcp4Offer (Private);
1312 
1313     if (Private->SelectIndex == 0) {
1314       Status = EFI_ABORTED;
1315     } else {
1316       *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer;
1317     }
1318     break;
1319 
1320   case Dhcp4RcvdAck:
1321     //
1322     // Cache the DHCPv4 ack to Private->Dhcp4Ack, but it's not the final ack in mode data
1323     // without verification.
1324     //
1325     ASSERT (Private->SelectIndex != 0);
1326 
1327     Status = PxeBcCopyDhcp4Ack (Private, Packet, FALSE);
1328     if (EFI_ERROR (Status)) {
1329       Status = EFI_ABORTED;
1330     }
1331     break;
1332 
1333   default:
1334     break;
1335   }
1336 
1337   return Status;
1338 }
1339 
1340 
1341 /**
1342   Build and send out the request packet for the bootfile, and parse the reply.
1343 
1344   @param[in]  Private               Pointer to PxeBc private data.
1345   @param[in]  Type                  PxeBc option boot item type.
1346   @param[in]  Layer                 Pointer to option boot item layer.
1347   @param[in]  UseBis                Use BIS or not.
1348   @param[in]  DestIp                Pointer to the server address.
1349   @param[in]  IpCount               The total count of the server address.
1350   @param[in]  SrvList               Pointer to EFI_PXE_BASE_CODE_SRVLIST.
1351 
1352   @retval     EFI_SUCCESS           Successfully discovered boot file.
1353   @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resource.
1354   @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.
1355   @retval     Others                Failed to discover boot file.
1356 
1357 **/
1358 EFI_STATUS
PxeBcDhcp4Discover(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)1359 PxeBcDhcp4Discover (
1360   IN  PXEBC_PRIVATE_DATA              *Private,
1361   IN  UINT16                          Type,
1362   IN  UINT16                          *Layer,
1363   IN  BOOLEAN                         UseBis,
1364   IN  EFI_IP_ADDRESS                  *DestIp,
1365   IN  UINT16                          IpCount,
1366   IN  EFI_PXE_BASE_CODE_SRVLIST       *SrvList
1367   )
1368 {
1369   EFI_PXE_BASE_CODE_UDP_PORT          Sport;
1370   EFI_PXE_BASE_CODE_MODE              *Mode;
1371   EFI_DHCP4_PROTOCOL                  *Dhcp4;
1372   EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN    Token;
1373   BOOLEAN                             IsBCast;
1374   EFI_STATUS                          Status;
1375   UINT16                              RepIndex;
1376   UINT16                              SrvIndex;
1377   UINT16                              TryIndex;
1378   EFI_DHCP4_LISTEN_POINT              ListenPoint;
1379   EFI_DHCP4_PACKET                    *Response;
1380   UINT8                               Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE];
1381   EFI_DHCP4_PACKET_OPTION             *OptList[PXEBC_DHCP4_OPTION_MAX_NUM];
1382   UINT32                              OptCount;
1383   EFI_DHCP4_PACKET_OPTION             *PxeOpt;
1384   PXEBC_OPTION_BOOT_ITEM              *PxeBootItem;
1385   UINT8                               VendorOptLen;
1386   UINT32                              Xid;
1387 
1388   Mode      = Private->PxeBc.Mode;
1389   Dhcp4     = Private->Dhcp4;
1390   Status    = EFI_SUCCESS;
1391 
1392   ZeroMem (&Token, sizeof (EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN));
1393 
1394   //
1395   // Use broadcast if destination address not specified.
1396   //
1397   if (DestIp == NULL) {
1398     Sport   = PXEBC_DHCP4_S_PORT;
1399     IsBCast = TRUE;
1400   } else {
1401     Sport   = PXEBC_BS_DISCOVER_PORT;
1402     IsBCast = FALSE;
1403   }
1404 
1405   if (!UseBis && Layer != NULL) {
1406     *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;
1407   }
1408 
1409   //
1410   // Build all the options for the request packet.
1411   //
1412   OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, TRUE);
1413 
1414   if (Private->IsDoDiscover) {
1415     //
1416     // Add vendor option of PXE_BOOT_ITEM
1417     //
1418     VendorOptLen      = (UINT8) ((sizeof (EFI_DHCP4_PACKET_OPTION) - 1) * 2 + sizeof (PXEBC_OPTION_BOOT_ITEM) + 1);
1419     OptList[OptCount] = AllocateZeroPool (VendorOptLen);
1420     if (OptList[OptCount] == NULL) {
1421       return EFI_OUT_OF_RESOURCES;
1422     }
1423 
1424     OptList[OptCount]->OpCode     = DHCP4_TAG_VENDOR;
1425     OptList[OptCount]->Length     = (UINT8) (VendorOptLen - 2);
1426     PxeOpt                        = (EFI_DHCP4_PACKET_OPTION *) OptList[OptCount]->Data;
1427     PxeOpt->OpCode                = PXEBC_VENDOR_TAG_BOOT_ITEM;
1428     PxeOpt->Length                = (UINT8) sizeof (PXEBC_OPTION_BOOT_ITEM);
1429     PxeBootItem                   = (PXEBC_OPTION_BOOT_ITEM *) PxeOpt->Data;
1430     PxeBootItem->Type             = HTONS (Type);
1431     PxeOpt->Data[PxeOpt->Length]  = DHCP4_TAG_EOP;
1432 
1433     if (Layer != NULL) {
1434       PxeBootItem->Layer          = HTONS (*Layer);
1435     }
1436 
1437     OptCount++;
1438   }
1439 
1440   //
1441   // Build the request packet with seed packet and option list.
1442   //
1443   Status = Dhcp4->Build (
1444                     Dhcp4,
1445                     &Private->SeedPacket,
1446                     0,
1447                     NULL,
1448                     OptCount,
1449                     OptList,
1450                     &Token.Packet
1451                     );
1452   //
1453   // Free the vendor option of PXE_BOOT_ITEM.
1454   //
1455   if (Private->IsDoDiscover) {
1456     FreePool (OptList[OptCount - 1]);
1457   }
1458 
1459   if (EFI_ERROR (Status)) {
1460     return Status;
1461   }
1462 
1463   if (Mode->SendGUID) {
1464     if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) Token.Packet->Dhcp4.Header.ClientHwAddr))) {
1465       //
1466       // Zero the Guid to indicate NOT programable if failed to get system Guid.
1467       //
1468       ZeroMem (Token.Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID));
1469     }
1470     Token.Packet->Dhcp4.Header.HwAddrLen = (UINT8)  sizeof (EFI_GUID);
1471   }
1472 
1473   //
1474   // Set fields of the token for the request packet.
1475   //
1476   Xid                                 = NET_RANDOM (NetRandomInitSeed ());
1477   Token.Packet->Dhcp4.Header.Xid      = HTONL (Xid);
1478   Token.Packet->Dhcp4.Header.Reserved = HTONS ((UINT16) ((IsBCast) ? 0x8000 : 0x0));
1479   CopyMem (&Token.Packet->Dhcp4.Header.ClientAddr, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
1480 
1481   Token.RemotePort = Sport;
1482 
1483   if (IsBCast) {
1484     SetMem (&Token.RemoteAddress, sizeof (EFI_IPv4_ADDRESS), 0xff);
1485   } else {
1486     CopyMem (&Token.RemoteAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));
1487   }
1488 
1489   CopyMem (&Token.GatewayAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));
1490 
1491   if (!IsBCast) {
1492     Token.ListenPointCount            = 1;
1493     Token.ListenPoints                = &ListenPoint;
1494     Token.ListenPoints[0].ListenPort  = PXEBC_BS_DISCOVER_PORT;
1495     CopyMem (&Token.ListenPoints[0].ListenAddress, &Private->StationIp, sizeof(EFI_IPv4_ADDRESS));
1496     CopyMem (&Token.ListenPoints[0].SubnetMask, &Private->SubnetMask, sizeof(EFI_IPv4_ADDRESS));
1497   }
1498 
1499   //
1500   // Send out the request packet to discover the bootfile.
1501   //
1502   for (TryIndex = 1; TryIndex <= PXEBC_BOOT_REQUEST_RETRIES; TryIndex++) {
1503 
1504     Token.TimeoutValue                  = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * TryIndex);
1505     Token.Packet->Dhcp4.Header.Seconds  = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * (TryIndex - 1));
1506 
1507     Status = Dhcp4->TransmitReceive (Dhcp4, &Token);
1508     if (Token.Status != EFI_TIMEOUT) {
1509       break;
1510     }
1511   }
1512 
1513   if (TryIndex > PXEBC_BOOT_REQUEST_RETRIES) {
1514     //
1515     // No server response our PXE request
1516     //
1517     Status = EFI_TIMEOUT;
1518   }
1519 
1520   if (!EFI_ERROR (Status)) {
1521 
1522     RepIndex  = 0;
1523     SrvIndex  = 0;
1524     Response  = Token.ResponseList;
1525     //
1526     // Find the right PXE Reply according to server address.
1527     //
1528     while (RepIndex < Token.ResponseCount) {
1529       if (Response->Length > PXEBC_DHCP4_PACKET_MAX_SIZE) {
1530         SrvIndex = 0;
1531         RepIndex++;
1532         Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size);
1533         continue;
1534       }
1535 
1536       while (SrvIndex < IpCount) {
1537         if (SrvList[SrvIndex].AcceptAnyResponse) {
1538           break;
1539         }
1540         if ((SrvList[SrvIndex].Type == Type) &&
1541             EFI_IP4_EQUAL (&Response->Dhcp4.Header.ServerAddr, &SrvList[SrvIndex].IpAddr)) {
1542           break;
1543         }
1544         SrvIndex++;
1545       }
1546 
1547       if ((IpCount != SrvIndex) || (IpCount == 0)) {
1548         break;
1549       }
1550 
1551       SrvIndex = 0;
1552       RepIndex++;
1553       Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size);
1554     }
1555 
1556     if (RepIndex < Token.ResponseCount) {
1557       //
1558       // Cache the right PXE reply packet here, set valid flag later.
1559       // Especially for PXE discover packet, store it into mode data here.
1560       //
1561       if (Private->IsDoDiscover) {
1562         Status = PxeBcCacheDhcp4Packet (&Private->PxeReply.Dhcp4.Packet.Ack, Response);
1563         if (EFI_ERROR(Status)) {
1564           goto ON_EXIT;
1565         }
1566         CopyMem (&Mode->PxeDiscover, &Token.Packet->Dhcp4, Token.Packet->Length);
1567       } else {
1568         Status = PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Response);
1569         if (EFI_ERROR(Status)) {
1570           goto ON_EXIT;
1571         }
1572       }
1573     } else {
1574       //
1575       // Not found the right PXE reply packet.
1576       //
1577       Status = EFI_NOT_FOUND;
1578     }
1579   }
1580 ON_EXIT:
1581 
1582   if (Token.ResponseList != NULL) {
1583     FreePool (Token.ResponseList);
1584   }
1585   if (Token.Packet != NULL) {
1586     FreePool (Token.Packet);
1587   }
1588   return Status;
1589 }
1590 
1591 /**
1592   Switch the Ip4 policy to static.
1593 
1594   @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
1595 
1596   @retval     EFI_SUCCESS         The policy is already configured to static.
1597   @retval     Others              Other error as indicated..
1598 
1599 **/
1600 EFI_STATUS
PxeBcSetIp4Policy(IN PXEBC_PRIVATE_DATA * Private)1601 PxeBcSetIp4Policy (
1602   IN PXEBC_PRIVATE_DATA            *Private
1603   )
1604 {
1605   EFI_STATUS                   Status;
1606   EFI_IP4_CONFIG2_PROTOCOL     *Ip4Config2;
1607   EFI_IP4_CONFIG2_POLICY       Policy;
1608   UINTN                        DataSize;
1609 
1610   Ip4Config2 = Private->Ip4Config2;
1611   DataSize = sizeof (EFI_IP4_CONFIG2_POLICY);
1612   Status = Ip4Config2->GetData (
1613                        Ip4Config2,
1614                        Ip4Config2DataTypePolicy,
1615                        &DataSize,
1616                        &Policy
1617                        );
1618   if (EFI_ERROR (Status)) {
1619     return Status;
1620   }
1621 
1622   if (Policy != Ip4Config2PolicyStatic) {
1623     Policy = Ip4Config2PolicyStatic;
1624     Status= Ip4Config2->SetData (
1625                           Ip4Config2,
1626                           Ip4Config2DataTypePolicy,
1627                           sizeof (EFI_IP4_CONFIG2_POLICY),
1628                           &Policy
1629                           );
1630     if (EFI_ERROR (Status)) {
1631       return Status;
1632     }
1633   }
1634 
1635   return  EFI_SUCCESS;
1636 }
1637 
1638 /**
1639   Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information.
1640 
1641   @param[in]  Private           Pointer to PxeBc private data.
1642   @param[in]  Dhcp4             Pointer to the EFI_DHCP4_PROTOCOL
1643 
1644   @retval EFI_SUCCESS           The D.O.R.A process successfully finished.
1645   @retval Others                Failed to finish the D.O.R.A process.
1646 
1647 **/
1648 EFI_STATUS
PxeBcDhcp4Dora(IN PXEBC_PRIVATE_DATA * Private,IN EFI_DHCP4_PROTOCOL * Dhcp4)1649 PxeBcDhcp4Dora (
1650   IN PXEBC_PRIVATE_DATA        *Private,
1651   IN EFI_DHCP4_PROTOCOL        *Dhcp4
1652   )
1653 {
1654   EFI_PXE_BASE_CODE_MODE       *PxeMode;
1655   EFI_DHCP4_CONFIG_DATA        Config;
1656   EFI_DHCP4_MODE_DATA          Mode;
1657   EFI_DHCP4_PACKET_OPTION      *OptList[PXEBC_DHCP4_OPTION_MAX_NUM];
1658   UINT8                        Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE];
1659   UINT32                       OptCount;
1660   EFI_STATUS                   Status;
1661 
1662   ASSERT (Dhcp4 != NULL);
1663 
1664   Status   = EFI_SUCCESS;
1665   PxeMode  = Private->PxeBc.Mode;
1666 
1667   //
1668   // Build option list for the request packet.
1669   //
1670   OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, FALSE);
1671   ASSERT (OptCount> 0);
1672 
1673   ZeroMem (&Mode, sizeof (EFI_DHCP4_MODE_DATA));
1674   ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA));
1675 
1676   Config.OptionCount      = OptCount;
1677   Config.OptionList       = OptList;
1678   Config.Dhcp4Callback    = PxeBcDhcp4CallBack;
1679   Config.CallbackContext  = Private;
1680   Config.DiscoverTryCount = PXEBC_DHCP_RETRIES;
1681   Config.DiscoverTimeout  = mPxeDhcpTimeout;
1682 
1683   //
1684   // Configure the DHCPv4 instance for PXE boot.
1685   //
1686   Status = Dhcp4->Configure (Dhcp4, &Config);
1687   if (EFI_ERROR (Status)) {
1688     goto ON_EXIT;
1689   }
1690 
1691   //
1692   // Initialize the record fields for DHCPv4 offer in private data.
1693   //
1694   Private->IsProxyRecved = FALSE;
1695   Private->OfferNum      = 0;
1696   ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
1697   ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
1698 
1699   //
1700   // Start DHCPv4 D.O.R.A. process to acquire IPv4 address. This may
1701   // have already been done, thus do not leave in error if the return
1702   // code is EFI_ALREADY_STARTED.
1703   //
1704   Status = Dhcp4->Start (Dhcp4, NULL);
1705   if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
1706     if (Status == EFI_ICMP_ERROR) {
1707       PxeMode->IcmpErrorReceived = TRUE;
1708     }
1709     goto ON_EXIT;
1710   }
1711 
1712   //
1713   // Get the acquired IPv4 address and store them.
1714   //
1715   Status = Dhcp4->GetModeData (Dhcp4, &Mode);
1716   if (EFI_ERROR (Status)) {
1717     goto ON_EXIT;
1718   }
1719 
1720   ASSERT (Mode.State == Dhcp4Bound);
1721 
1722   CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS));
1723   CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
1724   CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
1725   CopyMem (&PxeMode->StationIp, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
1726   CopyMem (&PxeMode->SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
1727 
1728   Status = PxeBcFlushStationIp (Private, &Private->StationIp, &Private->SubnetMask);
1729   if (EFI_ERROR (Status)) {
1730     goto ON_EXIT;
1731   }
1732 
1733   //
1734   // Check the selected offer whether BINL retry is needed.
1735   //
1736   Status = PxeBcHandleDhcp4Offer (Private);
1737 
1738   AsciiPrint ("\n  Station IP address is ");
1739 
1740   PxeBcShowIp4Addr (&Private->StationIp.v4);
1741   AsciiPrint ("\n");
1742 
1743 ON_EXIT:
1744   if (EFI_ERROR (Status)) {
1745     Dhcp4->Stop (Dhcp4);
1746     Dhcp4->Configure (Dhcp4, NULL);
1747   } else {
1748     ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA));
1749     Dhcp4->Configure (Dhcp4, &Config);
1750     Private->IsAddressOk = TRUE;
1751   }
1752 
1753   return Status;
1754 }
1755