]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Network/UefiPxeBcDxe/PxeBcDhcp.c
Refine code to make it more safely.
[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 - 2014, 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 /**
652 Select the specified proxy offer, such as BINL, DHCP_ONLY and so on.
653 If the proxy does not exist, try offers with bootfile.
654
655 @param Private Pointer to PxeBc private data.
656
657 **/
658 VOID
659 PxeBcSelectOffer (
660 IN PXEBC_PRIVATE_DATA *Private
661 )
662 {
663 UINT32 Index;
664 UINT32 OfferIndex;
665 EFI_DHCP4_PACKET *Offer;
666
667 Private->SelectedOffer = 0;
668
669 if (Private->SortOffers) {
670 //
671 // Select offer according to the priority
672 //
673 if (Private->ServerCount[DHCP4_PACKET_TYPE_PXE10] > 0) {
674 //
675 // DHCP with PXE10
676 //
677 Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_PXE10][0] + 1;
678
679 } else if (Private->ServerCount[DHCP4_PACKET_TYPE_WFM11A] > 0) {
680 //
681 // DHCP with WfM
682 //
683 Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_WFM11A][0] + 1;
684
685 } else if ((Private->ProxyIndex[DHCP4_PACKET_TYPE_PXE10] > 0) &&
686 (Private->ServerCount[DHCP4_PACKET_TYPE_DHCP_ONLY] > 0)
687 ) {
688 //
689 // DHCP only and proxy DHCP with PXE10
690 //
691 Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_DHCP_ONLY][0] + 1;
692 Private->ProxyOfferType = DHCP4_PACKET_TYPE_PXE10;
693
694 } else if ((Private->ProxyIndex[DHCP4_PACKET_TYPE_WFM11A] > 0) &&
695 (Private->ServerCount[DHCP4_PACKET_TYPE_DHCP_ONLY] > 0)
696 ) {
697 //
698 // DHCP only and proxy DHCP with WfM
699 //
700 Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_DHCP_ONLY][0] + 1;
701 Private->ProxyOfferType = DHCP4_PACKET_TYPE_WFM11A;
702
703 } else if (Private->ServerCount[DHCP4_PACKET_TYPE_BINL] > 0) {
704 //
705 // DHCP with BINL
706 //
707 Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_BINL][0] + 1;
708
709 } else if ((Private->ProxyIndex[DHCP4_PACKET_TYPE_BINL] > 0) &&
710 (Private->ServerCount[DHCP4_PACKET_TYPE_DHCP_ONLY] > 0)
711 ) {
712 //
713 // DHCP only and proxy DHCP with BINL
714 //
715 Private->SelectedOffer = Private->OfferIndex[DHCP4_PACKET_TYPE_DHCP_ONLY][0] + 1;
716 Private->ProxyOfferType = DHCP4_PACKET_TYPE_BINL;
717
718 } else {
719 //
720 // Try offers with bootfile
721 //
722 for (Index = 0; Index < Private->ServerCount[DHCP4_PACKET_TYPE_DHCP_ONLY]; Index++) {
723 //
724 // Select the first DHCP only offer with bootfile
725 //
726 OfferIndex = Private->OfferIndex[DHCP4_PACKET_TYPE_DHCP_ONLY][Index];
727 if (Private->Dhcp4Offers[OfferIndex].Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
728 Private->SelectedOffer = OfferIndex + 1;
729 break;
730 }
731 }
732
733 if (Private->SelectedOffer == 0) {
734 //
735 // Select the Bootp reply with bootfile if any
736 //
737 Private->SelectedOffer = Private->BootpIndex;
738 }
739 }
740 } else {
741 //
742 // Try the offers in the received order.
743 //
744 for (Index = 0; Index < Private->NumOffers; Index++) {
745
746 Offer = &Private->Dhcp4Offers[Index].Packet.Offer;
747
748 if (IS_PROXY_DHCP_OFFER (Offer)) {
749 //
750 // Skip proxy offers
751 //
752 continue;
753 }
754
755 if ((Private->Dhcp4Offers[Index].OfferType == DHCP4_PACKET_TYPE_DHCP_ONLY) &&
756 ((!Private->GotProxyOffer) && (Private->Dhcp4Offers[Index].Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL))) {
757 //
758 // DHCP only offer but no proxy offer received and no bootfile option in this offer
759 //
760 continue;
761 }
762
763 Private->SelectedOffer = Index + 1;
764 break;
765 }
766 }
767 }
768
769
770 /**
771 Callback routine.
772
773 EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver
774 to intercept events that occurred in the configuration process. This structure
775 provides advanced control of each state transition of the DHCP process. The
776 returned status code determines the behavior of the EFI DHCPv4 Protocol driver.
777 There are three possible returned values, which are described in the following
778 table.
779
780 @param This Pointer to the EFI DHCPv4 Protocol instance that is used to
781 configure this callback function.
782 @param Context Pointer to the context that is initialized by
783 EFI_DHCP4_PROTOCOL.Configure().
784 @param CurrentState The current operational state of the EFI DHCPv4 Protocol
785 driver.
786 @param Dhcp4Event The event that occurs in the current state, which usually means a
787 state transition.
788 @param Packet The DHCP packet that is going to be sent or already received.
789 @param NewPacket The packet that is used to replace the above Packet.
790
791 @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process.
792 @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol
793 driver will continue to wait for more DHCPOFFER packets until the retry
794 timeout expires.
795 @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process and
796 return to the Dhcp4Init or Dhcp4InitReboot state.
797
798 **/
799 EFI_STATUS
800 EFIAPI
801 PxeBcDhcpCallBack (
802 IN EFI_DHCP4_PROTOCOL * This,
803 IN VOID *Context,
804 IN EFI_DHCP4_STATE CurrentState,
805 IN EFI_DHCP4_EVENT Dhcp4Event,
806 IN EFI_DHCP4_PACKET * Packet OPTIONAL,
807 OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
808 )
809 {
810 PXEBC_PRIVATE_DATA *Private;
811 EFI_PXE_BASE_CODE_MODE *Mode;
812 EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;
813 EFI_DHCP4_PACKET_OPTION *MaxMsgSize;
814 UINT16 Value;
815 EFI_STATUS Status;
816 BOOLEAN Received;
817 EFI_DHCP4_HEADER *DhcpHeader;
818
819 if ((Dhcp4Event != Dhcp4RcvdOffer) &&
820 (Dhcp4Event != Dhcp4SelectOffer) &&
821 (Dhcp4Event != Dhcp4SendDiscover) &&
822 (Dhcp4Event != Dhcp4RcvdAck) &&
823 (Dhcp4Event != Dhcp4SendRequest)) {
824 return EFI_SUCCESS;
825 }
826
827 Private = (PXEBC_PRIVATE_DATA *) Context;
828 Mode = Private->PxeBc.Mode;
829 Callback = Private->PxeBcCallback;
830
831 //
832 // Override the Maximum DHCP Message Size.
833 //
834 MaxMsgSize = PxeBcParseExtendOptions (
835 Packet->Dhcp4.Option,
836 GET_OPTION_BUFFER_LEN (Packet),
837 PXEBC_DHCP4_TAG_MAXMSG
838 );
839 if (MaxMsgSize != NULL) {
840 Value = HTONS (PXEBC_DHCP4_MAX_PACKET_SIZE);
841 CopyMem (MaxMsgSize->Data, &Value, sizeof (Value));
842 }
843
844 if ((Dhcp4Event != Dhcp4SelectOffer) && (Callback != NULL)) {
845 Received = (BOOLEAN) ((Dhcp4Event == Dhcp4RcvdOffer) || (Dhcp4Event == Dhcp4RcvdAck));
846 Status = Callback->Callback (
847 Callback,
848 Private->Function,
849 Received,
850 Packet->Length,
851 (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp4
852 );
853 if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
854 return EFI_ABORTED;
855 }
856 }
857
858 Status = EFI_SUCCESS;
859
860 switch (Dhcp4Event) {
861
862 case Dhcp4SendDiscover:
863 case Dhcp4SendRequest:
864 if (Mode->SendGUID) {
865 //
866 // send the system GUID instead of the MAC address as the hardware address
867 // in the DHCP packet header.
868 //
869 DhcpHeader = &Packet->Dhcp4.Header;
870
871 if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) DhcpHeader->ClientHwAddr))) {
872 //
873 // GUID not yet set - send all 0xff's to show programable (via SetVariable)
874 // SetMem(DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof(EFI_GUID), 0xff);
875 // GUID not yet set - send all 0's to show not programable
876 //
877 ZeroMem (DhcpHeader->ClientHwAddr, sizeof (EFI_GUID));
878 }
879
880 DhcpHeader->HwAddrLen = (UINT8) sizeof (EFI_GUID);
881 }
882
883 if (Dhcp4Event == Dhcp4SendDiscover) {
884 //
885 // Cache the dhcp discover packet, of which some information will be used later.
886 //
887 CopyMem (Mode->DhcpDiscover.Raw, &Packet->Dhcp4, Packet->Length);
888 }
889
890 break;
891
892 case Dhcp4RcvdOffer:
893 Status = EFI_NOT_READY;
894 if (Private->NumOffers < PXEBC_MAX_OFFER_NUM) {
895 //
896 // Cache the dhcp offers in Private->Dhcp4Offers[]
897 //
898 PxeBcCacheDhcpOffer (Private, Packet);
899 }
900
901 break;
902
903 case Dhcp4SelectOffer:
904 //
905 // Select an offer, if succeeded, Private->SelectedOffer points to
906 // the index of the selected one.
907 //
908 PxeBcSelectOffer (Private);
909
910 if (Private->SelectedOffer == 0) {
911 Status = EFI_ABORTED;
912 } else {
913 *NewPacket = &Private->Dhcp4Offers[Private->SelectedOffer - 1].Packet.Offer;
914 }
915
916 break;
917
918 case Dhcp4RcvdAck:
919 //
920 // Cache Ack
921 //
922 ASSERT (Private->SelectedOffer != 0);
923
924 PxeBcCopyEfiDhcp4Packet (&Private->Dhcp4Ack.Packet.Ack, Packet);
925 break;
926
927 default:
928 break;
929 }
930
931 return Status;
932 }
933
934
935 /**
936 Initialize the DHCP options and build the option list.
937
938 @param Private Pointer to PxeBc private data.
939 @param OptList Pointer to a DHCP option list.
940
941 @param IsDhcpDiscover Discover dhcp option or not.
942
943 @return The index item number of the option list.
944
945 **/
946 UINT32
947 PxeBcBuildDhcpOptions (
948 IN PXEBC_PRIVATE_DATA *Private,
949 IN EFI_DHCP4_PACKET_OPTION **OptList,
950 IN BOOLEAN IsDhcpDiscover
951 )
952 {
953 UINT32 Index;
954 PXEBC_DHCP4_OPTION_ENTRY OptEnt;
955 UINT16 Value;
956
957 Index = 0;
958 OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Private->OptionBuffer;
959
960 if (!IsDhcpDiscover) {
961 //
962 // Append message type.
963 //
964 OptList[Index]->OpCode = PXEBC_DHCP4_TAG_MSG_TYPE;
965 OptList[Index]->Length = 1;
966 OptEnt.Mesg = (PXEBC_DHCP4_OPTION_MESG *) OptList[Index]->Data;
967 OptEnt.Mesg->Type = PXEBC_DHCP4_MSG_TYPE_REQUEST;
968 Index++;
969 OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
970
971 //
972 // Append max message size.
973 //
974 OptList[Index]->OpCode = PXEBC_DHCP4_TAG_MAXMSG;
975 OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE);
976 OptEnt.MaxMesgSize = (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *) OptList[Index]->Data;
977 Value = NTOHS (PXEBC_DHCP4_MAX_PACKET_SIZE);
978 CopyMem (&OptEnt.MaxMesgSize->Size, &Value, sizeof (UINT16));
979 Index++;
980 OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
981 }
982 //
983 // Parameter request list option.
984 //
985 OptList[Index]->OpCode = PXEBC_DHCP4_TAG_PARA_LIST;
986 OptList[Index]->Length = 35;
987 OptEnt.Para = (PXEBC_DHCP4_OPTION_PARA *) OptList[Index]->Data;
988 OptEnt.Para->ParaList[0] = PXEBC_DHCP4_TAG_NETMASK;
989 OptEnt.Para->ParaList[1] = PXEBC_DHCP4_TAG_TIME_OFFSET;
990 OptEnt.Para->ParaList[2] = PXEBC_DHCP4_TAG_ROUTER;
991 OptEnt.Para->ParaList[3] = PXEBC_DHCP4_TAG_TIME_SERVER;
992 OptEnt.Para->ParaList[4] = PXEBC_DHCP4_TAG_NAME_SERVER;
993 OptEnt.Para->ParaList[5] = PXEBC_DHCP4_TAG_DNS_SERVER;
994 OptEnt.Para->ParaList[6] = PXEBC_DHCP4_TAG_HOSTNAME;
995 OptEnt.Para->ParaList[7] = PXEBC_DHCP4_TAG_BOOTFILE_LEN;
996 OptEnt.Para->ParaList[8] = PXEBC_DHCP4_TAG_DOMAINNAME;
997 OptEnt.Para->ParaList[9] = PXEBC_DHCP4_TAG_ROOTPATH;
998 OptEnt.Para->ParaList[10] = PXEBC_DHCP4_TAG_EXTEND_PATH;
999 OptEnt.Para->ParaList[11] = PXEBC_DHCP4_TAG_EMTU;
1000 OptEnt.Para->ParaList[12] = PXEBC_DHCP4_TAG_TTL;
1001 OptEnt.Para->ParaList[13] = PXEBC_DHCP4_TAG_BROADCAST;
1002 OptEnt.Para->ParaList[14] = PXEBC_DHCP4_TAG_NIS_DOMAIN;
1003 OptEnt.Para->ParaList[15] = PXEBC_DHCP4_TAG_NIS_SERVER;
1004 OptEnt.Para->ParaList[16] = PXEBC_DHCP4_TAG_NTP_SERVER;
1005 OptEnt.Para->ParaList[17] = PXEBC_DHCP4_TAG_VENDOR;
1006 OptEnt.Para->ParaList[18] = PXEBC_DHCP4_TAG_REQUEST_IP;
1007 OptEnt.Para->ParaList[19] = PXEBC_DHCP4_TAG_LEASE;
1008 OptEnt.Para->ParaList[20] = PXEBC_DHCP4_TAG_SERVER_ID;
1009 OptEnt.Para->ParaList[21] = PXEBC_DHCP4_TAG_T1;
1010 OptEnt.Para->ParaList[22] = PXEBC_DHCP4_TAG_T2;
1011 OptEnt.Para->ParaList[23] = PXEBC_DHCP4_TAG_CLASS_ID;
1012 OptEnt.Para->ParaList[24] = PXEBC_DHCP4_TAG_TFTP;
1013 OptEnt.Para->ParaList[25] = PXEBC_DHCP4_TAG_BOOTFILE;
1014 OptEnt.Para->ParaList[26] = PXEBC_PXE_DHCP4_TAG_UUID;
1015 OptEnt.Para->ParaList[27] = 0x80;
1016 OptEnt.Para->ParaList[28] = 0x81;
1017 OptEnt.Para->ParaList[29] = 0x82;
1018 OptEnt.Para->ParaList[30] = 0x83;
1019 OptEnt.Para->ParaList[31] = 0x84;
1020 OptEnt.Para->ParaList[32] = 0x85;
1021 OptEnt.Para->ParaList[33] = 0x86;
1022 OptEnt.Para->ParaList[34] = 0x87;
1023 Index++;
1024 OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
1025
1026 //
1027 // Append UUID/Guid-based client identifier option
1028 //
1029 OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_UUID;
1030 OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UUID);
1031 OptEnt.Uuid = (PXEBC_DHCP4_OPTION_UUID *) OptList[Index]->Data;
1032 OptEnt.Uuid->Type = 0;
1033 Index++;
1034 OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
1035
1036 if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) {
1037 //
1038 // GUID not yet set - send all 0xff's to show programable (via SetVariable)
1039 // SetMem(DHCPV4_OPTIONS_BUFFER.DhcpPlatformId.Guid, sizeof(EFI_GUID), 0xff);
1040 // GUID not yet set - send all 0's to show not programable
1041 //
1042 ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID));
1043 }
1044
1045 //
1046 // Append client network device interface option
1047 //
1048 OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_UNDI;
1049 OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UNDI);
1050 OptEnt.Undi = (PXEBC_DHCP4_OPTION_UNDI *) OptList[Index]->Data;
1051 if (Private->Nii != NULL) {
1052 OptEnt.Undi->Type = Private->Nii->Type;
1053 OptEnt.Undi->MajorVer = Private->Nii->MajorVer;
1054 OptEnt.Undi->MinorVer = Private->Nii->MinorVer;
1055 } else {
1056 OptEnt.Undi->Type = DEFAULT_UNDI_TYPE;
1057 OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;
1058 OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;
1059 }
1060
1061 Index++;
1062 OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
1063
1064 //
1065 // Append client system architecture option
1066 //
1067 OptList[Index]->OpCode = PXEBC_PXE_DHCP4_TAG_ARCH;
1068 OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_ARCH);
1069 OptEnt.Arch = (PXEBC_DHCP4_OPTION_ARCH *) OptList[Index]->Data;
1070 Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);
1071 CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
1072 Index++;
1073 OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
1074
1075 //
1076 // Append client system architecture option
1077 //
1078 OptList[Index]->OpCode = PXEBC_DHCP4_TAG_CLASS_ID;
1079 OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_CLID);
1080 OptEnt.Clid = (PXEBC_DHCP4_OPTION_CLID *) OptList[Index]->Data;
1081 CopyMem (OptEnt.Clid, DEFAULT_CLASS_ID_DATA, sizeof (PXEBC_DHCP4_OPTION_CLID));
1082 CvtNum (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE, OptEnt.Clid->ArchitectureType, sizeof (OptEnt.Clid->ArchitectureType));
1083
1084 if (Private->Nii != NULL) {
1085 //
1086 // If NII protocol exists, update DHCP option data
1087 //
1088 CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName));
1089 CvtNum (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor));
1090 CvtNum (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor));
1091 }
1092
1093 Index++;
1094
1095 return Index;
1096 }
1097
1098
1099 /**
1100 Discover the boot of service and initialize the vendor option if exists.
1101
1102 @param Private Pointer to PxeBc private data.
1103 @param Type PxeBc option boot item type
1104 @param Layer PxeBc option boot item layer
1105 @param UseBis Use BIS or not
1106 @param DestIp Ip address for server
1107 @param IpCount The total count of the server ip address
1108 @param SrvList Server list
1109 @param IsDiscv Discover the vendor or not
1110 @param Reply The dhcp4 packet of Pxe reply
1111
1112 @retval EFI_SUCCESS Operation succeeds.
1113 @retval EFI_OUT_OF_RESOURCES Allocate memory pool failed.
1114 @retval EFI_NOT_FOUND There is no vendor option exists.
1115 @retval EFI_TIMEOUT Send Pxe Discover time out.
1116
1117 **/
1118 EFI_STATUS
1119 PxeBcDiscvBootService (
1120 IN PXEBC_PRIVATE_DATA * Private,
1121 IN UINT16 Type,
1122 IN UINT16 *Layer,
1123 IN BOOLEAN UseBis,
1124 IN EFI_IP_ADDRESS * DestIp,
1125 IN UINT16 IpCount,
1126 IN EFI_PXE_BASE_CODE_SRVLIST * SrvList,
1127 IN BOOLEAN IsDiscv,
1128 OUT EFI_DHCP4_PACKET * Reply OPTIONAL
1129 )
1130 {
1131 EFI_PXE_BASE_CODE_UDP_PORT Sport;
1132 EFI_PXE_BASE_CODE_MODE *Mode;
1133 EFI_DHCP4_PROTOCOL *Dhcp4;
1134 EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN Token;
1135 BOOLEAN IsBCast;
1136 EFI_STATUS Status;
1137 UINT16 RepIndex;
1138 UINT16 SrvIndex;
1139 UINT16 TryIndex;
1140 EFI_DHCP4_LISTEN_POINT ListenPoint;
1141 EFI_DHCP4_PACKET *Response;
1142 EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_MAX_OPTION_NUM];
1143 UINT32 OptCount;
1144 EFI_DHCP4_PACKET_OPTION *PxeOpt;
1145 PXEBC_OPTION_BOOT_ITEM *PxeBootItem;
1146 UINT8 VendorOptLen;
1147 EFI_DHCP4_HEADER *DhcpHeader;
1148 UINT32 Xid;
1149
1150 Mode = Private->PxeBc.Mode;
1151 Dhcp4 = Private->Dhcp4;
1152 Status = EFI_SUCCESS;
1153
1154 ZeroMem (&Token, sizeof (EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN));
1155
1156 if (DestIp == NULL) {
1157 Sport = PXEBC_DHCP4_S_PORT;
1158 IsBCast = TRUE;
1159 } else {
1160 Sport = PXEBC_BS_DISCOVER_PORT;
1161 IsBCast = FALSE;
1162 }
1163
1164 if (!UseBis && Layer != NULL) {
1165 *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;
1166 }
1167
1168 OptCount = PxeBcBuildDhcpOptions (Private, OptList, FALSE);
1169
1170 if (IsDiscv) {
1171 ASSERT (Layer != NULL);
1172 //
1173 // Add vendor option of PXE_BOOT_ITEM
1174 //
1175 VendorOptLen = (UINT8) ((sizeof (EFI_DHCP4_PACKET_OPTION) - 1) * 2 + sizeof (PXEBC_OPTION_BOOT_ITEM) + 1);
1176 OptList[OptCount] = AllocatePool (VendorOptLen);
1177 if (OptList[OptCount] == NULL) {
1178 return EFI_OUT_OF_RESOURCES;
1179 }
1180
1181 OptList[OptCount]->OpCode = PXEBC_DHCP4_TAG_VENDOR;
1182 OptList[OptCount]->Length = (UINT8) (VendorOptLen - 2);
1183 PxeOpt = (EFI_DHCP4_PACKET_OPTION *) OptList[OptCount]->Data;
1184 PxeOpt->OpCode = PXEBC_VENDOR_TAG_BOOT_ITEM;
1185 PxeOpt->Length = (UINT8) sizeof (PXEBC_OPTION_BOOT_ITEM);
1186 PxeBootItem = (PXEBC_OPTION_BOOT_ITEM *) PxeOpt->Data;
1187 PxeBootItem->Type = HTONS (Type);
1188 PxeBootItem->Layer = HTONS (*Layer);
1189 PxeOpt->Data[PxeOpt->Length] = PXEBC_DHCP4_TAG_EOP;
1190
1191 OptCount++;
1192 }
1193
1194 Status = Dhcp4->Build (Dhcp4, &Private->SeedPacket, 0, NULL, OptCount, OptList, &Token.Packet);
1195
1196 if (IsDiscv) {
1197 FreePool (OptList[OptCount - 1]);
1198 }
1199
1200 if (EFI_ERROR (Status)) {
1201 return Status;
1202 }
1203
1204 DhcpHeader = &Token.Packet->Dhcp4.Header;
1205 if (Mode->SendGUID) {
1206 if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) DhcpHeader->ClientHwAddr))) {
1207 //
1208 // GUID not yet set - send all 0's to show not programable
1209 //
1210 ZeroMem (DhcpHeader->ClientHwAddr, sizeof (EFI_GUID));
1211 }
1212
1213 DhcpHeader->HwAddrLen = (UINT8) sizeof (EFI_GUID);
1214 }
1215
1216 Xid = NET_RANDOM (NetRandomInitSeed ());
1217 Token.Packet->Dhcp4.Header.Xid = HTONL(Xid);
1218 Token.Packet->Dhcp4.Header.Reserved = HTONS((UINT16) ((IsBCast) ? 0x8000 : 0));
1219 CopyMem (&Token.Packet->Dhcp4.Header.ClientAddr, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
1220
1221 Token.RemotePort = Sport;
1222
1223 if (IsBCast) {
1224 SetMem (&Token.RemoteAddress, sizeof (EFI_IPv4_ADDRESS), 0xff);
1225 } else {
1226 CopyMem (&Token.RemoteAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));
1227 }
1228
1229 CopyMem (&Token.GatewayAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));
1230
1231 if (!IsBCast) {
1232 Token.ListenPointCount = 1;
1233 Token.ListenPoints = &ListenPoint;
1234 Token.ListenPoints[0].ListenPort = PXEBC_BS_DISCOVER_PORT;
1235 CopyMem (&Token.ListenPoints[0].ListenAddress, &Private->StationIp, sizeof(EFI_IPv4_ADDRESS));
1236 CopyMem (&Token.ListenPoints[0].SubnetMask, &Private->SubnetMask, sizeof(EFI_IPv4_ADDRESS));
1237 }
1238 //
1239 // Send Pxe Discover
1240 //
1241 for (TryIndex = 1; TryIndex <= PXEBC_BOOT_REQUEST_RETRIES; TryIndex++) {
1242
1243 Token.TimeoutValue = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * TryIndex);
1244 Token.Packet->Dhcp4.Header.Seconds = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * (TryIndex - 1));
1245
1246 Status = Dhcp4->TransmitReceive (Dhcp4, &Token);
1247
1248 if (Token.Status != EFI_TIMEOUT) {
1249 break;
1250 }
1251 }
1252
1253 if (TryIndex > PXEBC_BOOT_REQUEST_RETRIES) {
1254 //
1255 // No server response our PXE request
1256 //
1257 Status = EFI_TIMEOUT;
1258 }
1259
1260 if (!EFI_ERROR (Status)) {
1261 //
1262 // Find Pxe Reply
1263 //
1264 RepIndex = 0;
1265 SrvIndex = 0;
1266 Response = Token.ResponseList;
1267
1268 while (RepIndex < Token.ResponseCount) {
1269
1270 while (SrvIndex < IpCount) {
1271
1272 if (SrvList[SrvIndex].AcceptAnyResponse) {
1273 break;
1274 }
1275
1276 if ((SrvList[SrvIndex].Type == Type) && EFI_IP4_EQUAL (&(Response->Dhcp4.Header.ServerAddr), &(Private->ServerIp))) {
1277 break;
1278 }
1279
1280 SrvIndex++;
1281 }
1282
1283 if ((IpCount != SrvIndex) || (IpCount == 0)) {
1284 break;
1285 }
1286
1287 SrvIndex = 0;
1288 RepIndex++;
1289
1290 Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size);
1291 }
1292
1293 if (RepIndex < Token.ResponseCount) {
1294
1295 if (Reply != NULL) {
1296 PxeBcCopyEfiDhcp4Packet (Reply, Response);
1297 }
1298
1299 if (IsDiscv) {
1300 CopyMem (&(Mode->PxeDiscover), &(Token.Packet->Dhcp4), Token.Packet->Length);
1301 Mode->PxeDiscoverValid = TRUE;
1302
1303 CopyMem (Mode->PxeReply.Raw, &Response->Dhcp4, Response->Length);
1304 Mode->PxeReplyReceived = TRUE;
1305 }
1306 } else {
1307 Status = EFI_NOT_FOUND;
1308 }
1309
1310 //
1311 // free the responselist
1312 //
1313 if (Token.ResponseList != NULL) {
1314 FreePool (Token.ResponseList);
1315 }
1316 }
1317 //
1318 // Free the dhcp packet
1319 //
1320 FreePool (Token.Packet);
1321
1322 return Status;
1323 }
1324
1325
1326 /**
1327 Parse interested dhcp options.
1328
1329 @param Buffer Pointer to the dhcp options packet.
1330 @param Length The length of the dhcp options.
1331 @param OptTag The option OpCode.
1332
1333 @return NULL if the buffer length is 0 and OpCode is not
1334 PXEBC_DHCP4_TAG_EOP, or the pointer to the buffer.
1335
1336 **/
1337 EFI_DHCP4_PACKET_OPTION *
1338 PxeBcParseExtendOptions (
1339 IN UINT8 *Buffer,
1340 IN UINT32 Length,
1341 IN UINT8 OptTag
1342 )
1343 {
1344 EFI_DHCP4_PACKET_OPTION *Option;
1345 UINT32 Offset;
1346
1347 Option = (EFI_DHCP4_PACKET_OPTION *) Buffer;
1348 Offset = 0;
1349
1350 while (Offset < Length && Option->OpCode != PXEBC_DHCP4_TAG_EOP) {
1351
1352 if (Option->OpCode == OptTag) {
1353
1354 return Option;
1355 }
1356
1357 if (Option->OpCode == PXEBC_DHCP4_TAG_PAD) {
1358 Offset++;
1359 } else {
1360 Offset += Option->Length + 2;
1361 }
1362
1363 Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset);
1364 }
1365
1366 return NULL;
1367 }
1368
1369
1370 /**
1371 This function is to parse and check vendor options.
1372
1373 @param Dhcp4Option Pointer to dhcp options
1374 @param VendorOption Pointer to vendor options
1375
1376 @return TRUE if valid for vendor options, or FALSE.
1377
1378 **/
1379 BOOLEAN
1380 PxeBcParseVendorOptions (
1381 IN EFI_DHCP4_PACKET_OPTION *Dhcp4Option,
1382 IN PXEBC_VENDOR_OPTION *VendorOption
1383 )
1384 {
1385 UINT32 *BitMap;
1386 UINT8 VendorOptionLen;
1387 EFI_DHCP4_PACKET_OPTION *PxeOption;
1388 UINT8 Offset;
1389
1390 BitMap = VendorOption->BitMap;
1391 VendorOptionLen = Dhcp4Option->Length;
1392 PxeOption = (EFI_DHCP4_PACKET_OPTION *) &Dhcp4Option->Data[0];
1393 Offset = 0;
1394
1395 while ((Offset < VendorOptionLen) && (PxeOption->OpCode != PXEBC_DHCP4_TAG_EOP)) {
1396 //
1397 // Parse every Vendor Option and set its BitMap
1398 //
1399 switch (PxeOption->OpCode) {
1400
1401 case PXEBC_VENDOR_TAG_MTFTP_IP:
1402
1403 CopyMem (&VendorOption->MtftpIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
1404 break;
1405
1406 case PXEBC_VENDOR_TAG_MTFTP_CPORT:
1407
1408 CopyMem (&VendorOption->MtftpCPort, PxeOption->Data, sizeof (VendorOption->MtftpCPort));
1409 break;
1410
1411 case PXEBC_VENDOR_TAG_MTFTP_SPORT:
1412
1413 CopyMem (&VendorOption->MtftpSPort, PxeOption->Data, sizeof (VendorOption->MtftpSPort));
1414 break;
1415
1416 case PXEBC_VENDOR_TAG_MTFTP_TIMEOUT:
1417
1418 VendorOption->MtftpTimeout = *PxeOption->Data;
1419 break;
1420
1421 case PXEBC_VENDOR_TAG_MTFTP_DELAY:
1422
1423 VendorOption->MtftpDelay = *PxeOption->Data;
1424 break;
1425
1426 case PXEBC_VENDOR_TAG_DISCOVER_CTRL:
1427
1428 VendorOption->DiscoverCtrl = *PxeOption->Data;
1429 break;
1430
1431 case PXEBC_VENDOR_TAG_DISCOVER_MCAST:
1432
1433 CopyMem (&VendorOption->DiscoverMcastIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
1434 break;
1435
1436 case PXEBC_VENDOR_TAG_BOOT_SERVERS:
1437
1438 VendorOption->BootSvrLen = PxeOption->Length;
1439 VendorOption->BootSvr = (PXEBC_BOOT_SVR_ENTRY *) PxeOption->Data;
1440 break;
1441
1442 case PXEBC_VENDOR_TAG_BOOT_MENU:
1443
1444 VendorOption->BootMenuLen = PxeOption->Length;
1445 VendorOption->BootMenu = (PXEBC_BOOT_MENU_ENTRY *) PxeOption->Data;
1446 break;
1447
1448 case PXEBC_VENDOR_TAG_MENU_PROMPT:
1449
1450 VendorOption->MenuPromptLen = PxeOption->Length;
1451 VendorOption->MenuPrompt = (PXEBC_MENU_PROMPT *) PxeOption->Data;
1452 break;
1453
1454 case PXEBC_VENDOR_TAG_MCAST_ALLOC:
1455
1456 CopyMem (&VendorOption->McastIpBase, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
1457 CopyMem (&VendorOption->McastIpBlock, PxeOption->Data + 4, sizeof (VendorOption->McastIpBlock));
1458 CopyMem (&VendorOption->McastIpRange, PxeOption->Data + 6, sizeof (VendorOption->McastIpRange));
1459 break;
1460
1461 case PXEBC_VENDOR_TAG_CREDENTIAL_TYPES:
1462
1463 VendorOption->CredTypeLen = PxeOption->Length;
1464 VendorOption->CredType = (UINT32 *) PxeOption->Data;
1465 break;
1466
1467 case PXEBC_VENDOR_TAG_BOOT_ITEM:
1468
1469 CopyMem (&VendorOption->BootSrvType, PxeOption->Data, sizeof (VendorOption->BootSrvType));
1470 CopyMem (&VendorOption->BootSrvLayer, PxeOption->Data + 2, sizeof (VendorOption->BootSrvLayer));
1471 break;
1472 }
1473
1474 SET_VENDOR_OPTION_BIT_MAP (BitMap, PxeOption->OpCode);
1475
1476 if (PxeOption->OpCode == PXEBC_DHCP4_TAG_PAD) {
1477 Offset++;
1478 } else {
1479 Offset = (UINT8) (Offset + PxeOption->Length + 2);
1480 }
1481
1482 PxeOption = (EFI_DHCP4_PACKET_OPTION *) (Dhcp4Option->Data + Offset);
1483 }
1484
1485 //
1486 // FixMe, return falas if invalid of any vendor option
1487 //
1488
1489 return TRUE;
1490 }
1491
1492
1493 /**
1494 This function display boot item detail.
1495
1496 If the length of the boot item string over 70 Char, just display 70 Char.
1497
1498 @param Str Pointer to a string (boot item string).
1499 @param Len The length of string.
1500
1501 **/
1502 VOID
1503 PxeBcDisplayBootItem (
1504 IN UINT8 *Str,
1505 IN UINT8 Len
1506 )
1507 {
1508 UINT8 Tmp;
1509
1510 Len = (UINT8) MIN (70, Len);
1511 Tmp = Str[Len];
1512 Str[Len] = 0;
1513 AsciiPrint ("%a \n", Str);
1514 Str[Len] = Tmp;
1515 }
1516
1517
1518 /**
1519 Choose the boot prompt.
1520
1521 @param Private Pointer to PxeBc private data.
1522
1523 @retval EFI_SUCCESS Select boot prompt done.
1524 @retval EFI_TIMEOUT Select boot prompt time out.
1525 @retval EFI_NOT_FOUND The proxy offer is not Pxe10.
1526 @retval EFI_ABORTED User cancel the operation.
1527 @retval EFI_NOT_READY Read the input key from the keybroad has not finish.
1528
1529 **/
1530 EFI_STATUS
1531 PxeBcSelectBootPrompt (
1532 IN PXEBC_PRIVATE_DATA *Private
1533 )
1534 {
1535 PXEBC_CACHED_DHCP4_PACKET *Packet;
1536 PXEBC_VENDOR_OPTION *VendorOpt;
1537 EFI_EVENT TimeoutEvent;
1538 EFI_EVENT DescendEvent;
1539 EFI_INPUT_KEY InputKey;
1540 EFI_STATUS Status;
1541 UINT8 Timeout;
1542 UINT8 *Prompt;
1543 UINT8 PromptLen;
1544 INT32 SecCol;
1545 INT32 SecRow;
1546
1547 TimeoutEvent = NULL;
1548 DescendEvent = NULL;
1549
1550 if (Private->PxeBc.Mode->ProxyOfferReceived) {
1551
1552 Packet = &Private->ProxyOffer;
1553 } else {
1554
1555 Packet = &Private->Dhcp4Ack;
1556 }
1557
1558 if (Packet->OfferType != DHCP4_PACKET_TYPE_PXE10) {
1559 return EFI_NOT_FOUND;
1560 }
1561
1562 VendorOpt = &Packet->PxeVendorOption;
1563 //
1564 // According to the PXE specification 2.1, Table 2-1 PXE DHCP Options (Full
1565 // List), we must not consider a boot prompt or boot menu if all of the
1566 // following hold:
1567 // - the PXE_DISCOVERY_CONTROL PXE tag is present inside the Vendor Options
1568 // (=43) DHCP tag, and
1569 // - the PXE_DISCOVERY_CONTROL PXE tag has bit 3 set, and
1570 // - a boot file name has been presented with DHCP option 67.
1571 //
1572 if (IS_DISABLE_PROMPT_MENU (VendorOpt->DiscoverCtrl) &&
1573 Packet->Dhcp4Option[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
1574 return EFI_ABORTED;
1575 }
1576
1577 if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) {
1578 return EFI_SUCCESS;
1579 }
1580
1581 Timeout = VendorOpt->MenuPrompt->Timeout;
1582 Prompt = VendorOpt->MenuPrompt->Prompt;
1583 PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1);
1584
1585 if (Timeout == 0) {
1586 return EFI_SUCCESS;
1587 }
1588
1589 if (Timeout == 255) {
1590 return EFI_TIMEOUT;
1591 }
1592
1593 Status = gBS->CreateEvent (
1594 EVT_TIMER,
1595 TPL_CALLBACK,
1596 NULL,
1597 NULL,
1598 &TimeoutEvent
1599 );
1600
1601 if (EFI_ERROR (Status)) {
1602 return Status;
1603 }
1604
1605 Status = gBS->SetTimer (
1606 TimeoutEvent,
1607 TimerRelative,
1608 Timeout * TICKS_PER_SECOND
1609 );
1610
1611 if (EFI_ERROR (Status)) {
1612 goto ON_EXIT;
1613 }
1614
1615 Status = gBS->CreateEvent (
1616 EVT_TIMER,
1617 TPL_CALLBACK,
1618 NULL,
1619 NULL,
1620 &DescendEvent
1621 );
1622
1623 if (EFI_ERROR (Status)) {
1624 goto ON_EXIT;
1625 }
1626
1627 Status = gBS->SetTimer (
1628 DescendEvent,
1629 TimerPeriodic,
1630 TICKS_PER_SECOND
1631 );
1632
1633 if (EFI_ERROR (Status)) {
1634 goto ON_EXIT;
1635 }
1636
1637 SecCol = gST->ConOut->Mode->CursorColumn;
1638 SecRow = gST->ConOut->Mode->CursorRow;
1639
1640 PxeBcDisplayBootItem (Prompt, PromptLen);
1641
1642 gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
1643 AsciiPrint ("(%d) ", Timeout--);
1644
1645 while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
1646
1647 if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) {
1648 gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
1649 AsciiPrint ("(%d) ", Timeout--);
1650 }
1651
1652 if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
1653
1654 gBS->Stall (10 * TICKS_PER_MS);
1655 continue;
1656 }
1657
1658 if (InputKey.ScanCode == 0) {
1659
1660 switch (InputKey.UnicodeChar) {
1661 case CTRL ('c'):
1662 Status = EFI_ABORTED;
1663 break;
1664
1665 case CTRL ('m'):
1666 case 'm':
1667 case 'M':
1668 Status = EFI_TIMEOUT;
1669 break;
1670
1671 default:
1672 continue;
1673 }
1674 } else {
1675
1676 switch (InputKey.ScanCode) {
1677 case SCAN_F8:
1678 Status = EFI_TIMEOUT;
1679 break;
1680
1681 case SCAN_ESC:
1682 Status = EFI_ABORTED;
1683 break;
1684
1685 default:
1686 continue;
1687 }
1688 }
1689
1690 break;
1691 }
1692
1693 gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1);
1694
1695 ON_EXIT:
1696
1697 if (DescendEvent != NULL) {
1698 gBS->CloseEvent (DescendEvent);
1699 }
1700
1701 if (TimeoutEvent != NULL) {
1702 gBS->CloseEvent (TimeoutEvent);
1703 }
1704
1705 return Status;
1706 }
1707
1708
1709 /**
1710 Select the boot menu.
1711
1712 @param Private Pointer to PxeBc private data.
1713 @param Type The type of the menu.
1714 @param UseDefaultItem Use default item or not.
1715
1716 @retval EFI_ABORTED User cancel operation.
1717 @retval EFI_SUCCESS Select the boot menu success.
1718 @retval EFI_NOT_READY Read the input key from the keybroad has not finish.
1719
1720 **/
1721 EFI_STATUS
1722 PxeBcSelectBootMenu (
1723 IN PXEBC_PRIVATE_DATA *Private,
1724 OUT UINT16 *Type,
1725 IN BOOLEAN UseDefaultItem
1726 )
1727 {
1728 PXEBC_CACHED_DHCP4_PACKET *Packet;
1729 PXEBC_VENDOR_OPTION *VendorOpt;
1730 EFI_INPUT_KEY InputKey;
1731 UINT8 MenuSize;
1732 UINT8 MenuNum;
1733 INT32 TopRow;
1734 UINT16 Select;
1735 UINT16 LastSelect;
1736 UINT8 Index;
1737 BOOLEAN Finish;
1738 CHAR8 Blank[70];
1739 PXEBC_BOOT_MENU_ENTRY *MenuItem;
1740 PXEBC_BOOT_MENU_ENTRY *MenuArray[PXEBC_MAX_MENU_NUM];
1741
1742 Finish = FALSE;
1743 Select = 1;
1744 Index = 0;
1745 *Type = 0;
1746
1747 if (Private->PxeBc.Mode->ProxyOfferReceived) {
1748
1749 Packet = &Private->ProxyOffer;
1750 } else {
1751
1752 Packet = &Private->Dhcp4Ack;
1753 }
1754
1755 ASSERT (Packet->OfferType == DHCP4_PACKET_TYPE_PXE10);
1756
1757 VendorOpt = &Packet->PxeVendorOption;
1758
1759 if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) {
1760 return EFI_SUCCESS;
1761 }
1762
1763 SetMem (Blank, sizeof(Blank), ' ');
1764
1765 MenuSize = VendorOpt->BootMenuLen;
1766 MenuItem = VendorOpt->BootMenu;
1767
1768 if (MenuSize == 0) {
1769 return EFI_NOT_READY;
1770 }
1771
1772 while (MenuSize > 0) {
1773 MenuArray[Index++] = MenuItem;
1774 MenuSize = (UINT8) (MenuSize - (MenuItem->DescLen + 3));
1775 MenuItem = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3);
1776 if (Index >= PXEBC_MAX_MENU_NUM) {
1777 break;
1778 }
1779 }
1780
1781 if (UseDefaultItem) {
1782 *Type = MenuArray[0]->Type;
1783 *Type = NTOHS (*Type);
1784 return EFI_SUCCESS;
1785 }
1786
1787 MenuNum = Index;
1788
1789 for (Index = 0; Index < MenuNum; Index++) {
1790 PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen);
1791 }
1792
1793 TopRow = gST->ConOut->Mode->CursorRow - MenuNum;
1794
1795 do {
1796 ASSERT (Select < PXEBC_MAX_MENU_NUM);
1797 //
1798 // highlight selected row
1799 //
1800 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
1801 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select);
1802 Blank[MenuArray[Select]->DescLen] = 0;
1803 AsciiPrint ("%a\r", Blank);
1804 PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen);
1805 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
1806 LastSelect = Select;
1807
1808 while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
1809 gBS->Stall (10 * TICKS_PER_MS);
1810 }
1811
1812 if (InputKey.ScanCode != 0) {
1813 switch (InputKey.UnicodeChar) {
1814 case CTRL ('c'):
1815 InputKey.ScanCode = SCAN_ESC;
1816 break;
1817
1818 case CTRL ('j'): /* linefeed */
1819 case CTRL ('m'): /* return */
1820 Finish = TRUE;
1821 break;
1822
1823 case CTRL ('i'): /* tab */
1824 case ' ':
1825 case 'd':
1826 case 'D':
1827 InputKey.ScanCode = SCAN_DOWN;
1828 break;
1829
1830 case CTRL ('h'): /* backspace */
1831 case 'u':
1832 case 'U':
1833 InputKey.ScanCode = SCAN_UP;
1834 break;
1835
1836 default:
1837 InputKey.ScanCode = 0;
1838 }
1839 }
1840
1841 switch (InputKey.ScanCode) {
1842 case SCAN_LEFT:
1843 case SCAN_UP:
1844 if (Select > 0) {
1845 --Select;
1846 }
1847
1848 break;
1849
1850 case SCAN_DOWN:
1851 case SCAN_RIGHT:
1852 if (++Select == MenuNum) {
1853 --Select;
1854 }
1855
1856 break;
1857
1858 case SCAN_PAGE_UP:
1859 case SCAN_HOME:
1860 Select = 0;
1861 break;
1862
1863 case SCAN_PAGE_DOWN:
1864 case SCAN_END:
1865 Select = (UINT16) (MenuNum - 1);
1866 break;
1867
1868 case SCAN_ESC:
1869 return EFI_ABORTED;
1870 }
1871
1872 /* unhighlight last selected row */
1873 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
1874 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect);
1875 Blank[MenuArray[LastSelect]->DescLen] = 0;
1876 AsciiPrint ("%a\r", Blank);
1877 PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen);
1878 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
1879 } while (!Finish);
1880
1881 ASSERT (Select < PXEBC_MAX_MENU_NUM);
1882
1883 //
1884 // Swap the byte order
1885 //
1886 CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16));
1887 *Type = NTOHS (*Type);
1888
1889 return EFI_SUCCESS;
1890 }
1891