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