/** 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 succesfully. @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->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->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; }
EFI_STATUS BdsPxeLoadImage ( IN OUT EFI_DEVICE_PATH **DevicePath, IN EFI_HANDLE Handle, IN EFI_DEVICE_PATH *RemainingDevicePath, IN EFI_ALLOCATE_TYPE Type, IN OUT EFI_PHYSICAL_ADDRESS* Image, OUT UINTN *ImageSize ) { EFI_STATUS Status; EFI_LOAD_FILE_PROTOCOL *LoadFileProtocol; UINTN BufferSize; EFI_PXE_BASE_CODE_PROTOCOL *Pxe; // Get Load File Protocol attached to the PXE protocol Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFileProtocol); if (EFI_ERROR (Status)) { return Status; } Status = LoadFileProtocol->LoadFile (LoadFileProtocol, *DevicePath, TRUE, &BufferSize, NULL); if (Status == EFI_BUFFER_TOO_SMALL) { Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(BufferSize), Image); if (EFI_ERROR (Status)) { return Status; } Status = LoadFileProtocol->LoadFile (LoadFileProtocol, *DevicePath, TRUE, &BufferSize, (VOID*)(UINTN)(*Image)); if (!EFI_ERROR (Status) && (ImageSize != NULL)) { *ImageSize = BufferSize; } } if (Status == EFI_ALREADY_STARTED) { Status = gBS->LocateProtocol (&gEfiPxeBaseCodeProtocolGuid, NULL, (VOID **)&Pxe); if (!EFI_ERROR(Status)) { // If PXE is already started, we stop it Pxe->Stop (Pxe); // And we try again return BdsPxeLoadImage (DevicePath, Handle, RemainingDevicePath, Type, Image, ImageSize); } } return Status; }
EFI_STATUS EFIAPI EblPerformDHCP ( IN BOOLEAN SortOffers ) { EFI_STATUS Status; EFI_PXE_BASE_CODE_PROTOCOL *Pxe; Status = gBS->LocateProtocol (&gEfiPxeBaseCodeProtocolGuid, NULL, (VOID **)&Pxe); if (EFI_ERROR(Status)) { return Status; } Status = Pxe->Start (Pxe, gUseIpv6); if (EFI_ERROR(Status) && (Status != EFI_ALREADY_STARTED)) { return Status; } Status = Pxe->Dhcp(Pxe, TRUE); return Status; }
EFI_STATUS EFIAPI EblGetCurrentIpAddress ( IN OUT EFI_IP_ADDRESS *Ip ) { EFI_STATUS Status; EFI_PXE_BASE_CODE_PROTOCOL *Pxe; Status = gBS->LocateProtocol (&gEfiPxeBaseCodeProtocolGuid, NULL, (VOID **)&Pxe); if (EFI_ERROR(Status)) { return Status; } Status = Pxe->Start (Pxe, gUseIpv6); if (EFI_ERROR(Status) && (Status != EFI_ALREADY_STARTED)) { return Status; } CopyMem (Ip, &Pxe->Mode->StationIp, sizeof (EFI_IP_ADDRESS)); 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->Controller, &gEfiPxeBaseCodeCallbackProtocolGuid, &Private->LoadFileCallback ); } }
EFI_STATUS BdsTftpLoadImage ( IN EFI_DEVICE_PATH* DevicePath, IN EFI_HANDLE Handle, IN EFI_DEVICE_PATH* RemainingDevicePath, IN EFI_ALLOCATE_TYPE Type, IN OUT EFI_PHYSICAL_ADDRESS *Image, OUT UINTN *ImageSize ) { EFI_STATUS Status; EFI_PXE_BASE_CODE_PROTOCOL *Pxe; UINT64 TftpBufferSize; VOID* TftpBuffer; EFI_IP_ADDRESS ServerIp; IPv4_DEVICE_PATH* IPv4DevicePathNode; FILEPATH_DEVICE_PATH* FilePathDevicePath; EFI_IP_ADDRESS LocalIp; ASSERT(IS_DEVICE_PATH_NODE(RemainingDevicePath,MESSAGING_DEVICE_PATH,MSG_IPv4_DP)); IPv4DevicePathNode = (IPv4_DEVICE_PATH*)RemainingDevicePath; FilePathDevicePath = (FILEPATH_DEVICE_PATH*)(IPv4DevicePathNode + 1); Status = gBS->LocateProtocol (&gEfiPxeBaseCodeProtocolGuid, NULL, (VOID **)&Pxe); if (EFI_ERROR(Status)) { return Status; } Status = Pxe->Start (Pxe, FALSE); if (EFI_ERROR(Status) && (Status != EFI_ALREADY_STARTED)) { return Status; } if (!IPv4DevicePathNode->StaticIpAddress) { Status = Pxe->Dhcp(Pxe, TRUE); } else { CopyMem (&LocalIp.v4, &IPv4DevicePathNode->LocalIpAddress, sizeof (EFI_IPv4_ADDRESS)); Status = Pxe->SetStationIp (Pxe, &LocalIp, NULL); } if (EFI_ERROR(Status)) { return Status; } CopyMem (&ServerIp.v4, &IPv4DevicePathNode->RemoteIpAddress, sizeof (EFI_IPv4_ADDRESS)); Status = Pxe->Mtftp ( Pxe, EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, NULL, FALSE, &TftpBufferSize, NULL, &ServerIp, (UINT8 *)FilePathDevicePath->PathName, NULL, TRUE ); if (EFI_ERROR(Status)) { return Status; } // Allocate a buffer to hold the whole file. TftpBuffer = AllocatePool(TftpBufferSize); if (TftpBuffer == NULL) { return EFI_OUT_OF_RESOURCES; } Status = Pxe->Mtftp ( Pxe, EFI_PXE_BASE_CODE_TFTP_READ_FILE, TftpBuffer, FALSE, &TftpBufferSize, NULL, &ServerIp, (UINT8 *)FilePathDevicePath->PathName, NULL, FALSE ); if (EFI_ERROR(Status)) { FreePool(TftpBuffer); } else if (ImageSize != NULL) { *ImageSize = (UINTN)TftpBufferSize; } return Status; }
/** 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; }
/** 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; } ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL); // // 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; }
/** 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; } ASSERT (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL); // // 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 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 = &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; } // // Parse the boot server Ipv4 address by next server address. // If this field isn't available, use option 54 instead. // CopyMem ( &Private->ServerIp, &Cache4->Packet.Offer.Dhcp4.Header.ServerAddr, sizeof (EFI_IPv4_ADDRESS) ); if (Private->ServerIp.Addr[0] == 0) { CopyMem ( &Private->ServerIp, Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data, sizeof (EFI_IPv4_ADDRESS) ); } // // Parse the boot file name by option. // ASSERT (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL); 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; }