]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c
Add NetworkPkg (P.UDK2010.UP3.Network.P1)
[mirror_edk2.git] / NetworkPkg / UefiPxeBcDxe / PxeBcBoot.c
1 /** @file
2 Boot functions implementation for UefiPxeBc Driver.
3
4 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
5
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php.
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "PxeBcImpl.h"
17
18
19 /**
20 Display the string of the boot item.
21
22 If the length of the boot item string beyond 70 Char, just display 70 Char.
23
24 @param[in] Str The pointer to the string.
25 @param[in] Len The length of the string.
26
27 **/
28 VOID
29 PxeBcDisplayBootItem (
30 IN UINT8 *Str,
31 IN UINT8 Len
32 )
33 {
34 UINT8 Tmp;
35
36 //
37 // Cut off the chars behind 70th.
38 //
39 Len = (UINT8) MIN (PXEBC_DISPLAY_MAX_LINE, Len);
40 Tmp = Str[Len];
41 Str[Len] = 0;
42 AsciiPrint ("%a \n", Str);
43
44 //
45 // Restore the original 70th char.
46 //
47 Str[Len] = Tmp;
48 }
49
50
51 /**
52 Select and maintain the boot prompt if needed.
53
54 @param[in] Private Pointer to PxeBc private data.
55
56 @retval EFI_SUCCESS Selected boot prompt done.
57 @retval EFI_TIMEOUT Selected boot prompt timed out.
58 @retval EFI_NOT_FOUND The proxy offer is not Pxe10.
59 @retval EFI_ABORTED User cancelled the operation.
60 @retval EFI_NOT_READY Reading the input key from the keyboard has not finish.
61
62 **/
63 EFI_STATUS
64 PxeBcSelectBootPrompt (
65 IN PXEBC_PRIVATE_DATA *Private
66 )
67 {
68 PXEBC_DHCP_PACKET_CACHE *Cache;
69 PXEBC_VENDOR_OPTION *VendorOpt;
70 EFI_PXE_BASE_CODE_MODE *Mode;
71 EFI_EVENT TimeoutEvent;
72 EFI_EVENT DescendEvent;
73 EFI_INPUT_KEY InputKey;
74 EFI_STATUS Status;
75 UINT32 OfferType;
76 UINT8 Timeout;
77 UINT8 *Prompt;
78 UINT8 PromptLen;
79 INT32 SecCol;
80 INT32 SecRow;
81
82 TimeoutEvent = NULL;
83 DescendEvent = NULL;
84 Mode = Private->PxeBc.Mode;
85 Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;
86 OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;
87
88 //
89 // Only ProxyPxe10 offer needs boot prompt.
90 //
91 if (OfferType != PxeOfferTypeProxyPxe10) {
92 return EFI_NOT_FOUND;
93 }
94
95 //
96 // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.
97 //
98 ASSERT (!Mode->UsingIpv6);
99
100 VendorOpt = &Cache->Dhcp4.VendorOpt;
101 if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) {
102 return EFI_SUCCESS;
103 }
104
105 Timeout = VendorOpt->MenuPrompt->Timeout;
106 Prompt = VendorOpt->MenuPrompt->Prompt;
107 PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1);
108
109 //
110 // The valid scope of Timeout refers to PXE2.1 spec.
111 //
112 if (Timeout == 0) {
113 return EFI_SUCCESS;
114 }
115 if (Timeout == 255) {
116 return EFI_TIMEOUT;
117 }
118
119 //
120 // Create and start a timer as timeout event.
121 //
122 Status = gBS->CreateEvent (
123 EVT_TIMER,
124 TPL_CALLBACK,
125 NULL,
126 NULL,
127 &TimeoutEvent
128 );
129 if (EFI_ERROR (Status)) {
130 return Status;
131 }
132
133 Status = gBS->SetTimer (
134 TimeoutEvent,
135 TimerRelative,
136 Timeout * TICKS_PER_SECOND
137 );
138 if (EFI_ERROR (Status)) {
139 goto ON_EXIT;
140 }
141
142 //
143 // Create and start a periodic timer as descend event by second.
144 //
145 Status = gBS->CreateEvent (
146 EVT_TIMER,
147 TPL_CALLBACK,
148 NULL,
149 NULL,
150 &DescendEvent
151 );
152 if (EFI_ERROR (Status)) {
153 goto ON_EXIT;
154 }
155
156 Status = gBS->SetTimer (
157 DescendEvent,
158 TimerPeriodic,
159 TICKS_PER_SECOND
160 );
161 if (EFI_ERROR (Status)) {
162 goto ON_EXIT;
163 }
164
165 //
166 // Display the boot item and cursor on the screen.
167 //
168 SecCol = gST->ConOut->Mode->CursorColumn;
169 SecRow = gST->ConOut->Mode->CursorRow;
170
171 PxeBcDisplayBootItem (Prompt, PromptLen);
172
173 gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
174 AsciiPrint ("(%d) ", Timeout--);
175
176 while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
177 if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) {
178 gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
179 AsciiPrint ("(%d) ", Timeout--);
180 }
181 if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
182 gBS->Stall (10 * TICKS_PER_MS);
183 continue;
184 }
185 //
186 // Parse the input key by user.
187 //
188 if (InputKey.ScanCode == 0) {
189
190 switch (InputKey.UnicodeChar) {
191
192 case CTRL ('c'):
193 Status = EFI_ABORTED;
194 break;
195
196 case CTRL ('m'):
197 case 'm':
198 case 'M':
199 Status = EFI_TIMEOUT;
200 break;
201
202 default:
203 continue;
204 }
205
206 } else {
207
208 switch (InputKey.ScanCode) {
209
210 case SCAN_F8:
211 Status = EFI_TIMEOUT;
212 break;
213
214 case SCAN_ESC:
215 Status = EFI_ABORTED;
216 break;
217
218 default:
219 continue;
220 }
221 }
222
223 break;
224 }
225
226 //
227 // Reset the cursor on the screen.
228 //
229 gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1);
230
231 ON_EXIT:
232 if (DescendEvent != NULL) {
233 gBS->CloseEvent (DescendEvent);
234 }
235 if (TimeoutEvent != NULL) {
236 gBS->CloseEvent (TimeoutEvent);
237 }
238
239 return Status;
240 }
241
242
243 /**
244 Select the boot menu by user's input.
245
246 @param[in] Private Pointer to PxeBc private data.
247 @param[out] Type The type of the menu.
248 @param[in] UseDefaultItem Use default item or not.
249
250 @retval EFI_ABORTED User cancel operation.
251 @retval EFI_SUCCESS Select the boot menu success.
252 @retval EFI_NOT_READY Read the input key from the keybroad has not finish.
253
254 **/
255 EFI_STATUS
256 PxeBcSelectBootMenu (
257 IN PXEBC_PRIVATE_DATA *Private,
258 OUT UINT16 *Type,
259 IN BOOLEAN UseDefaultItem
260 )
261 {
262 EFI_PXE_BASE_CODE_MODE *Mode;
263 PXEBC_DHCP_PACKET_CACHE *Cache;
264 PXEBC_VENDOR_OPTION *VendorOpt;
265 EFI_INPUT_KEY InputKey;
266 UINT32 OfferType;
267 UINT8 MenuSize;
268 UINT8 MenuNum;
269 INT32 TopRow;
270 UINT16 Select;
271 UINT16 LastSelect;
272 UINT8 Index;
273 BOOLEAN Finish;
274 CHAR8 Blank[PXEBC_DISPLAY_MAX_LINE];
275 PXEBC_BOOT_MENU_ENTRY *MenuItem;
276 PXEBC_BOOT_MENU_ENTRY *MenuArray[PXEBC_MENU_MAX_NUM];
277
278 Finish = FALSE;
279 Select = 1;
280 Index = 0;
281 *Type = 0;
282 Mode = Private->PxeBc.Mode;
283 Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;
284 OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;
285
286 //
287 // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.
288 //
289 ASSERT (!Mode->UsingIpv6);
290 ASSERT (OfferType == PxeOfferTypeProxyPxe10);
291
292 VendorOpt = &Cache->Dhcp4.VendorOpt;
293 if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) {
294 return EFI_SUCCESS;
295 }
296
297 //
298 // Display the boot menu on the screen.
299 //
300 SetMem (Blank, sizeof(Blank), ' ');
301
302 MenuSize = VendorOpt->BootMenuLen;
303 MenuItem = VendorOpt->BootMenu;
304
305 if (MenuSize == 0) {
306 return EFI_DEVICE_ERROR;
307 }
308
309 while (MenuSize > 0 && Index < PXEBC_MENU_MAX_NUM) {
310 ASSERT (MenuItem != NULL);
311 MenuArray[Index] = MenuItem;
312 MenuSize = (UINT8) (MenuSize - (MenuItem->DescLen + 3));
313 MenuItem = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3);
314 Index++;
315 }
316
317 if (UseDefaultItem) {
318 ASSERT (MenuArray[0] != NULL);
319 CopyMem (Type, &MenuArray[0]->Type, sizeof (UINT16));
320 *Type = NTOHS (*Type);
321 return EFI_SUCCESS;
322 }
323
324 MenuNum = Index;
325
326 for (Index = 0; Index < MenuNum; Index++) {
327 ASSERT (MenuArray[Index] != NULL);
328 PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen);
329 }
330
331 TopRow = gST->ConOut->Mode->CursorRow - MenuNum;
332
333 //
334 // Select the boot item by user in the boot menu.
335 //
336 do {
337 //
338 // Highlight selected row.
339 //
340 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
341 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select);
342 ASSERT (Select < PXEBC_MENU_MAX_NUM);
343 ASSERT (MenuArray[Select] != NULL);
344 Blank[MenuArray[Select]->DescLen] = 0;
345 AsciiPrint ("%a\r", Blank);
346 PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen);
347 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
348 LastSelect = Select;
349
350 while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
351 gBS->Stall (10 * TICKS_PER_MS);
352 }
353
354 if (InputKey.ScanCode != 0) {
355 switch (InputKey.UnicodeChar) {
356 case CTRL ('c'):
357 InputKey.ScanCode = SCAN_ESC;
358 break;
359
360 case CTRL ('j'): /* linefeed */
361 case CTRL ('m'): /* return */
362 Finish = TRUE;
363 break;
364
365 case CTRL ('i'): /* tab */
366 case ' ':
367 case 'd':
368 case 'D':
369 InputKey.ScanCode = SCAN_DOWN;
370 break;
371
372 case CTRL ('h'): /* backspace */
373 case 'u':
374 case 'U':
375 InputKey.ScanCode = SCAN_UP;
376 break;
377
378 default:
379 InputKey.ScanCode = 0;
380 }
381 }
382
383 switch (InputKey.ScanCode) {
384 case SCAN_LEFT:
385 case SCAN_UP:
386 if (Select != 0) {
387 Select--;
388 }
389 break;
390
391 case SCAN_DOWN:
392 case SCAN_RIGHT:
393 if (++Select == MenuNum) {
394 Select--;
395 }
396 break;
397
398 case SCAN_PAGE_UP:
399 case SCAN_HOME:
400 Select = 0;
401 break;
402
403 case SCAN_PAGE_DOWN:
404 case SCAN_END:
405 Select = (UINT16) (MenuNum - 1);
406 break;
407
408 case SCAN_ESC:
409 return EFI_ABORTED;
410 }
411
412 //
413 // Unhighlight the last selected row.
414 //
415 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
416 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect);
417 ASSERT (LastSelect < PXEBC_MENU_MAX_NUM);
418 ASSERT (MenuArray[LastSelect] != NULL);
419 Blank[MenuArray[LastSelect]->DescLen] = 0;
420 AsciiPrint ("%a\r", Blank);
421 PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen);
422 gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
423 } while (!Finish);
424
425 //
426 // Swap the byte order.
427 //
428 ASSERT (Select < PXEBC_MENU_MAX_NUM);
429 ASSERT (MenuArray[Select] != NULL);
430 CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16));
431 *Type = NTOHS (*Type);
432
433 return EFI_SUCCESS;
434 }
435
436
437 /**
438 Parse out the boot information from the last Dhcp4 reply packet.
439
440 @param[in, out] Private Pointer to PxeBc private data.
441 @param[out] BufferSize Size of the boot file to be downloaded.
442
443 @retval EFI_SUCCESS Successfully parsed out all the boot information.
444 @retval Others Failed to parse out the boot information.
445
446 **/
447 EFI_STATUS
448 PxeBcDhcp4BootInfo (
449 IN OUT PXEBC_PRIVATE_DATA *Private,
450 OUT UINT64 *BufferSize
451 )
452 {
453 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
454 EFI_PXE_BASE_CODE_MODE *Mode;
455 EFI_STATUS Status;
456 PXEBC_DHCP4_PACKET_CACHE *Cache4;
457 UINT16 Value;
458
459 PxeBc = &Private->PxeBc;
460 Mode = PxeBc->Mode;
461 Status = EFI_SUCCESS;
462 *BufferSize = 0;
463
464 //
465 // Get the last received Dhcp4 reply packet.
466 //
467 if (Mode->PxeReplyReceived) {
468 Cache4 = &Private->PxeReply.Dhcp4;
469 } else if (Mode->ProxyOfferReceived) {
470 Cache4 = &Private->ProxyOffer.Dhcp4;
471 } else {
472 Cache4 = &Private->DhcpAck.Dhcp4;
473 }
474
475 //
476 // Parse the boot server Ipv4 address by next server address.
477 // If this field isn't available, use option 54 instead.
478 //
479 CopyMem (
480 &Private->ServerIp,
481 &Cache4->Packet.Offer.Dhcp4.Header.ServerAddr,
482 sizeof (EFI_IPv4_ADDRESS)
483 );
484
485 if (Private->ServerIp.Addr[0] == 0) {
486 CopyMem (
487 &Private->ServerIp,
488 Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,
489 sizeof (EFI_IPv4_ADDRESS)
490 );
491 }
492
493 //
494 // Parse the boot file name by option.
495 //
496 ASSERT (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);
497 Private->BootFileName = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data;
498
499 if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN] != NULL) {
500 //
501 // Parse the boot file size by option.
502 //
503 CopyMem (&Value, Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN]->Data, sizeof (Value));
504 Value = NTOHS (Value);
505 //
506 // The field of boot file size is 512 bytes in unit.
507 //
508 *BufferSize = 512 * Value;
509 } else {
510 //
511 // Get the bootfile size by tftp command if no option available.
512 //
513 Status = PxeBc->Mtftp (
514 PxeBc,
515 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
516 NULL,
517 FALSE,
518 BufferSize,
519 &Private->BlockSize,
520 &Private->ServerIp,
521 Private->BootFileName,
522 NULL,
523 FALSE
524 );
525 }
526
527 //
528 // Save the value of boot file size.
529 //
530 Private->BootFileSize = (UINTN) *BufferSize;
531
532 //
533 // Display all the information: boot server address, boot file name and boot file size.
534 //
535 AsciiPrint ("\n Server IP address is ");
536 PxeBcShowIp4Addr (&Private->ServerIp.v4);
537 AsciiPrint ("\n NBP filename is %a", Private->BootFileName);
538 AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize);
539
540 return Status;
541 }
542
543
544 /**
545 Parse out the boot information from the last Dhcp6 reply packet.
546
547 @param[in, out] Private Pointer to PxeBc private data.
548 @param[out] BufferSize Size of the boot file to be downloaded.
549
550 @retval EFI_SUCCESS Successfully parsed out all the boot information.
551 @retval EFI_BUFFER_TOO_SMALL
552 @retval Others Failed to parse out the boot information.
553
554 **/
555 EFI_STATUS
556 PxeBcDhcp6BootInfo (
557 IN OUT PXEBC_PRIVATE_DATA *Private,
558 OUT UINT64 *BufferSize
559 )
560 {
561 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
562 EFI_PXE_BASE_CODE_MODE *Mode;
563 EFI_STATUS Status;
564 PXEBC_DHCP6_PACKET_CACHE *Cache6;
565 UINT16 Value;
566
567 PxeBc = &Private->PxeBc;
568 Mode = PxeBc->Mode;
569 Status = EFI_SUCCESS;
570 *BufferSize = 0;
571
572 //
573 // Get the last received Dhcp6 reply packet.
574 //
575 if (Mode->PxeReplyReceived) {
576 Cache6 = &Private->PxeReply.Dhcp6;
577 } else if (Mode->ProxyOfferReceived) {
578 Cache6 = &Private->ProxyOffer.Dhcp6;
579 } else {
580 Cache6 = &Private->DhcpAck.Dhcp6;
581 }
582
583 ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
584
585 //
586 // Parse (m)tftp server ip address and bootfile name.
587 //
588 Status = PxeBcExtractBootFileUrl (
589 &Private->BootFileName,
590 &Private->ServerIp.v6,
591 (CHAR8 *) (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),
592 NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)
593 );
594 if (EFI_ERROR (Status)) {
595 return Status;
596 }
597
598 //
599 // Parse the value of boot file size.
600 //
601 if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] != NULL) {
602 //
603 // Parse it out if have the boot file parameter option.
604 //
605 Status = PxeBcExtractBootFileParam ((CHAR8 *) Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM]->Data, &Value);
606 if (EFI_ERROR (Status)) {
607 return Status;
608 }
609 //
610 // The field of boot file size is 512 bytes in unit.
611 //
612 *BufferSize = 512 * Value;
613 } else {
614 //
615 // Send get file size command by tftp if option unavailable.
616 //
617 Status = PxeBc->Mtftp (
618 PxeBc,
619 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
620 NULL,
621 FALSE,
622 BufferSize,
623 &Private->BlockSize,
624 &Private->ServerIp,
625 Private->BootFileName,
626 NULL,
627 FALSE
628 );
629 }
630
631 //
632 // Save the value of boot file size.
633 //
634 Private->BootFileSize = (UINTN) *BufferSize;
635
636 //
637 // Display all the information: boot server address, boot file name and boot file size.
638 //
639 AsciiPrint ("\n Server IP address is ");
640 PxeBcShowIp6Addr (&Private->ServerIp.v6);
641 AsciiPrint ("\n NBP filename is %a", Private->BootFileName);
642 AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize);
643
644 return Status;
645 }
646
647
648 /**
649 Extract the discover information and boot server entry from the
650 cached packets if unspecified.
651
652 @param[in] Private Pointer to PxeBc private data.
653 @param[in] Type The type of bootstrap to perform.
654 @param[in, out] Info Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.
655 @param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY.
656 @param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST.
657
658 @retval EFI_SUCCESS Successfully extracted the information.
659 @retval EFI_DEVICE_ERROR Failed to extract the information.
660
661 **/
662 EFI_STATUS
663 PxeBcExtractDiscoverInfo (
664 IN PXEBC_PRIVATE_DATA *Private,
665 IN UINT16 Type,
666 IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO *Info,
667 OUT PXEBC_BOOT_SVR_ENTRY **BootEntry,
668 OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList
669 )
670 {
671 EFI_PXE_BASE_CODE_MODE *Mode;
672 PXEBC_DHCP4_PACKET_CACHE *Cache4;
673 PXEBC_VENDOR_OPTION *VendorOpt;
674 PXEBC_BOOT_SVR_ENTRY *Entry;
675 BOOLEAN IsFound;
676
677 Mode = Private->PxeBc.Mode;
678
679 if (Mode->UsingIpv6) {
680 Info->IpCnt = 1;
681 Info->UseUCast = TRUE;
682
683 Info->SrvList[0].Type = Type;
684 Info->SrvList[0].AcceptAnyResponse = FALSE;
685
686 //
687 // There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet.
688 //
689 CopyMem (&Info->SrvList[0].IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));
690
691 *SrvList = Info->SrvList;
692 } else {
693 Entry = NULL;
694 IsFound = FALSE;
695 Cache4 = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer.Dhcp4 : &Private->DhcpAck.Dhcp4;
696 VendorOpt = &Cache4->VendorOpt;
697
698 if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) {
699 //
700 // Address is not acquired or no discovery options.
701 //
702 return EFI_INVALID_PARAMETER;
703 }
704
705 //
706 // Parse the boot server entry from the vendor option in the last cached packet.
707 //
708 Info->UseMCast = (BOOLEAN) !IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl);
709 Info->UseBCast = (BOOLEAN) !IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl);
710 Info->MustUseList = (BOOLEAN) IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl);
711 Info->UseUCast = Info->MustUseList;
712
713 if (Info->UseMCast) {
714 //
715 // Get the multicast discover ip address from vendor option if has.
716 //
717 CopyMem (&Info->ServerMCastIp.v4, &VendorOpt->DiscoverMcastIp, sizeof (EFI_IPv4_ADDRESS));
718 }
719
720 Info->IpCnt = 0;
721
722 if (Info->MustUseList) {
723 Entry = VendorOpt->BootSvr;
724
725 while (((UINT8) (Entry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) {
726 if (Entry->Type == HTONS (Type)) {
727 IsFound = TRUE;
728 break;
729 }
730 Entry = GET_NEXT_BOOT_SVR_ENTRY (Entry);
731 }
732
733 if (!IsFound) {
734 return EFI_DEVICE_ERROR;
735 }
736
737 Info->IpCnt = Entry->IpCnt;
738 }
739
740 *BootEntry = Entry;
741 }
742
743 return EFI_SUCCESS;
744 }
745
746
747 /**
748 Build the discover packet and send out for boot server.
749
750 @param[in] Private Pointer to PxeBc private data.
751 @param[in] Type PxeBc option boot item type.
752 @param[in] Layer Pointer to option boot item layer.
753 @param[in] UseBis Use BIS or not.
754 @param[in] DestIp Pointer to the destination address.
755 @param[in] IpCount The count of the server address.
756 @param[in] SrvList Pointer to the server address list.
757
758 @retval EFI_SUCCESS Successfully discovered boot file.
759 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.
760 @retval EFI_NOT_FOUND Can't get the PXE reply packet.
761 @retval Others Failed to discover boot file.
762
763 **/
764 EFI_STATUS
765 PxeBcDiscoverBootServer (
766 IN PXEBC_PRIVATE_DATA *Private,
767 IN UINT16 Type,
768 IN UINT16 *Layer,
769 IN BOOLEAN UseBis,
770 IN EFI_IP_ADDRESS *DestIp,
771 IN UINT16 IpCount,
772 IN EFI_PXE_BASE_CODE_SRVLIST *SrvList
773 )
774 {
775 if (Private->PxeBc.Mode->UsingIpv6) {
776 return PxeBcDhcp6Discover (
777 Private,
778 Type,
779 Layer,
780 UseBis,
781 DestIp
782 );
783 } else {
784 return PxeBcDhcp4Discover (
785 Private,
786 Type,
787 Layer,
788 UseBis,
789 DestIp,
790 IpCount,
791 SrvList
792 );
793 }
794 }
795
796
797 /**
798 Discover all the boot information for boot file.
799
800 @param[in, out] Private Pointer to PxeBc private data.
801 @param[out] BufferSize Size of the boot file to be downloaded.
802
803 @retval EFI_SUCCESS Successfully obtained all the boot information .
804 @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
805 @retval EFI_ABORTED User cancel current operation.
806 @retval Others Failed to parse out the boot information.
807
808 **/
809 EFI_STATUS
810 PxeBcDiscoverBootFile (
811 IN OUT PXEBC_PRIVATE_DATA *Private,
812 OUT UINT64 *BufferSize
813 )
814 {
815 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
816 EFI_PXE_BASE_CODE_MODE *Mode;
817 EFI_STATUS Status;
818 UINT16 Type;
819 UINT16 Layer;
820 BOOLEAN UseBis;
821
822 PxeBc = &Private->PxeBc;
823 Mode = PxeBc->Mode;
824 Type = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP;
825 Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL;
826
827 //
828 // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
829 // other pxe boot information.
830 //
831 Status = PxeBc->Dhcp (PxeBc, TRUE);
832 if (EFI_ERROR (Status)) {
833 return Status;
834 }
835
836 //
837 // Select a boot server from boot server list.
838 //
839 Status = PxeBcSelectBootPrompt (Private);
840
841 if (Status == EFI_SUCCESS) {
842 //
843 // Choose by user's input.
844 //
845 Status = PxeBcSelectBootMenu (Private, &Type, TRUE);
846 } else if (Status == EFI_TIMEOUT) {
847 //
848 // Choose by default item.
849 //
850 Status = PxeBcSelectBootMenu (Private, &Type, FALSE);
851 }
852
853 if (!EFI_ERROR (Status)) {
854
855 if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) {
856 //
857 // Local boot(PXE bootstrap server) need abort
858 //
859 return EFI_ABORTED;
860 }
861
862 //
863 // Start to discover the boot server to get (m)tftp server ip address, bootfile
864 // name and bootfile size.
865 //
866 UseBis = (BOOLEAN) (Mode->BisSupported && Mode->BisDetected);
867 Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL);
868 if (EFI_ERROR (Status)) {
869 return Status;
870 }
871 }
872
873 //
874 // Parse the boot information.
875 //
876 if (Mode->UsingIpv6) {
877 Status = PxeBcDhcp6BootInfo (Private, BufferSize);
878 } else {
879 Status = PxeBcDhcp4BootInfo (Private, BufferSize);
880 }
881
882 return Status;
883 }
884
885
886 /**
887 Install PxeBaseCodeCallbackProtocol if not installed before.
888
889 @param[in, out] Private Pointer to PxeBc private data.
890 @param[out] NewMakeCallback If TRUE, it is a new callback.
891 Otherwise, it is not new callback.
892 @retval EFI_SUCCESS PxeBaseCodeCallbackProtocol installed succesfully.
893 @retval Others Failed to install PxeBaseCodeCallbackProtocol.
894
895 **/
896 EFI_STATUS
897 PxeBcInstallCallback (
898 IN OUT PXEBC_PRIVATE_DATA *Private,
899 OUT BOOLEAN *NewMakeCallback
900 )
901 {
902 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
903 EFI_STATUS Status;
904
905 //
906 // Check whether PxeBaseCodeCallbackProtocol already installed.
907 //
908 PxeBc = &Private->PxeBc;
909 Status = gBS->HandleProtocol (
910 Private->Controller,
911 &gEfiPxeBaseCodeCallbackProtocolGuid,
912 (VOID **) &Private->PxeBcCallback
913 );
914 if (Status == EFI_UNSUPPORTED) {
915
916 CopyMem (
917 &Private->LoadFileCallback,
918 &gPxeBcCallBackTemplate,
919 sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL)
920 );
921
922 //
923 // Install a default callback if user didn't offer one.
924 //
925 Status = gBS->InstallProtocolInterface (
926 &Private->Controller,
927 &gEfiPxeBaseCodeCallbackProtocolGuid,
928 EFI_NATIVE_INTERFACE,
929 &Private->LoadFileCallback
930 );
931
932 (*NewMakeCallback) = (BOOLEAN) (Status == EFI_SUCCESS);
933
934 Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback);
935 if (EFI_ERROR (Status)) {
936 PxeBc->Stop (PxeBc);
937 return Status;
938 }
939 }
940
941 return EFI_SUCCESS;
942 }
943
944
945 /**
946 Uninstall PxeBaseCodeCallbackProtocol.
947
948 @param[in] Private Pointer to PxeBc private data.
949 @param[in] NewMakeCallback If TRUE, it is a new callback.
950 Otherwise, it is not new callback.
951
952 **/
953 VOID
954 PxeBcUninstallCallback (
955 IN PXEBC_PRIVATE_DATA *Private,
956 IN BOOLEAN NewMakeCallback
957 )
958 {
959 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
960
961 PxeBc = &Private->PxeBc;
962
963 if (NewMakeCallback) {
964
965 NewMakeCallback = FALSE;
966
967 PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback);
968
969 gBS->UninstallProtocolInterface (
970 Private->Controller,
971 &gEfiPxeBaseCodeCallbackProtocolGuid,
972 &Private->LoadFileCallback
973 );
974 }
975 }
976
977
978 /**
979 Download one of boot file in the list, and it's special for IPv6.
980
981 @param[in] Private Pointer to PxeBc private data.
982 @param[in, out] BufferSize Size of user buffer for input;
983 required buffer size for output.
984 @param[in] Buffer Pointer to user buffer.
985
986 @retval EFI_SUCCESS Read one of boot file in the list successfully.
987 @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
988 @retval EFI_NOT_FOUND There is no proper boot file available.
989 @retval Others Failed to download boot file in the list.
990
991 **/
992 EFI_STATUS
993 PxeBcReadBootFileList (
994 IN PXEBC_PRIVATE_DATA *Private,
995 IN OUT UINT64 *BufferSize,
996 IN VOID *Buffer OPTIONAL
997 )
998 {
999 EFI_STATUS Status;
1000 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
1001
1002 PxeBc = &Private->PxeBc;
1003
1004 //
1005 // Try to download the boot file if everything is ready.
1006 //
1007 if (Buffer != NULL) {
1008 Status = PxeBc->Mtftp (
1009 PxeBc,
1010 EFI_PXE_BASE_CODE_TFTP_READ_FILE,
1011 Buffer,
1012 FALSE,
1013 BufferSize,
1014 &Private->BlockSize,
1015 &Private->ServerIp,
1016 Private->BootFileName,
1017 NULL,
1018 FALSE
1019 );
1020
1021
1022 } else {
1023 Status = EFI_BUFFER_TOO_SMALL;
1024 }
1025
1026 return Status;
1027 }
1028
1029
1030 /**
1031 Load boot file into user buffer.
1032
1033 @param[in] Private Pointer to PxeBc private data.
1034 @param[in, out] BufferSize Size of user buffer for input;
1035 required buffer size for output.
1036 @param[in] Buffer Pointer to user buffer.
1037
1038 @retval EFI_SUCCESS Get all the boot information successfully.
1039 @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
1040 @retval EFI_ABORTED User cancelled the current operation.
1041 @retval Others Failed to parse out the boot information.
1042
1043 **/
1044 EFI_STATUS
1045 PxeBcLoadBootFile (
1046 IN PXEBC_PRIVATE_DATA *Private,
1047 IN OUT UINTN *BufferSize,
1048 IN VOID *Buffer OPTIONAL
1049 )
1050 {
1051 BOOLEAN NewMakeCallback;
1052 UINT64 RequiredSize;
1053 UINT64 CurrentSize;
1054 EFI_STATUS Status;
1055 EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
1056 EFI_PXE_BASE_CODE_MODE *PxeBcMode;
1057
1058 NewMakeCallback = FALSE;
1059 PxeBc = &Private->PxeBc;
1060 PxeBcMode = &Private->Mode;
1061 CurrentSize = *BufferSize;
1062 RequiredSize = 0;
1063
1064 //
1065 // Install pxebc callback protocol if hasn't been installed yet.
1066 //
1067 Status = PxeBcInstallCallback (Private, &NewMakeCallback);
1068 if (EFI_ERROR(Status)) {
1069 return Status;
1070 }
1071
1072 if (Private->BootFileSize == 0) {
1073 //
1074 // Discover the boot information about the bootfile if hasn't.
1075 //
1076 Status = PxeBcDiscoverBootFile (Private, &RequiredSize);
1077
1078 if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize)) {
1079 //
1080 // It's error if the required buffer size is beyond the system scope.
1081 //
1082 Status = EFI_DEVICE_ERROR;
1083 goto ON_EXIT;
1084 } else if (RequiredSize > 0) {
1085 //
1086 // Get the right buffer size of the bootfile required.
1087 //
1088 if (CurrentSize < RequiredSize || Buffer == NULL) {
1089 //
1090 // It's buffer too small if the size of user buffer is smaller than the required.
1091 //
1092 CurrentSize = RequiredSize;
1093 Status = EFI_BUFFER_TOO_SMALL;
1094 goto ON_EXIT;
1095 }
1096 CurrentSize = RequiredSize;
1097 } else if (RequiredSize == 0 && PxeBcMode->UsingIpv6) {
1098 //
1099 // Try to download another bootfile in list if failed to get the filesize of the last one.
1100 // It's special for the case of IPv6.
1101 //
1102 Status = PxeBcReadBootFileList (Private, &CurrentSize, Buffer);
1103 goto ON_EXIT;
1104 }
1105 } else if (CurrentSize < Private->BootFileSize || Buffer == NULL ) {
1106 //
1107 // It's buffer too small if the size of user buffer is smaller than the required.
1108 //
1109 CurrentSize = Private->BootFileSize;
1110 Status = EFI_BUFFER_TOO_SMALL;
1111 goto ON_EXIT;
1112 }
1113
1114 //
1115 // Begin to download the bootfile if everything is ready.
1116 //
1117 AsciiPrint ("\n Downloading NBP file...\n");
1118 if (PxeBcMode->UsingIpv6) {
1119 Status = PxeBcReadBootFileList (
1120 Private,
1121 &CurrentSize,
1122 Buffer
1123 );
1124 } else {
1125 Status = PxeBc->Mtftp (
1126 PxeBc,
1127 EFI_PXE_BASE_CODE_TFTP_READ_FILE,
1128 Buffer,
1129 FALSE,
1130 &CurrentSize,
1131 &Private->BlockSize,
1132 &Private->ServerIp,
1133 Private->BootFileName,
1134 NULL,
1135 FALSE
1136 );
1137 }
1138
1139 ON_EXIT:
1140 *BufferSize = (UINTN) CurrentSize;
1141 PxeBcUninstallCallback(Private, NewMakeCallback);
1142
1143 if (Status == EFI_SUCCESS) {
1144 AsciiPrint ("\n Succeed to download NBP file.\n");
1145 return EFI_SUCCESS;
1146 } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) {
1147 AsciiPrint ("\n PXE-E05: Buffer size is smaller than the requested file.\n");
1148 } else if (Status == EFI_DEVICE_ERROR) {
1149 AsciiPrint ("\n PXE-E07: Network device error.\n");
1150 } else if (Status == EFI_OUT_OF_RESOURCES) {
1151 AsciiPrint ("\n PXE-E09: Could not allocate I/O buffers.\n");
1152 } else if (Status == EFI_NO_MEDIA) {
1153 AsciiPrint ("\n PXE-E12: Could not detect network connection.\n");
1154 } else if (Status == EFI_NO_RESPONSE) {
1155 AsciiPrint ("\n PXE-E16: No offer received.\n");
1156 } else if (Status == EFI_TIMEOUT) {
1157 AsciiPrint ("\n PXE-E18: Server response timeout.\n");
1158 } else if (Status == EFI_ABORTED) {
1159 AsciiPrint ("\n PXE-E21: Remote boot cancelled.\n");
1160 } else if (Status == EFI_ICMP_ERROR) {
1161 AsciiPrint ("\n PXE-E22: Client received ICMP error from server.\n");
1162 } else if (Status == EFI_TFTP_ERROR) {
1163 AsciiPrint ("\n PXE-E23: Client received TFTP error from server.\n");
1164 } else if (Status != EFI_BUFFER_TOO_SMALL) {
1165 AsciiPrint ("\n PXE-E99: Unexpected network error.\n");
1166 }
1167
1168 return Status;
1169 }
1170