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