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