123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272 |
- /** @file
- Boot functions implementation for UefiPxeBc Driver.
- Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
- (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include "PxeBcImpl.h"
- /**
- Display the string of the boot item.
- If the length of the boot item string beyond 70 Char, just display 70 Char.
- @param[in] Str The pointer to the string.
- @param[in] Len The length of the string.
- **/
- VOID
- PxeBcDisplayBootItem (
- IN UINT8 *Str,
- IN UINT8 Len
- )
- {
- UINT8 Tmp;
- //
- // Cut off the chars behind 70th.
- //
- Len = (UINT8)MIN (PXEBC_DISPLAY_MAX_LINE, Len);
- Tmp = Str[Len];
- Str[Len] = 0;
- AsciiPrint ("%a \n", Str);
- //
- // Restore the original 70th char.
- //
- Str[Len] = Tmp;
- }
- /**
- Select and maintain the boot prompt if needed.
- @param[in] Private Pointer to PxeBc private data.
- @retval EFI_SUCCESS Selected boot prompt done.
- @retval EFI_TIMEOUT Selected boot prompt timed out.
- @retval EFI_NOT_FOUND The proxy offer is not Pxe10.
- @retval EFI_ABORTED User cancelled the operation.
- @retval EFI_NOT_READY Reading the input key from the keyboard has not finish.
- **/
- EFI_STATUS
- PxeBcSelectBootPrompt (
- IN PXEBC_PRIVATE_DATA *Private
- )
- {
- PXEBC_DHCP_PACKET_CACHE *Cache;
- PXEBC_VENDOR_OPTION *VendorOpt;
- EFI_PXE_BASE_CODE_MODE *Mode;
- EFI_EVENT TimeoutEvent;
- EFI_EVENT DescendEvent;
- EFI_INPUT_KEY InputKey;
- EFI_STATUS Status;
- UINT32 OfferType;
- UINT8 Timeout;
- UINT8 *Prompt;
- UINT8 PromptLen;
- INT32 SecCol;
- INT32 SecRow;
- TimeoutEvent = NULL;
- DescendEvent = NULL;
- Mode = Private->PxeBc.Mode;
- Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;
- OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;
- //
- // Only DhcpPxe10 and ProxyPxe10 offer needs boot prompt.
- //
- if ((OfferType != PxeOfferTypeProxyPxe10) && (OfferType != PxeOfferTypeDhcpPxe10)) {
- return EFI_NOT_FOUND;
- }
- //
- // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.
- //
- ASSERT (!Mode->UsingIpv6);
- VendorOpt = &Cache->Dhcp4.VendorOpt;
- //
- // According to the PXE specification 2.1, Table 2-1 PXE DHCP Options,
- // we must not consider a boot prompt or boot menu if all of the following hold:
- // - the PXE_DISCOVERY_CONTROL tag(6) is present inside the Vendor Options(43), and has bit 3 set
- // - a boot file name has been presented in the initial DHCP or ProxyDHCP offer packet.
- //
- if (IS_DISABLE_PROMPT_MENU (VendorOpt->DiscoverCtrl) &&
- (Cache->Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL))
- {
- return EFI_ABORTED;
- }
- if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) {
- return EFI_TIMEOUT;
- }
- Timeout = VendorOpt->MenuPrompt->Timeout;
- Prompt = VendorOpt->MenuPrompt->Prompt;
- PromptLen = (UINT8)(VendorOpt->MenuPromptLen - 1);
- //
- // The valid scope of Timeout refers to PXE2.1 spec.
- //
- if (Timeout == 0) {
- return EFI_TIMEOUT;
- }
- if (Timeout == 255) {
- return EFI_SUCCESS;
- }
- //
- // Create and start a timer as timeout event.
- //
- Status = gBS->CreateEvent (
- EVT_TIMER,
- TPL_CALLBACK,
- NULL,
- NULL,
- &TimeoutEvent
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- Status = gBS->SetTimer (
- TimeoutEvent,
- TimerRelative,
- MultU64x32 (Timeout, TICKS_PER_SECOND)
- );
- if (EFI_ERROR (Status)) {
- goto ON_EXIT;
- }
- //
- // Create and start a periodic timer as descend event by second.
- //
- Status = gBS->CreateEvent (
- EVT_TIMER,
- TPL_CALLBACK,
- NULL,
- NULL,
- &DescendEvent
- );
- if (EFI_ERROR (Status)) {
- goto ON_EXIT;
- }
- Status = gBS->SetTimer (
- DescendEvent,
- TimerPeriodic,
- TICKS_PER_SECOND
- );
- if (EFI_ERROR (Status)) {
- goto ON_EXIT;
- }
- //
- // Display the boot item and cursor on the screen.
- //
- SecCol = gST->ConOut->Mode->CursorColumn;
- SecRow = gST->ConOut->Mode->CursorRow;
- PxeBcDisplayBootItem (Prompt, PromptLen);
- gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
- AsciiPrint ("(%d) ", Timeout--);
- Status = EFI_TIMEOUT;
- while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
- if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) {
- gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
- AsciiPrint ("(%d) ", Timeout--);
- }
- if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
- gBS->Stall (10 * TICKS_PER_MS);
- continue;
- }
- //
- // Parse the input key by user.
- // If <F8> or <Ctrl> + <M> is pressed, return success to display the boot menu.
- //
- if (InputKey.ScanCode == 0) {
- switch (InputKey.UnicodeChar) {
- case CTRL ('c'):
- Status = EFI_ABORTED;
- break;
- case CTRL ('m'):
- case 'm':
- case 'M':
- Status = EFI_SUCCESS;
- break;
- default:
- continue;
- }
- } else {
- switch (InputKey.ScanCode) {
- case SCAN_F8:
- Status = EFI_SUCCESS;
- break;
- case SCAN_ESC:
- Status = EFI_ABORTED;
- break;
- default:
- continue;
- }
- }
- break;
- }
- //
- // Reset the cursor on the screen.
- //
- gST->ConOut->SetCursorPosition (gST->ConOut, 0, SecRow + 1);
- ON_EXIT:
- if (DescendEvent != NULL) {
- gBS->CloseEvent (DescendEvent);
- }
- if (TimeoutEvent != NULL) {
- gBS->CloseEvent (TimeoutEvent);
- }
- return Status;
- }
- /**
- Select the boot menu by user's input.
- @param[in] Private Pointer to PxeBc private data.
- @param[out] Type The type of the menu.
- @param[in] UseDefaultItem Use default item or not.
- @retval EFI_ABORTED User cancel operation.
- @retval EFI_SUCCESS Select the boot menu success.
- @retval EFI_NOT_READY Read the input key from the keyboard has not finish.
- **/
- EFI_STATUS
- PxeBcSelectBootMenu (
- IN PXEBC_PRIVATE_DATA *Private,
- OUT UINT16 *Type,
- IN BOOLEAN UseDefaultItem
- )
- {
- EFI_PXE_BASE_CODE_MODE *Mode;
- PXEBC_DHCP_PACKET_CACHE *Cache;
- PXEBC_VENDOR_OPTION *VendorOpt;
- EFI_INPUT_KEY InputKey;
- UINT32 OfferType;
- UINT8 MenuSize;
- UINT8 MenuNum;
- INT32 TopRow;
- UINT16 Select;
- UINT16 LastSelect;
- UINT8 Index;
- BOOLEAN Finish;
- CHAR8 Blank[PXEBC_DISPLAY_MAX_LINE];
- PXEBC_BOOT_MENU_ENTRY *MenuItem;
- PXEBC_BOOT_MENU_ENTRY *MenuArray[PXEBC_MENU_MAX_NUM];
- Finish = FALSE;
- Select = 0;
- Index = 0;
- *Type = 0;
- Mode = Private->PxeBc.Mode;
- Cache = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;
- OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;
- //
- // There is no specified DhcpPxe10/ProxyPxe10 for IPv6 in PXE and UEFI spec.
- //
- ASSERT (!Mode->UsingIpv6);
- ASSERT (OfferType == PxeOfferTypeProxyPxe10 || OfferType == PxeOfferTypeDhcpPxe10);
- VendorOpt = &Cache->Dhcp4.VendorOpt;
- if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) {
- return EFI_SUCCESS;
- }
- //
- // Display the boot menu on the screen.
- //
- SetMem (Blank, sizeof (Blank), ' ');
- MenuSize = VendorOpt->BootMenuLen;
- MenuItem = VendorOpt->BootMenu;
- if (MenuSize == 0) {
- return EFI_DEVICE_ERROR;
- }
- while (MenuSize > 0 && Index < PXEBC_MENU_MAX_NUM) {
- ASSERT (MenuItem != NULL);
- MenuArray[Index] = MenuItem;
- MenuSize = (UINT8)(MenuSize - (MenuItem->DescLen + 3));
- MenuItem = (PXEBC_BOOT_MENU_ENTRY *)((UINT8 *)MenuItem + MenuItem->DescLen + 3);
- Index++;
- }
- if (UseDefaultItem) {
- ASSERT (MenuArray[0] != NULL);
- CopyMem (Type, &MenuArray[0]->Type, sizeof (UINT16));
- *Type = NTOHS (*Type);
- return EFI_SUCCESS;
- }
- MenuNum = Index;
- for (Index = 0; Index < MenuNum; Index++) {
- ASSERT (MenuArray[Index] != NULL);
- PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen);
- }
- TopRow = gST->ConOut->Mode->CursorRow - MenuNum;
- //
- // Select the boot item by user in the boot menu.
- //
- do {
- //
- // Highlight selected row.
- //
- gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
- gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select);
- ASSERT (Select < PXEBC_MENU_MAX_NUM);
- ASSERT (MenuArray[Select] != NULL);
- Blank[MenuArray[Select]->DescLen] = 0;
- AsciiPrint ("%a\r", Blank);
- PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen);
- gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
- LastSelect = Select;
- while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
- gBS->Stall (10 * TICKS_PER_MS);
- }
- if (InputKey.ScanCode == 0) {
- switch (InputKey.UnicodeChar) {
- case CTRL ('c'):
- InputKey.ScanCode = SCAN_ESC;
- break;
- case CTRL ('j'): /* linefeed */
- case CTRL ('m'): /* return */
- Finish = TRUE;
- break;
- case CTRL ('i'): /* tab */
- case ' ':
- case 'd':
- case 'D':
- InputKey.ScanCode = SCAN_DOWN;
- break;
- case CTRL ('h'): /* backspace */
- case 'u':
- case 'U':
- InputKey.ScanCode = SCAN_UP;
- break;
- default:
- InputKey.ScanCode = 0;
- }
- }
- switch (InputKey.ScanCode) {
- case SCAN_LEFT:
- case SCAN_UP:
- if (Select != 0) {
- Select--;
- }
- break;
- case SCAN_DOWN:
- case SCAN_RIGHT:
- if (++Select == MenuNum) {
- Select--;
- }
- break;
- case SCAN_PAGE_UP:
- case SCAN_HOME:
- Select = 0;
- break;
- case SCAN_PAGE_DOWN:
- case SCAN_END:
- Select = (UINT16)(MenuNum - 1);
- break;
- case SCAN_ESC:
- return EFI_ABORTED;
- }
- //
- // Unhighlight the last selected row.
- //
- gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
- gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect);
- ASSERT (LastSelect < PXEBC_MENU_MAX_NUM);
- ASSERT (MenuArray[LastSelect] != NULL);
- Blank[MenuArray[LastSelect]->DescLen] = 0;
- AsciiPrint ("%a\r", Blank);
- PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen);
- gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
- } while (!Finish);
- //
- // Swap the byte order.
- //
- ASSERT (Select < PXEBC_MENU_MAX_NUM);
- ASSERT (MenuArray[Select] != NULL);
- CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16));
- *Type = NTOHS (*Type);
- return EFI_SUCCESS;
- }
- /**
- Parse out the boot information from the last Dhcp4 reply packet.
- @param[in, out] Private Pointer to PxeBc private data.
- @param[out] BufferSize Size of the boot file to be downloaded.
- @retval EFI_SUCCESS Successfully parsed out all the boot information.
- @retval Others Failed to parse out the boot information.
- **/
- EFI_STATUS
- PxeBcDhcp4BootInfo (
- IN OUT PXEBC_PRIVATE_DATA *Private,
- OUT UINT64 *BufferSize
- )
- {
- EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
- EFI_PXE_BASE_CODE_MODE *Mode;
- EFI_STATUS Status;
- PXEBC_DHCP4_PACKET_CACHE *Cache4;
- UINT16 Value;
- PXEBC_VENDOR_OPTION *VendorOpt;
- PXEBC_BOOT_SVR_ENTRY *Entry;
- PxeBc = &Private->PxeBc;
- Mode = PxeBc->Mode;
- Status = EFI_SUCCESS;
- *BufferSize = 0;
- //
- // Get the last received Dhcp4 reply packet.
- //
- if (Mode->PxeReplyReceived) {
- Cache4 = &Private->PxeReply.Dhcp4;
- } else if (Mode->ProxyOfferReceived) {
- Cache4 = &Private->ProxyOffer.Dhcp4;
- } else {
- Cache4 = &Private->DhcpAck.Dhcp4;
- }
- if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {
- //
- // This should never happen in a correctly configured DHCP / PXE
- // environment. One misconfiguration that can cause it is two DHCP servers
- // mistakenly running on the same network segment at the same time, and
- // racing each other in answering DHCP requests. Thus, the DHCP packets
- // that the edk2 PXE client considers "belonging together" may actually be
- // entirely independent, coming from two (competing) DHCP servers.
- //
- // Try to deal with this gracefully. Note that this check is not
- // comprehensive, as we don't try to identify all such errors.
- //
- return EFI_PROTOCOL_ERROR;
- }
- //
- // Parse the boot server address.
- // If prompt/discover is disabled, get the first boot server from the boot servers list.
- // Otherwise, parse the boot server Ipv4 address from next server address field in DHCP header.
- // If all these fields are not available, use option 54 instead.
- //
- VendorOpt = &Cache4->VendorOpt;
- if (IS_DISABLE_PROMPT_MENU (VendorOpt->DiscoverCtrl) && IS_VALID_BOOT_SERVERS (VendorOpt->BitMap)) {
- Entry = VendorOpt->BootSvr;
- if ((VendorOpt->BootSvrLen >= sizeof (PXEBC_BOOT_SVR_ENTRY)) && (Entry->IpCnt > 0)) {
- CopyMem (
- &Private->ServerIp,
- &Entry->IpAddr[0],
- sizeof (EFI_IPv4_ADDRESS)
- );
- }
- }
- if (Private->ServerIp.Addr[0] == 0) {
- //
- // ServerIp.Addr[0] equals zero means we failed to get IP address from boot server list.
- // Try to use next server address field.
- //
- CopyMem (
- &Private->ServerIp,
- &Cache4->Packet.Offer.Dhcp4.Header.ServerAddr,
- sizeof (EFI_IPv4_ADDRESS)
- );
- }
- if (Private->ServerIp.Addr[0] == 0) {
- //
- // Still failed , use the IP address from option 54.
- //
- CopyMem (
- &Private->ServerIp,
- Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,
- sizeof (EFI_IPv4_ADDRESS)
- );
- }
- //
- // Parse the boot file name by option.
- //
- Private->BootFileName = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data;
- if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN] != NULL) {
- //
- // Parse the boot file size by option.
- //
- CopyMem (&Value, Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN]->Data, sizeof (Value));
- Value = NTOHS (Value);
- //
- // The field of boot file size is 512 bytes in unit.
- //
- *BufferSize = 512 * Value;
- } else {
- //
- // Get the bootfile size by tftp command if no option available.
- //
- Status = PxeBc->Mtftp (
- PxeBc,
- EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
- NULL,
- FALSE,
- BufferSize,
- &Private->BlockSize,
- &Private->ServerIp,
- Private->BootFileName,
- NULL,
- FALSE
- );
- }
- //
- // Save the value of boot file size.
- //
- Private->BootFileSize = (UINTN)*BufferSize;
- //
- // Display all the information: boot server address, boot file name and boot file size.
- //
- AsciiPrint ("\n Server IP address is ");
- PxeBcShowIp4Addr (&Private->ServerIp.v4);
- AsciiPrint ("\n NBP filename is %a", Private->BootFileName);
- AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize);
- return Status;
- }
- /**
- Parse out the boot information from the last Dhcp6 reply packet.
- @param[in, out] Private Pointer to PxeBc private data.
- @param[out] BufferSize Size of the boot file to be downloaded.
- @retval EFI_SUCCESS Successfully parsed out all the boot information.
- @retval EFI_BUFFER_TOO_SMALL
- @retval Others Failed to parse out the boot information.
- **/
- EFI_STATUS
- PxeBcDhcp6BootInfo (
- IN OUT PXEBC_PRIVATE_DATA *Private,
- OUT UINT64 *BufferSize
- )
- {
- EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
- EFI_PXE_BASE_CODE_MODE *Mode;
- EFI_STATUS Status;
- PXEBC_DHCP6_PACKET_CACHE *Cache6;
- UINT16 Value;
- PxeBc = &Private->PxeBc;
- Mode = PxeBc->Mode;
- Status = EFI_SUCCESS;
- *BufferSize = 0;
- //
- // Get the last received Dhcp6 reply packet.
- //
- if (Mode->PxeReplyReceived) {
- Cache6 = &Private->PxeReply.Dhcp6;
- } else if (Mode->ProxyOfferReceived) {
- Cache6 = &Private->ProxyOffer.Dhcp6;
- } else {
- Cache6 = &Private->DhcpAck.Dhcp6;
- }
- if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {
- //
- // This should never happen in a correctly configured DHCP / PXE
- // environment. One misconfiguration that can cause it is two DHCP servers
- // mistakenly running on the same network segment at the same time, and
- // racing each other in answering DHCP requests. Thus, the DHCP packets
- // that the edk2 PXE client considers "belonging together" may actually be
- // entirely independent, coming from two (competing) DHCP servers.
- //
- // Try to deal with this gracefully. Note that this check is not
- // comprehensive, as we don't try to identify all such errors.
- //
- return EFI_PROTOCOL_ERROR;
- }
- //
- // Set the station address to IP layer.
- //
- Status = PxeBcSetIp6Address (Private);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- //
- // Parse (m)tftp server ip address and bootfile name.
- //
- Status = PxeBcExtractBootFileUrl (
- Private,
- &Private->BootFileName,
- &Private->ServerIp.v6,
- (CHAR8 *)(Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),
- NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- //
- // Parse the value of boot file size.
- //
- if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] != NULL) {
- //
- // Parse it out if have the boot file parameter option.
- //
- Status = PxeBcExtractBootFileParam ((CHAR8 *)Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM]->Data, &Value);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- //
- // The field of boot file size is 512 bytes in unit.
- //
- *BufferSize = 512 * Value;
- } else {
- //
- // Send get file size command by tftp if option unavailable.
- //
- Status = PxeBc->Mtftp (
- PxeBc,
- EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
- NULL,
- FALSE,
- BufferSize,
- &Private->BlockSize,
- &Private->ServerIp,
- Private->BootFileName,
- NULL,
- FALSE
- );
- }
- //
- // Save the value of boot file size.
- //
- Private->BootFileSize = (UINTN)*BufferSize;
- //
- // Display all the information: boot server address, boot file name and boot file size.
- //
- AsciiPrint ("\n Server IP address is ");
- PxeBcShowIp6Addr (&Private->ServerIp.v6);
- AsciiPrint ("\n NBP filename is %a", Private->BootFileName);
- AsciiPrint ("\n NBP filesize is %d Bytes", Private->BootFileSize);
- return Status;
- }
- /**
- Extract the discover information and boot server entry from the
- cached packets if unspecified.
- @param[in] Private Pointer to PxeBc private data.
- @param[in] Type The type of bootstrap to perform.
- @param[in, out] DiscoverInfo Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.
- @param[out] BootEntry Pointer to PXEBC_BOOT_SVR_ENTRY.
- @param[out] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST.
- @retval EFI_SUCCESS Successfully extracted the information.
- @retval EFI_DEVICE_ERROR Failed to extract the information.
- **/
- EFI_STATUS
- PxeBcExtractDiscoverInfo (
- IN PXEBC_PRIVATE_DATA *Private,
- IN UINT16 Type,
- IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO **DiscoverInfo,
- OUT PXEBC_BOOT_SVR_ENTRY **BootEntry,
- OUT EFI_PXE_BASE_CODE_SRVLIST **SrvList
- )
- {
- EFI_PXE_BASE_CODE_MODE *Mode;
- PXEBC_DHCP4_PACKET_CACHE *Cache4;
- PXEBC_VENDOR_OPTION *VendorOpt;
- PXEBC_BOOT_SVR_ENTRY *Entry;
- BOOLEAN IsFound;
- EFI_PXE_BASE_CODE_DISCOVER_INFO *Info;
- UINT16 Index;
- Mode = Private->PxeBc.Mode;
- Info = *DiscoverInfo;
- if (Mode->UsingIpv6) {
- Info->IpCnt = 1;
- Info->UseUCast = TRUE;
- Info->SrvList[0].Type = Type;
- Info->SrvList[0].AcceptAnyResponse = FALSE;
- //
- // There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet.
- //
- CopyMem (&Info->SrvList[0].IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));
- *SrvList = Info->SrvList;
- } else {
- Entry = NULL;
- IsFound = FALSE;
- Cache4 = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer.Dhcp4 : &Private->DhcpAck.Dhcp4;
- VendorOpt = &Cache4->VendorOpt;
- if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) {
- //
- // Address is not acquired or no discovery options.
- //
- return EFI_INVALID_PARAMETER;
- }
- //
- // Parse the boot server entry from the vendor option in the last cached packet.
- //
- Info->UseMCast = (BOOLEAN) !IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl);
- Info->UseBCast = (BOOLEAN) !IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl);
- Info->MustUseList = (BOOLEAN)IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl);
- Info->UseUCast = (BOOLEAN)IS_VALID_BOOT_SERVERS (VendorOpt->BitMap);
- if (Info->UseMCast) {
- //
- // Get the multicast discover ip address from vendor option if has.
- //
- CopyMem (&Info->ServerMCastIp.v4, &VendorOpt->DiscoverMcastIp, sizeof (EFI_IPv4_ADDRESS));
- }
- Info->IpCnt = 0;
- if (Info->UseUCast) {
- Entry = VendorOpt->BootSvr;
- while (((UINT8)(Entry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) {
- if (Entry->Type == HTONS (Type)) {
- IsFound = TRUE;
- break;
- }
- Entry = GET_NEXT_BOOT_SVR_ENTRY (Entry);
- }
- if (!IsFound) {
- return EFI_DEVICE_ERROR;
- }
- Info->IpCnt = Entry->IpCnt;
- if (Info->IpCnt >= 1) {
- *DiscoverInfo = AllocatePool (sizeof (*Info) + (Info->IpCnt - 1) * sizeof (**SrvList));
- if (*DiscoverInfo == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- CopyMem (*DiscoverInfo, Info, sizeof (*Info));
- Info = *DiscoverInfo;
- }
- for (Index = 0; Index < Info->IpCnt; Index++) {
- CopyMem (&Info->SrvList[Index].IpAddr, &Entry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS));
- Info->SrvList[Index].AcceptAnyResponse = !Info->MustUseList;
- Info->SrvList[Index].Type = NTOHS (Entry->Type);
- }
- }
- *BootEntry = Entry;
- *SrvList = Info->SrvList;
- }
- return EFI_SUCCESS;
- }
- /**
- Build the discover packet and send out for boot server.
- @param[in] Private Pointer to PxeBc private data.
- @param[in] Type PxeBc option boot item type.
- @param[in] Layer Pointer to option boot item layer.
- @param[in] UseBis Use BIS or not.
- @param[in] DestIp Pointer to the destination address.
- @param[in] IpCount The count of the server address.
- @param[in] SrvList Pointer to the server address list.
- @retval EFI_SUCCESS Successfully discovered boot file.
- @retval EFI_OUT_OF_RESOURCES Failed to allocate resource.
- @retval EFI_NOT_FOUND Can't get the PXE reply packet.
- @retval Others Failed to discover boot file.
- **/
- EFI_STATUS
- PxeBcDiscoverBootServer (
- IN PXEBC_PRIVATE_DATA *Private,
- IN UINT16 Type,
- IN UINT16 *Layer,
- IN BOOLEAN UseBis,
- IN EFI_IP_ADDRESS *DestIp,
- IN UINT16 IpCount,
- IN EFI_PXE_BASE_CODE_SRVLIST *SrvList
- )
- {
- if (Private->PxeBc.Mode->UsingIpv6) {
- return PxeBcDhcp6Discover (
- Private,
- Type,
- Layer,
- UseBis,
- DestIp
- );
- } else {
- return PxeBcDhcp4Discover (
- Private,
- Type,
- Layer,
- UseBis,
- DestIp,
- IpCount,
- SrvList
- );
- }
- }
- /**
- Discover all the boot information for boot file.
- @param[in, out] Private Pointer to PxeBc private data.
- @param[out] BufferSize Size of the boot file to be downloaded.
- @retval EFI_SUCCESS Successfully obtained all the boot information .
- @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
- @retval EFI_ABORTED User cancel current operation.
- @retval Others Failed to parse out the boot information.
- **/
- EFI_STATUS
- PxeBcDiscoverBootFile (
- IN OUT PXEBC_PRIVATE_DATA *Private,
- OUT UINT64 *BufferSize
- )
- {
- EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
- EFI_PXE_BASE_CODE_MODE *Mode;
- EFI_STATUS Status;
- UINT16 Type;
- UINT16 Layer;
- BOOLEAN UseBis;
- PxeBc = &Private->PxeBc;
- Mode = PxeBc->Mode;
- Type = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP;
- Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL;
- //
- // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
- // other pxe boot information.
- //
- Status = PxeBc->Dhcp (PxeBc, TRUE);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- //
- // Select a boot server from boot server list.
- //
- Status = PxeBcSelectBootPrompt (Private);
- if (Status == EFI_SUCCESS) {
- //
- // Choose by user's input.
- //
- Status = PxeBcSelectBootMenu (Private, &Type, FALSE);
- } else if (Status == EFI_TIMEOUT) {
- //
- // Choose by default item.
- //
- Status = PxeBcSelectBootMenu (Private, &Type, TRUE);
- }
- if (!EFI_ERROR (Status)) {
- if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) {
- //
- // Local boot(PXE bootstrap server) need abort
- //
- return EFI_ABORTED;
- }
- //
- // Start to discover the boot server to get (m)tftp server ip address, bootfile
- // name and bootfile size.
- //
- UseBis = (BOOLEAN)(Mode->BisSupported && Mode->BisDetected);
- Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- if (Mode->PxeReplyReceived && !Mode->ProxyOfferReceived) {
- //
- // Some network boot loader only search the packet in Mode.ProxyOffer to get its server
- // IP address, so we need to store a copy of Mode.PxeReply packet into Mode.ProxyOffer.
- //
- if (Mode->UsingIpv6) {
- CopyMem (
- &Mode->ProxyOffer.Dhcpv6,
- &Mode->PxeReply.Dhcpv6,
- Private->PxeReply.Dhcp6.Packet.Ack.Length
- );
- } else {
- CopyMem (
- &Mode->ProxyOffer.Dhcpv4,
- &Mode->PxeReply.Dhcpv4,
- Private->PxeReply.Dhcp4.Packet.Ack.Length
- );
- }
- Mode->ProxyOfferReceived = TRUE;
- }
- }
- //
- // Parse the boot information.
- //
- if (Mode->UsingIpv6) {
- Status = PxeBcDhcp6BootInfo (Private, BufferSize);
- } else {
- Status = PxeBcDhcp4BootInfo (Private, BufferSize);
- }
- return Status;
- }
- /**
- Install PxeBaseCodeCallbackProtocol if not installed before.
- @param[in, out] Private Pointer to PxeBc private data.
- @param[out] NewMakeCallback If TRUE, it is a new callback.
- Otherwise, it is not new callback.
- @retval EFI_SUCCESS PxeBaseCodeCallbackProtocol installed successfully.
- @retval Others Failed to install PxeBaseCodeCallbackProtocol.
- **/
- EFI_STATUS
- PxeBcInstallCallback (
- IN OUT PXEBC_PRIVATE_DATA *Private,
- OUT BOOLEAN *NewMakeCallback
- )
- {
- EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
- EFI_STATUS Status;
- //
- // Check whether PxeBaseCodeCallbackProtocol already installed.
- //
- PxeBc = &Private->PxeBc;
- Status = gBS->HandleProtocol (
- Private->Mode.UsingIpv6 ? Private->Ip6Nic->Controller : Private->Ip4Nic->Controller,
- &gEfiPxeBaseCodeCallbackProtocolGuid,
- (VOID **)&Private->PxeBcCallback
- );
- if (Status == EFI_UNSUPPORTED) {
- CopyMem (
- &Private->LoadFileCallback,
- &gPxeBcCallBackTemplate,
- sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL)
- );
- //
- // Install a default callback if user didn't offer one.
- //
- Status = gBS->InstallProtocolInterface (
- Private->Mode.UsingIpv6 ? &Private->Ip6Nic->Controller : &Private->Ip4Nic->Controller,
- &gEfiPxeBaseCodeCallbackProtocolGuid,
- EFI_NATIVE_INTERFACE,
- &Private->LoadFileCallback
- );
- (*NewMakeCallback) = (BOOLEAN)(Status == EFI_SUCCESS);
- Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback);
- if (EFI_ERROR (Status)) {
- PxeBc->Stop (PxeBc);
- return Status;
- }
- }
- return EFI_SUCCESS;
- }
- /**
- Uninstall PxeBaseCodeCallbackProtocol.
- @param[in] Private Pointer to PxeBc private data.
- @param[in] NewMakeCallback If TRUE, it is a new callback.
- Otherwise, it is not new callback.
- **/
- VOID
- PxeBcUninstallCallback (
- IN PXEBC_PRIVATE_DATA *Private,
- IN BOOLEAN NewMakeCallback
- )
- {
- EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
- PxeBc = &Private->PxeBc;
- if (NewMakeCallback) {
- NewMakeCallback = FALSE;
- PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback);
- gBS->UninstallProtocolInterface (
- Private->Mode.UsingIpv6 ? Private->Ip6Nic->Controller : Private->Ip4Nic->Controller,
- &gEfiPxeBaseCodeCallbackProtocolGuid,
- &Private->LoadFileCallback
- );
- }
- }
- /**
- Download one of boot file in the list, and it's special for IPv6.
- @param[in] Private Pointer to PxeBc private data.
- @param[in, out] BufferSize Size of user buffer for input;
- required buffer size for output.
- @param[in] Buffer Pointer to user buffer.
- @retval EFI_SUCCESS Read one of boot file in the list successfully.
- @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
- @retval EFI_NOT_FOUND There is no proper boot file available.
- @retval Others Failed to download boot file in the list.
- **/
- EFI_STATUS
- PxeBcReadBootFileList (
- IN PXEBC_PRIVATE_DATA *Private,
- IN OUT UINT64 *BufferSize,
- IN VOID *Buffer OPTIONAL
- )
- {
- EFI_STATUS Status;
- EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
- PxeBc = &Private->PxeBc;
- //
- // Try to download the boot file if everything is ready.
- //
- if (Buffer != NULL) {
- Status = PxeBc->Mtftp (
- PxeBc,
- EFI_PXE_BASE_CODE_TFTP_READ_FILE,
- Buffer,
- FALSE,
- BufferSize,
- &Private->BlockSize,
- &Private->ServerIp,
- Private->BootFileName,
- NULL,
- FALSE
- );
- } else {
- Status = EFI_BUFFER_TOO_SMALL;
- }
- return Status;
- }
- /**
- Load boot file into user buffer.
- @param[in] Private Pointer to PxeBc private data.
- @param[in, out] BufferSize Size of user buffer for input;
- required buffer size for output.
- @param[in] Buffer Pointer to user buffer.
- @retval EFI_SUCCESS Get all the boot information successfully.
- @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
- @retval EFI_ABORTED User cancelled the current operation.
- @retval Others Failed to parse out the boot information.
- **/
- EFI_STATUS
- PxeBcLoadBootFile (
- IN PXEBC_PRIVATE_DATA *Private,
- IN OUT UINTN *BufferSize,
- IN VOID *Buffer OPTIONAL
- )
- {
- BOOLEAN NewMakeCallback;
- UINT64 RequiredSize;
- UINT64 CurrentSize;
- EFI_STATUS Status;
- EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
- EFI_PXE_BASE_CODE_MODE *PxeBcMode;
- NewMakeCallback = FALSE;
- PxeBc = &Private->PxeBc;
- PxeBcMode = &Private->Mode;
- CurrentSize = *BufferSize;
- RequiredSize = 0;
- //
- // Install pxebc callback protocol if hasn't been installed yet.
- //
- Status = PxeBcInstallCallback (Private, &NewMakeCallback);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- if (Private->BootFileSize == 0) {
- //
- // Discover the boot information about the bootfile if hasn't.
- //
- Status = PxeBcDiscoverBootFile (Private, &RequiredSize);
- if (EFI_ERROR (Status)) {
- goto ON_EXIT;
- }
- if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize)) {
- //
- // It's error if the required buffer size is beyond the system scope.
- //
- Status = EFI_DEVICE_ERROR;
- goto ON_EXIT;
- } else if (RequiredSize > 0) {
- //
- // Get the right buffer size of the bootfile required.
- //
- if ((CurrentSize < RequiredSize) || (Buffer == NULL)) {
- //
- // It's buffer too small if the size of user buffer is smaller than the required.
- //
- CurrentSize = RequiredSize;
- Status = EFI_BUFFER_TOO_SMALL;
- goto ON_EXIT;
- }
- CurrentSize = RequiredSize;
- } else if ((RequiredSize == 0) && PxeBcMode->UsingIpv6) {
- //
- // Try to download another bootfile in list if failed to get the filesize of the last one.
- // It's special for the case of IPv6.
- //
- Status = PxeBcReadBootFileList (Private, &CurrentSize, Buffer);
- goto ON_EXIT;
- }
- } else if ((CurrentSize < Private->BootFileSize) || (Buffer == NULL)) {
- //
- // It's buffer too small if the size of user buffer is smaller than the required.
- //
- CurrentSize = Private->BootFileSize;
- Status = EFI_BUFFER_TOO_SMALL;
- goto ON_EXIT;
- }
- //
- // Begin to download the bootfile if everything is ready.
- //
- AsciiPrint ("\n Downloading NBP file...\n");
- if (PxeBcMode->UsingIpv6) {
- Status = PxeBcReadBootFileList (
- Private,
- &CurrentSize,
- Buffer
- );
- } else {
- Status = PxeBc->Mtftp (
- PxeBc,
- EFI_PXE_BASE_CODE_TFTP_READ_FILE,
- Buffer,
- FALSE,
- &CurrentSize,
- &Private->BlockSize,
- &Private->ServerIp,
- Private->BootFileName,
- NULL,
- FALSE
- );
- }
- ON_EXIT:
- *BufferSize = (UINTN)CurrentSize;
- PxeBcUninstallCallback (Private, NewMakeCallback);
- if (Status == EFI_SUCCESS) {
- AsciiPrint ("\n NBP file downloaded successfully.\n");
- return EFI_SUCCESS;
- } else if ((Status == EFI_BUFFER_TOO_SMALL) && (Buffer != NULL)) {
- AsciiPrint ("\n PXE-E05: Buffer size is smaller than the requested file.\n");
- } else if (Status == EFI_DEVICE_ERROR) {
- AsciiPrint ("\n PXE-E07: Network device error.\n");
- } else if (Status == EFI_OUT_OF_RESOURCES) {
- AsciiPrint ("\n PXE-E09: Could not allocate I/O buffers.\n");
- } else if (Status == EFI_NO_MEDIA) {
- AsciiPrint ("\n PXE-E12: Could not detect network connection.\n");
- } else if (Status == EFI_NO_RESPONSE) {
- AsciiPrint ("\n PXE-E16: No valid offer received.\n");
- } else if (Status == EFI_TIMEOUT) {
- AsciiPrint ("\n PXE-E18: Server response timeout.\n");
- } else if (Status == EFI_ABORTED) {
- AsciiPrint ("\n PXE-E21: Remote boot cancelled.\n");
- } else if (Status == EFI_ICMP_ERROR) {
- AsciiPrint ("\n PXE-E22: Client received ICMP error from server.\n");
- } else if (Status == EFI_TFTP_ERROR) {
- AsciiPrint ("\n PXE-E23: Client received TFTP error from server.\n");
- } else if (Status == EFI_NOT_FOUND) {
- AsciiPrint ("\n PXE-E53: No boot filename received.\n");
- } else if (Status != EFI_BUFFER_TOO_SMALL) {
- AsciiPrint ("\n PXE-E99: Unexpected network error.\n");
- }
- return Status;
- }
|