/** @file Functions implementation related with DHCPv4 for UefiPxeBc Driver. Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "PxeBcImpl.h" // // This is a map from the interested DHCP4 option tags' index to the tag value. // UINT8 mInterestedDhcp4Tags[PXEBC_DHCP4_TAG_INDEX_MAX] = { DHCP4_TAG_BOOTFILE_LEN, DHCP4_TAG_VENDOR, DHCP4_TAG_OVERLOAD, DHCP4_TAG_MSG_TYPE, DHCP4_TAG_SERVER_ID, DHCP4_TAG_VENDOR_CLASS_ID, DHCP4_TAG_BOOTFILE }; // // There are 4 times retries with the value of 4, 8, 16 and 32, refers to PXE2.1 spec. // UINT32 mPxeDhcpTimeout[4] = {4, 8, 16, 32}; /** Parse a certain dhcp4 option by OptTag in Buffer, and return with start pointer. @param[in] Buffer Pointer to the option buffer. @param[in] Length Length of the option buffer. @param[in] OptTag Tag of the required option. @retval NULL Failed to find the required option. @retval Others The position of the required option. **/ EFI_DHCP4_PACKET_OPTION * PxeBcParseDhcp4Options ( IN UINT8 *Buffer, IN UINT32 Length, IN UINT8 OptTag ) { EFI_DHCP4_PACKET_OPTION *Option; UINT32 Offset; Option = (EFI_DHCP4_PACKET_OPTION *) Buffer; Offset = 0; while (Offset < Length && Option->OpCode != DHCP4_TAG_EOP) { if (Option->OpCode == OptTag) { // // Found the required option. // return Option; } // // Skip the current option to the next. // if (Option->OpCode == DHCP4_TAG_PAD) { Offset++; } else { Offset += Option->Length + 2; } Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset); } return NULL; } /** Parse the PXE vendor options and extract the information from them. @param[in] Dhcp4Option Pointer to vendor options in buffer. @param[in] VendorOption Pointer to structure to store information in vendor options. **/ VOID PxeBcParseVendorOptions ( IN EFI_DHCP4_PACKET_OPTION *Dhcp4Option, IN PXEBC_VENDOR_OPTION *VendorOption ) { UINT32 *BitMap; UINT8 VendorOptionLen; EFI_DHCP4_PACKET_OPTION *PxeOption; UINT8 Offset; BitMap = VendorOption->BitMap; VendorOptionLen = Dhcp4Option->Length; PxeOption = (EFI_DHCP4_PACKET_OPTION *) &Dhcp4Option->Data[0]; Offset = 0; ASSERT (PxeOption != NULL); while ((Offset < VendorOptionLen) && (PxeOption->OpCode != DHCP4_TAG_EOP)) { // // Parse all the interesting PXE vendor options one by one. // switch (PxeOption->OpCode) { case PXEBC_VENDOR_TAG_MTFTP_IP: CopyMem (&VendorOption->MtftpIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS)); break; case PXEBC_VENDOR_TAG_MTFTP_CPORT: CopyMem (&VendorOption->MtftpCPort, PxeOption->Data, sizeof (VendorOption->MtftpCPort)); break; case PXEBC_VENDOR_TAG_MTFTP_SPORT: CopyMem (&VendorOption->MtftpSPort, PxeOption->Data, sizeof (VendorOption->MtftpSPort)); break; case PXEBC_VENDOR_TAG_MTFTP_TIMEOUT: VendorOption->MtftpTimeout = *PxeOption->Data; break; case PXEBC_VENDOR_TAG_MTFTP_DELAY: VendorOption->MtftpDelay = *PxeOption->Data; break; case PXEBC_VENDOR_TAG_DISCOVER_CTRL: VendorOption->DiscoverCtrl = *PxeOption->Data; break; case PXEBC_VENDOR_TAG_DISCOVER_MCAST: CopyMem (&VendorOption->DiscoverMcastIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS)); break; case PXEBC_VENDOR_TAG_BOOT_SERVERS: VendorOption->BootSvrLen = PxeOption->Length; VendorOption->BootSvr = (PXEBC_BOOT_SVR_ENTRY *) PxeOption->Data; break; case PXEBC_VENDOR_TAG_BOOT_MENU: VendorOption->BootMenuLen = PxeOption->Length; VendorOption->BootMenu = (PXEBC_BOOT_MENU_ENTRY *) PxeOption->Data; break; case PXEBC_VENDOR_TAG_MENU_PROMPT: VendorOption->MenuPromptLen = PxeOption->Length; VendorOption->MenuPrompt = (PXEBC_MENU_PROMPT *) PxeOption->Data; break; case PXEBC_VENDOR_TAG_MCAST_ALLOC: CopyMem (&VendorOption->McastIpBase, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS)); CopyMem (&VendorOption->McastIpBlock, PxeOption->Data + 4, sizeof (VendorOption->McastIpBlock)); CopyMem (&VendorOption->McastIpRange, PxeOption->Data + 6, sizeof (VendorOption->McastIpRange)); break; case PXEBC_VENDOR_TAG_CREDENTIAL_TYPES: VendorOption->CredTypeLen = PxeOption->Length; VendorOption->CredType = (UINT32 *) PxeOption->Data; break; case PXEBC_VENDOR_TAG_BOOT_ITEM: CopyMem (&VendorOption->BootSrvType, PxeOption->Data, sizeof (VendorOption->BootSrvType)); CopyMem (&VendorOption->BootSrvLayer, PxeOption->Data + 2, sizeof (VendorOption->BootSrvLayer)); break; default: // // Not interesting PXE vendor options. // break; } // // Set the bit map for the special PXE options. // SET_VENDOR_OPTION_BIT_MAP (BitMap, PxeOption->OpCode); // // Continue to the next option. // if (PxeOption->OpCode == DHCP4_TAG_PAD) { Offset++; } else { Offset = (UINT8) (Offset + PxeOption->Length + 2); } PxeOption = (EFI_DHCP4_PACKET_OPTION *) (Dhcp4Option->Data + Offset); } } /** Build the options buffer for the DHCPv4 request packet. @param[in] Private Pointer to PxeBc private data. @param[out] OptList Pointer to the option pointer array. @param[in] Buffer Pointer to the buffer to contain the option list. @param[in] NeedMsgType If TRUE, it is necessary to include the Msg type option. Otherwise, it is not necessary. @return Index The count of the built-in options. **/ UINT32 PxeBcBuildDhcp4Options ( IN PXEBC_PRIVATE_DATA *Private, OUT EFI_DHCP4_PACKET_OPTION **OptList, IN UINT8 *Buffer, IN BOOLEAN NeedMsgType ) { UINT32 Index; PXEBC_DHCP4_OPTION_ENTRY OptEnt; UINT16 Value; Index = 0; OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer; if (NeedMsgType) { // // Append message type. // OptList[Index]->OpCode = DHCP4_TAG_MSG_TYPE; OptList[Index]->Length = 1; OptEnt.Mesg = (PXEBC_DHCP4_OPTION_MESG *) OptList[Index]->Data; OptEnt.Mesg->Type = PXEBC_DHCP4_MSG_TYPE_REQUEST; Index++; OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); // // Append max message size. // OptList[Index]->OpCode = DHCP4_TAG_MAXMSG; OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE); OptEnt.MaxMesgSize = (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *) OptList[Index]->Data; Value = NTOHS (PXEBC_DHCP4_PACKET_MAX_SIZE); CopyMem (&OptEnt.MaxMesgSize->Size, &Value, sizeof (UINT16)); Index++; OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); } // // Append parameter request list option. // OptList[Index]->OpCode = DHCP4_TAG_PARA_LIST; OptList[Index]->Length = 35; OptEnt.Para = (PXEBC_DHCP4_OPTION_PARA *) OptList[Index]->Data; OptEnt.Para->ParaList[0] = DHCP4_TAG_NETMASK; OptEnt.Para->ParaList[1] = DHCP4_TAG_TIME_OFFSET; OptEnt.Para->ParaList[2] = DHCP4_TAG_ROUTER; OptEnt.Para->ParaList[3] = DHCP4_TAG_TIME_SERVER; OptEnt.Para->ParaList[4] = DHCP4_TAG_NAME_SERVER; OptEnt.Para->ParaList[5] = DHCP4_TAG_DNS_SERVER; OptEnt.Para->ParaList[6] = DHCP4_TAG_HOSTNAME; OptEnt.Para->ParaList[7] = DHCP4_TAG_BOOTFILE_LEN; OptEnt.Para->ParaList[8] = DHCP4_TAG_DOMAINNAME; OptEnt.Para->ParaList[9] = DHCP4_TAG_ROOTPATH; OptEnt.Para->ParaList[10] = DHCP4_TAG_EXTEND_PATH; OptEnt.Para->ParaList[11] = DHCP4_TAG_EMTU; OptEnt.Para->ParaList[12] = DHCP4_TAG_TTL; OptEnt.Para->ParaList[13] = DHCP4_TAG_BROADCAST; OptEnt.Para->ParaList[14] = DHCP4_TAG_NIS_DOMAIN; OptEnt.Para->ParaList[15] = DHCP4_TAG_NIS_SERVER; OptEnt.Para->ParaList[16] = DHCP4_TAG_NTP_SERVER; OptEnt.Para->ParaList[17] = DHCP4_TAG_VENDOR; OptEnt.Para->ParaList[18] = DHCP4_TAG_REQUEST_IP; OptEnt.Para->ParaList[19] = DHCP4_TAG_LEASE; OptEnt.Para->ParaList[20] = DHCP4_TAG_SERVER_ID; OptEnt.Para->ParaList[21] = DHCP4_TAG_T1; OptEnt.Para->ParaList[22] = DHCP4_TAG_T2; OptEnt.Para->ParaList[23] = DHCP4_TAG_VENDOR_CLASS_ID; OptEnt.Para->ParaList[24] = DHCP4_TAG_TFTP; OptEnt.Para->ParaList[25] = DHCP4_TAG_BOOTFILE; OptEnt.Para->ParaList[26] = DHCP4_TAG_UUID; OptEnt.Para->ParaList[27] = 0x80; OptEnt.Para->ParaList[28] = 0x81; OptEnt.Para->ParaList[29] = 0x82; OptEnt.Para->ParaList[30] = 0x83; OptEnt.Para->ParaList[31] = 0x84; OptEnt.Para->ParaList[32] = 0x85; OptEnt.Para->ParaList[33] = 0x86; OptEnt.Para->ParaList[34] = 0x87; Index++; OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); // // Append UUID/Guid-based client identifier option // OptList[Index]->OpCode = DHCP4_TAG_UUID; OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UUID); OptEnt.Uuid = (PXEBC_DHCP4_OPTION_UUID *) OptList[Index]->Data; OptEnt.Uuid->Type = 0; Index++; OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) { // // Zero the Guid to indicate NOT programmable if failed to get system Guid. // DEBUG ((EFI_D_WARN, "PXE: Failed to read system GUID from the smbios table!\n")); ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID)); } // // Append client network device interface option // OptList[Index]->OpCode = DHCP4_TAG_UNDI; OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UNDI); OptEnt.Undi = (PXEBC_DHCP4_OPTION_UNDI *) OptList[Index]->Data; if (Private->Nii != NULL) { OptEnt.Undi->Type = Private->Nii->Type; OptEnt.Undi->MajorVer = Private->Nii->MajorVer; OptEnt.Undi->MinorVer = Private->Nii->MinorVer; } else { OptEnt.Undi->Type = DEFAULT_UNDI_TYPE; OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR; OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR; } Index++; OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); // // Append client system architecture option // OptList[Index]->OpCode = DHCP4_TAG_ARCH; OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_ARCH); OptEnt.Arch = (PXEBC_DHCP4_OPTION_ARCH *) OptList[Index]->Data; Value = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE); CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16)); Index++; OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]); // // Append vendor class identify option // OptList[Index]->OpCode = DHCP4_TAG_VENDOR_CLASS_ID; OptList[Index]->Length = (UINT8) sizeof (PXEBC_DHCP4_OPTION_CLID); OptEnt.Clid = (PXEBC_DHCP4_OPTION_CLID *) OptList[Index]->Data; CopyMem ( OptEnt.Clid, DEFAULT_CLASS_ID_DATA, sizeof (PXEBC_DHCP4_OPTION_CLID) ); PxeBcUintnToAscDecWithFormat ( EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE, OptEnt.Clid->ArchitectureType, sizeof (OptEnt.Clid->ArchitectureType) ); if (Private->Nii != NULL) { CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName)); PxeBcUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor)); PxeBcUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor)); } Index++; return Index; } /** Create a template DHCPv4 packet as a seed. @param[out] Seed Pointer to the seed packet. @param[in] Udp4 Pointer to EFI_UDP4_PROTOCOL. **/ VOID PxeBcSeedDhcp4Packet ( OUT EFI_DHCP4_PACKET *Seed, IN EFI_UDP4_PROTOCOL *Udp4 ) { EFI_SIMPLE_NETWORK_MODE Mode; EFI_DHCP4_HEADER *Header; // // Get IfType and HwAddressSize from SNP mode data. // Udp4->GetModeData (Udp4, NULL, NULL, NULL, &Mode); Seed->Size = sizeof (EFI_DHCP4_PACKET); Seed->Length = sizeof (Seed->Dhcp4); Header = &Seed->Dhcp4.Header; ZeroMem (Header, sizeof (EFI_DHCP4_HEADER)); Header->OpCode = PXEBC_DHCP4_OPCODE_REQUEST; Header->HwType = Mode.IfType; Header->HwAddrLen = (UINT8) Mode.HwAddressSize; CopyMem (Header->ClientHwAddr, &Mode.CurrentAddress, Header->HwAddrLen); Seed->Dhcp4.Magik = PXEBC_DHCP4_MAGIC; Seed->Dhcp4.Option[0] = DHCP4_TAG_EOP; } /** Cache the DHCPv4 packet. @param[in] Dst Pointer to the cache buffer for DHCPv4 packet. @param[in] Src Pointer to the DHCPv4 packet to be cached. @retval EFI_SUCCESS Packet is copied. @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet. **/ EFI_STATUS PxeBcCacheDhcp4Packet ( IN EFI_DHCP4_PACKET *Dst, IN EFI_DHCP4_PACKET *Src ) { if (Dst->Size < Src->Length) { return EFI_BUFFER_TOO_SMALL; } CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length); Dst->Length = Src->Length; return EFI_SUCCESS; } /** Parse the cached DHCPv4 packet, including all the options. @param[in] Cache4 Pointer to cached DHCPv4 packet. @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully. @retval EFI_DEVICE_ERROR Failed to parse and invalid packet. **/ EFI_STATUS PxeBcParseDhcp4Packet ( IN PXEBC_DHCP4_PACKET_CACHE *Cache4 ) { EFI_DHCP4_PACKET *Offer; EFI_DHCP4_PACKET_OPTION **Options; EFI_DHCP4_PACKET_OPTION *Option; PXEBC_OFFER_TYPE OfferType; UINTN Index; BOOLEAN IsProxyOffer; BOOLEAN IsPxeOffer; UINT8 *Ptr8; BOOLEAN FileFieldOverloaded; IsProxyOffer = FALSE; IsPxeOffer = FALSE; FileFieldOverloaded = FALSE; ZeroMem (Cache4->OptList, sizeof (Cache4->OptList)); ZeroMem (&Cache4->VendorOpt, sizeof (Cache4->VendorOpt)); Offer = &Cache4->Packet.Offer; Options = Cache4->OptList; // // Parse DHCPv4 options in this offer, and store the pointers. // First, try to parse DHCPv4 options from the DHCP optional parameters field. // for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) { Options[Index] = PxeBcParseDhcp4Options ( Offer->Dhcp4.Option, GET_OPTION_BUFFER_LEN (Offer), mInterestedDhcp4Tags[Index] ); } // // Second, Check if bootfilename and serverhostname is overloaded to carry DHCP options refers to rfc-2132. // If yes, try to parse options from the BootFileName field, then ServerName field. // Option = Options[PXEBC_DHCP4_TAG_INDEX_OVERLOAD]; if (Option != NULL) { if ((Option->Data[0] & PXEBC_DHCP4_OVERLOAD_FILE) != 0) { FileFieldOverloaded = TRUE; for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) { if (Options[Index] == NULL) { Options[Index] = PxeBcParseDhcp4Options ( (UINT8 *) Offer->Dhcp4.Header.BootFileName, sizeof (Offer->Dhcp4.Header.BootFileName), mInterestedDhcp4Tags[Index] ); } } } if ((Option->Data[0] & PXEBC_DHCP4_OVERLOAD_SERVER_NAME) != 0) { for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) { if (Options[Index] == NULL) { Options[Index] = PxeBcParseDhcp4Options ( (UINT8 *) Offer->Dhcp4.Header.ServerName, sizeof (Offer->Dhcp4.Header.ServerName), mInterestedDhcp4Tags[Index] ); } } } } // // The offer with zero "yiaddr" is a proxy offer. // if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) { IsProxyOffer = TRUE; } // // The offer with "PXEClient" is a PXE offer. // Option = Options[PXEBC_DHCP4_TAG_INDEX_CLASS_ID]; if ((Option != NULL) && (Option->Length >= 9) && (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) { IsPxeOffer = TRUE; } // // Parse PXE vendor options in this offer, and store the contents/pointers. // Option = Options[PXEBC_DHCP4_TAG_INDEX_VENDOR]; if (IsPxeOffer && Option != NULL) { PxeBcParseVendorOptions (Option, &Cache4->VendorOpt); } // // Parse PXE boot file name: // According to PXE spec, boot file name should be read from DHCP option 67 (bootfile name) if present. // Otherwise, read from boot file field in DHCP header. // if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { // // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null // terminated string. So force to append null terminated character at the end of string. // Ptr8 = (UINT8*)&Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data[0]; Ptr8 += Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Length; if (*(Ptr8 - 1) != '\0') { *Ptr8 = '\0'; } } else if (!FileFieldOverloaded && Offer->Dhcp4.Header.BootFileName[0] != 0) { // // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it. // Do not count dhcp option header here, or else will destroy the serverhostname. // Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *) (&Offer->Dhcp4.Header.BootFileName[0] - OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0])); } // // Determine offer type of the DHCPv4 packet. // Option = Options[PXEBC_DHCP4_TAG_INDEX_MSG_TYPE]; if (Option == NULL || Option->Data[0] == 0) { // // It's a Bootp offer. // OfferType = PxeOfferTypeBootp; Option = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]; if (Option == NULL) { // // If the Bootp offer without bootfilename, discard it. // return EFI_DEVICE_ERROR; } } else { if (IS_VALID_DISCOVER_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) { // // It's a PXE10 offer with PXEClient and discover vendor option. // OfferType = IsProxyOffer ? PxeOfferTypeProxyPxe10 : PxeOfferTypeDhcpPxe10; } else if (IS_VALID_MTFTP_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) { // // It's a WFM11a offer with PXEClient and mtftp vendor option. // But multi-cast download is not supported currently, so discard it. // return EFI_DEVICE_ERROR; } else if (IsPxeOffer) { // // It's a BINL offer only with PXEClient. // OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl; } else { // // It's a DHCPv4 only offer, which is a pure DHCPv4 offer packet. // OfferType = PxeOfferTypeDhcpOnly; } } Cache4->OfferType = OfferType; return EFI_SUCCESS; } /** Cache the DHCPv4 ack packet, and parse it on demand. @param[in] Private Pointer to PxeBc private data. @param[in] Ack Pointer to the DHCPv4 ack packet. @param[in] Verified If TRUE, parse the ACK packet and store info into mode data. @retval EFI_SUCCESS Cache and parse the packet successfully. @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet. **/ EFI_STATUS PxeBcCopyDhcp4Ack ( IN PXEBC_PRIVATE_DATA *Private, IN EFI_DHCP4_PACKET *Ack, IN BOOLEAN Verified ) { EFI_PXE_BASE_CODE_MODE *Mode; EFI_STATUS Status; Mode = Private->PxeBc.Mode; Status = PxeBcCacheDhcp4Packet (&Private->DhcpAck.Dhcp4.Packet.Ack, Ack); if (EFI_ERROR (Status)) { return Status; } if (Verified) { // // Parse the ack packet and store it into mode data if needed. // PxeBcParseDhcp4Packet (&Private->DhcpAck.Dhcp4); CopyMem (&Mode->DhcpAck.Dhcpv4, &Ack->Dhcp4, Ack->Length); Mode->DhcpAckReceived = TRUE; } return EFI_SUCCESS; } /** Cache the DHCPv4 proxy offer packet according to the received order. @param[in] Private Pointer to PxeBc private data. @param[in] OfferIndex The received order of offer packets. @retval EFI_SUCCESS Cache and parse the packet successfully. @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet. **/ EFI_STATUS PxeBcCopyProxyOffer ( IN PXEBC_PRIVATE_DATA *Private, IN UINT32 OfferIndex ) { EFI_PXE_BASE_CODE_MODE *Mode; EFI_DHCP4_PACKET *Offer; EFI_STATUS Status; ASSERT (OfferIndex < Private->OfferNum); ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM); Mode = Private->PxeBc.Mode; Offer = &Private->OfferBuffer[OfferIndex].Dhcp4.Packet.Offer; // // Cache the proxy offer packet and parse it. // Status = PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Offer); if (EFI_ERROR(Status)) { return Status; } PxeBcParseDhcp4Packet (&Private->ProxyOffer.Dhcp4); // // Store this packet into mode data. // CopyMem (&Mode->ProxyOffer.Dhcpv4, &Offer->Dhcp4, Offer->Length); Mode->ProxyOfferReceived = TRUE; return EFI_SUCCESS; } /** Retry to request bootfile name by the BINL offer. @param[in] Private Pointer to PxeBc private data. @param[in] Index The received order of offer packets. @retval EFI_SUCCESS Successfully retried to request bootfile name. @retval EFI_DEVICE_ERROR Failed to retry bootfile name. **/ EFI_STATUS PxeBcRetryBinlOffer ( IN PXEBC_PRIVATE_DATA *Private, IN UINT32 Index ) { EFI_DHCP4_PACKET *Offer; EFI_IP_ADDRESS ServerIp; EFI_STATUS Status; PXEBC_DHCP4_PACKET_CACHE *Cache4; EFI_DHCP4_PACKET *Reply; ASSERT (Index < PXEBC_OFFER_MAX_NUM); ASSERT (Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpBinl || Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeProxyBinl); Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer; // // Prefer to siaddr in header as next server address. If it's zero, then use option 54. // if (Offer->Dhcp4.Header.ServerAddr.Addr[0] == 0) { CopyMem ( &ServerIp.Addr[0], Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data, sizeof (EFI_IPv4_ADDRESS) ); } else { CopyMem ( &ServerIp.Addr[0], &Offer->Dhcp4.Header.ServerAddr, sizeof (EFI_IPv4_ADDRESS) ); } Private->IsDoDiscover = FALSE; Cache4 = &Private->ProxyOffer.Dhcp4; Reply = &Cache4->Packet.Offer; // // Send another request packet for bootfile name. // Status = PxeBcDhcp4Discover ( Private, 0, NULL, FALSE, &ServerIp, 0, NULL ); if (EFI_ERROR (Status)) { return Status; } // // Parse the reply for the last request packet. // Status = PxeBcParseDhcp4Packet (Cache4); if (EFI_ERROR (Status)) { return Status; } if (Cache4->OfferType != PxeOfferTypeProxyPxe10 && Cache4->OfferType != PxeOfferTypeProxyWfm11a && Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) { // // This BINL ack doesn't have discovery option set or multicast option set // or bootfile name specified. // return EFI_DEVICE_ERROR; } // // Store the reply into mode data. // Private->PxeBc.Mode->ProxyOfferReceived = TRUE; CopyMem (&Private->PxeBc.Mode->ProxyOffer.Dhcpv4, &Reply->Dhcp4, Reply->Length); return EFI_SUCCESS; } /** Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount. @param[in] Private Pointer to PxeBc private data. @param[in] RcvdOffer Pointer to the received offer packet. @retval EFI_SUCCESS Cache and parse the packet successfully. @retval Others Operation failed. **/ EFI_STATUS PxeBcCacheDhcp4Offer ( IN PXEBC_PRIVATE_DATA *Private, IN EFI_DHCP4_PACKET *RcvdOffer ) { PXEBC_DHCP4_PACKET_CACHE *Cache4; EFI_DHCP4_PACKET *Offer; PXEBC_OFFER_TYPE OfferType; EFI_STATUS Status; ASSERT (Private->OfferNum < PXEBC_OFFER_MAX_NUM); Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4; Offer = &Cache4->Packet.Offer; // // Cache the content of DHCPv4 packet firstly. // Status = PxeBcCacheDhcp4Packet (Offer, RcvdOffer); if (EFI_ERROR(Status)) { return Status; } // // Validate the DHCPv4 packet, and parse the options and offer type. // if (EFI_ERROR (PxeBcParseDhcp4Packet (Cache4))) { return EFI_ABORTED; } // // Determine whether cache the current offer by type, and record OfferIndex and OfferCount. // OfferType = Cache4->OfferType; ASSERT (OfferType < PxeOfferTypeMax); if (OfferType == PxeOfferTypeBootp) { // // It's a Bootp offer, only cache the first one, and discard the others. // if (Private->OfferCount[OfferType] == 0) { Private->OfferIndex[OfferType][0] = Private->OfferNum; Private->OfferCount[OfferType] = 1; } else { return EFI_ABORTED; } } else { ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM); if (IS_PROXY_DHCP_OFFER (Offer)) { // // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer. // Private->IsProxyRecved = TRUE; if (OfferType == PxeOfferTypeProxyBinl) { // // Cache all proxy BINL offers. // Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; Private->OfferCount[OfferType]++; } else if ((OfferType == PxeOfferTypeProxyPxe10 || OfferType == PxeOfferTypeProxyWfm11a) && Private->OfferCount[OfferType] < 1) { // // Only cache the first PXE10/WFM11a offer, and discard the others. // Private->OfferIndex[OfferType][0] = Private->OfferNum; Private->OfferCount[OfferType] = 1; } else { return EFI_ABORTED; } } else { // // It's a DHCPv4 offer with yiaddr, and cache them all. // Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum; Private->OfferCount[OfferType]++; } } Private->OfferNum++; return EFI_SUCCESS; } /** Select an DHCPv4 offer, and record SelectIndex and SelectProxyType. @param[in] Private Pointer to PxeBc private data. **/ VOID PxeBcSelectDhcp4Offer ( IN PXEBC_PRIVATE_DATA *Private ) { UINT32 Index; UINT32 OfferIndex; EFI_DHCP4_PACKET *Offer; Private->SelectIndex = 0; if (Private->IsOfferSorted) { // // Select offer by default policy. // if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) { // // 1. DhcpPxe10 offer // Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1; } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) { // // 2. DhcpWfm11a offer // Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1; } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) { // // 3. DhcpOnly offer and ProxyPxe10 offer. // Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; Private->SelectProxyType = PxeOfferTypeProxyPxe10; } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) { // // 4. DhcpOnly offer and ProxyWfm11a offer. // Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; Private->SelectProxyType = PxeOfferTypeProxyWfm11a; } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) { // // 5. DhcpBinl offer. // Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1; } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 && Private->OfferCount[PxeOfferTypeProxyBinl] > 0) { // // 6. DhcpOnly offer and ProxyBinl offer. // Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1; Private->SelectProxyType = PxeOfferTypeProxyBinl; } else { // // 7. DhcpOnly offer with bootfilename. // for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) { OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index]; if (Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { Private->SelectIndex = OfferIndex + 1; break; } } // // 8. Bootp offer with bootfilename. // OfferIndex = Private->OfferIndex[PxeOfferTypeBootp][0]; if (Private->SelectIndex == 0 && Private->OfferCount[PxeOfferTypeBootp] > 0 && Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) { Private->SelectIndex = OfferIndex + 1; } } } else { // // Select offer by received order. // for (Index = 0; Index < Private->OfferNum; Index++) { Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer; if (IS_PROXY_DHCP_OFFER (Offer)) { // // Skip proxy offers // continue; } if (!Private->IsProxyRecved && Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpOnly && Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) { // // Skip if DhcpOnly offer without any other proxy offers or bootfilename. // continue; } // // Record the index of the select offer. // Private->SelectIndex = Index + 1; break; } } } /** Handle the DHCPv4 offer packet. @param[in] Private Pointer to PxeBc private data. @retval EFI_SUCCESS Handled the DHCPv4 offer packet successfully. @retval EFI_NO_RESPONSE No response to the following request packet. @retval EFI_NOT_FOUND No boot filename received. @retval EFI_BUFFER_TOO_SMALL Can't cache the offer pacet. **/ EFI_STATUS PxeBcHandleDhcp4Offer ( IN PXEBC_PRIVATE_DATA *Private ) { PXEBC_DHCP4_PACKET_CACHE *Cache4; EFI_DHCP4_PACKET_OPTION **Options; UINT32 Index; EFI_DHCP4_PACKET *Offer; PXEBC_OFFER_TYPE OfferType; UINT32 ProxyIndex; UINT32 SelectIndex; EFI_STATUS Status; EFI_PXE_BASE_CODE_MODE *Mode; EFI_DHCP4_PACKET *Ack; ASSERT (Private->SelectIndex > 0); SelectIndex = (UINT32) (Private->SelectIndex - 1); ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM); Cache4 = &Private->OfferBuffer[SelectIndex].Dhcp4; Options = Cache4->OptList; Status = EFI_SUCCESS; if (Cache4->OfferType == PxeOfferTypeDhcpBinl) { // // DhcpBinl offer is selected, so need try to request bootfilename by this offer. // if (EFI_ERROR (PxeBcRetryBinlOffer (Private, SelectIndex))) { Status = EFI_NO_RESPONSE; } } else if (Cache4->OfferType == PxeOfferTypeDhcpOnly) { if (Private->IsProxyRecved) { // // DhcpOnly offer is selected, so need try to request bootfile name. // ProxyIndex = 0; if (Private->IsOfferSorted) { // // The proxy offer should be determined if select by default policy. // IsOfferSorted means all offers are labeled by OfferIndex. // ASSERT (Private->SelectProxyType < PxeOfferTypeMax); ASSERT (Private->OfferCount[Private->SelectProxyType] > 0); if (Private->SelectProxyType == PxeOfferTypeProxyBinl) { // // Try all the cached ProxyBinl offer one by one to request bootfile name. // for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) { ASSERT (Index < PXEBC_OFFER_MAX_NUM); ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index]; if (!EFI_ERROR (PxeBcRetryBinlOffer (Private, ProxyIndex))) { break; } } if (Index == Private->OfferCount[Private->SelectProxyType]) { Status = EFI_NO_RESPONSE; } } else { // // For other proxy offers, only one is buffered. // ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0]; } } else { // // The proxy offer should not be determined if select by received order. // Status = EFI_NO_RESPONSE; for (Index = 0; Index < Private->OfferNum; Index++) { ASSERT (Index < PXEBC_OFFER_MAX_NUM); Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer; OfferType = Private->OfferBuffer[Index].Dhcp4.OfferType; if (!IS_PROXY_DHCP_OFFER (Offer)) { // // Skip non proxy DHCPv4 offers. // continue; } if (OfferType == PxeOfferTypeProxyBinl) { // // Try all the cached ProxyBinl offer one by one to request bootfile name. // if (EFI_ERROR (PxeBcRetryBinlOffer (Private, Index))) { continue; } } Private->SelectProxyType = OfferType; ProxyIndex = Index; Status = EFI_SUCCESS; break; } } if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) { // // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it. // Status = PxeBcCopyProxyOffer (Private, ProxyIndex); } } else { // // Otherwise, the bootfile name must be included in DhcpOnly offer. // if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) { Status = EFI_NOT_FOUND; } } } if (!EFI_ERROR (Status)) { // // All PXE boot information is ready by now. // Mode = Private->PxeBc.Mode; Offer = &Cache4->Packet.Offer; Ack = &Private->DhcpAck.Dhcp4.Packet.Ack; if (Cache4->OfferType == PxeOfferTypeBootp) { // // Bootp is a special case that only 2 packets involved instead of 4. So the bootp's reply // should be taken as ack. // Ack = Offer; } Status = PxeBcCopyDhcp4Ack (Private, Ack, TRUE); if (EFI_ERROR (Status)) { return Status; } Mode->DhcpDiscoverValid = TRUE; } return Status; } /** EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver to intercept events that occurred in the configuration process. @param[in] This Pointer to the EFI DHCPv4 Protocol. @param[in] Context Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure(). @param[in] CurrentState The current operational state of the EFI DHCPv4 Protocol driver. @param[in] Dhcp4Event The event that occurs in the current state, which usually means a state transition. @param[in] Packet The DHCPv4 packet that is going to be sent or already received. @param[out] NewPacket The packet that is used to replace the above Packet. @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process. @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol driver will continue to wait for more DHCPOFFER packets until the retry timeout expires. @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process and return to the Dhcp4Init or Dhcp4InitReboot state. **/ EFI_STATUS EFIAPI PxeBcDhcp4CallBack ( IN EFI_DHCP4_PROTOCOL *This, IN VOID *Context, IN EFI_DHCP4_STATE CurrentState, IN EFI_DHCP4_EVENT Dhcp4Event, IN EFI_DHCP4_PACKET *Packet OPTIONAL, OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL ) { PXEBC_PRIVATE_DATA *Private; EFI_PXE_BASE_CODE_MODE *Mode; EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback; EFI_DHCP4_PACKET_OPTION *MaxMsgSize; UINT16 Value; EFI_STATUS Status; BOOLEAN Received; if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer) && (Dhcp4Event != Dhcp4SendDiscover) && (Dhcp4Event != Dhcp4RcvdAck)) { return EFI_SUCCESS; } ASSERT (Packet != NULL); Private = (PXEBC_PRIVATE_DATA *) Context; Mode = Private->PxeBc.Mode; Callback = Private->PxeBcCallback; // // Override the Maximum DHCP Message Size. // MaxMsgSize = PxeBcParseDhcp4Options ( Packet->Dhcp4.Option, GET_OPTION_BUFFER_LEN (Packet), DHCP4_TAG_MAXMSG ); if (MaxMsgSize != NULL) { Value = HTONS (PXEBC_DHCP4_PACKET_MAX_SIZE); CopyMem (MaxMsgSize->Data, &Value, sizeof (Value)); } // // Callback to user if any packets sent or received. // if (Dhcp4Event != Dhcp4SelectOffer && Callback != NULL) { Received = (BOOLEAN) (Dhcp4Event == Dhcp4RcvdOffer || Dhcp4Event == Dhcp4RcvdAck); Status = Callback->Callback ( Callback, Private->Function, Received, Packet->Length, (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp4 ); if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) { return EFI_ABORTED; } } Status = EFI_SUCCESS; switch (Dhcp4Event) { case Dhcp4SendDiscover: if (Packet->Length > PXEBC_DHCP4_PACKET_MAX_SIZE) { // // If the to be sent packet exceeds the maximum length, abort the DHCP process. // Status = EFI_ABORTED; break; } // // Cache the DHCPv4 discover packet to mode data directly. // It need to check SendGuid as well as Dhcp4SendRequest. // CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp4, Packet->Length); case Dhcp4SendRequest: if (Packet->Length > PXEBC_DHCP4_PACKET_MAX_SIZE) { // // If the to be sent packet exceeds the maximum length, abort the DHCP process. // Status = EFI_ABORTED; break; } if (Mode->SendGUID) { // // Send the system Guid instead of the MAC address as the hardware address if required. // if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) Packet->Dhcp4.Header.ClientHwAddr))) { // // Zero the Guid to indicate NOT programmable if failed to get system Guid. // DEBUG ((EFI_D_WARN, "PXE: Failed to read system GUID from the smbios table!\n")); ZeroMem (Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID)); } Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID); } break; case Dhcp4RcvdOffer: Status = EFI_NOT_READY; if (Packet->Length > PXEBC_DHCP4_PACKET_MAX_SIZE) { // // Ignore the incoming packets which exceed the maximum length. // break; } if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) { // // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record // the OfferIndex and OfferCount. // If error happens, just ignore this packet and continue to wait more offer. // PxeBcCacheDhcp4Offer (Private, Packet); } break; case Dhcp4SelectOffer: ASSERT (NewPacket != NULL); // // Select offer by the default policy or by order, and record the SelectIndex // and SelectProxyType. // PxeBcSelectDhcp4Offer (Private); if (Private->SelectIndex == 0) { Status = EFI_ABORTED; } else { *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer; } break; case Dhcp4RcvdAck: // // Cache the DHCPv4 ack to Private->Dhcp4Ack, but it's not the final ack in mode data // without verification. // ASSERT (Private->SelectIndex != 0); Status = PxeBcCopyDhcp4Ack (Private, Packet, FALSE); if (EFI_ERROR (Status)) { Status = EFI_ABORTED; } break; default: break; } return Status; } /** Build and send out the request packet for the bootfile, and parse the reply. @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 server address. @param[in] IpCount The total count of the server address. @param[in] SrvList Pointer to EFI_PXE_BASE_CODE_SRVLIST. @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 PxeBcDhcp4Discover ( 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 ) { EFI_PXE_BASE_CODE_UDP_PORT Sport; EFI_PXE_BASE_CODE_MODE *Mode; EFI_DHCP4_PROTOCOL *Dhcp4; EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN Token; BOOLEAN IsBCast; EFI_STATUS Status; UINT16 RepIndex; UINT16 SrvIndex; UINT16 TryIndex; EFI_DHCP4_LISTEN_POINT ListenPoint; EFI_DHCP4_PACKET *Response; UINT8 Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE]; EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_OPTION_MAX_NUM]; UINT32 OptCount; EFI_DHCP4_PACKET_OPTION *PxeOpt; PXEBC_OPTION_BOOT_ITEM *PxeBootItem; UINT8 VendorOptLen; UINT32 Xid; Mode = Private->PxeBc.Mode; Dhcp4 = Private->Dhcp4; Status = EFI_SUCCESS; ZeroMem (&Token, sizeof (EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN)); // // Use broadcast if destination address not specified. // if (DestIp == NULL) { Sport = PXEBC_DHCP4_S_PORT; IsBCast = TRUE; } else { Sport = PXEBC_BS_DISCOVER_PORT; IsBCast = FALSE; } if (!UseBis && Layer != NULL) { *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK; } // // Build all the options for the request packet. // OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, TRUE); if (Private->IsDoDiscover) { // // Add vendor option of PXE_BOOT_ITEM // VendorOptLen = (UINT8) ((sizeof (EFI_DHCP4_PACKET_OPTION) - 1) * 2 + sizeof (PXEBC_OPTION_BOOT_ITEM) + 1); OptList[OptCount] = AllocateZeroPool (VendorOptLen); if (OptList[OptCount] == NULL) { return EFI_OUT_OF_RESOURCES; } OptList[OptCount]->OpCode = DHCP4_TAG_VENDOR; OptList[OptCount]->Length = (UINT8) (VendorOptLen - 2); PxeOpt = (EFI_DHCP4_PACKET_OPTION *) OptList[OptCount]->Data; PxeOpt->OpCode = PXEBC_VENDOR_TAG_BOOT_ITEM; PxeOpt->Length = (UINT8) sizeof (PXEBC_OPTION_BOOT_ITEM); PxeBootItem = (PXEBC_OPTION_BOOT_ITEM *) PxeOpt->Data; PxeBootItem->Type = HTONS (Type); PxeOpt->Data[PxeOpt->Length] = DHCP4_TAG_EOP; if (Layer != NULL) { PxeBootItem->Layer = HTONS (*Layer); } OptCount++; } // // Build the request packet with seed packet and option list. // Status = Dhcp4->Build ( Dhcp4, &Private->SeedPacket, 0, NULL, OptCount, OptList, &Token.Packet ); // // Free the vendor option of PXE_BOOT_ITEM. // if (Private->IsDoDiscover) { FreePool (OptList[OptCount - 1]); } if (EFI_ERROR (Status)) { return Status; } if (Mode->SendGUID) { if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) Token.Packet->Dhcp4.Header.ClientHwAddr))) { // // Zero the Guid to indicate NOT programmable if failed to get system Guid. // DEBUG ((EFI_D_WARN, "PXE: Failed to read system GUID from the smbios table!\n")); ZeroMem (Token.Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID)); } Token.Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID); } // // Set fields of the token for the request packet. // Xid = NET_RANDOM (NetRandomInitSeed ()); Token.Packet->Dhcp4.Header.Xid = HTONL (Xid); Token.Packet->Dhcp4.Header.Reserved = HTONS ((UINT16) ((IsBCast) ? 0x8000 : 0x0)); CopyMem (&Token.Packet->Dhcp4.Header.ClientAddr, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS)); Token.RemotePort = Sport; if (IsBCast) { SetMem (&Token.RemoteAddress, sizeof (EFI_IPv4_ADDRESS), 0xff); } else { CopyMem (&Token.RemoteAddress, DestIp, sizeof (EFI_IPv4_ADDRESS)); } CopyMem (&Token.GatewayAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS)); if (!IsBCast) { Token.ListenPointCount = 1; Token.ListenPoints = &ListenPoint; Token.ListenPoints[0].ListenPort = PXEBC_BS_DISCOVER_PORT; CopyMem (&Token.ListenPoints[0].ListenAddress, &Private->StationIp, sizeof(EFI_IPv4_ADDRESS)); CopyMem (&Token.ListenPoints[0].SubnetMask, &Private->SubnetMask, sizeof(EFI_IPv4_ADDRESS)); } // // Send out the request packet to discover the bootfile. // for (TryIndex = 1; TryIndex <= PXEBC_BOOT_REQUEST_RETRIES; TryIndex++) { Token.TimeoutValue = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * TryIndex); Token.Packet->Dhcp4.Header.Seconds = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * (TryIndex - 1)); Status = Dhcp4->TransmitReceive (Dhcp4, &Token); if (Token.Status != EFI_TIMEOUT) { break; } } if (TryIndex > PXEBC_BOOT_REQUEST_RETRIES) { // // No server response our PXE request // Status = EFI_TIMEOUT; } if (!EFI_ERROR (Status)) { RepIndex = 0; SrvIndex = 0; Response = Token.ResponseList; // // Find the right PXE Reply according to server address. // while (RepIndex < Token.ResponseCount) { if (Response->Length > PXEBC_DHCP4_PACKET_MAX_SIZE) { SrvIndex = 0; RepIndex++; Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size); continue; } while (SrvIndex < IpCount) { if (SrvList[SrvIndex].AcceptAnyResponse) { break; } if ((SrvList[SrvIndex].Type == Type) && EFI_IP4_EQUAL (&Response->Dhcp4.Header.ServerAddr, &SrvList[SrvIndex].IpAddr)) { break; } SrvIndex++; } if ((IpCount != SrvIndex) || (IpCount == 0)) { break; } SrvIndex = 0; RepIndex++; Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size); } if (RepIndex < Token.ResponseCount) { // // Cache the right PXE reply packet here, set valid flag later. // Especially for PXE discover packet, store it into mode data here. // if (Private->IsDoDiscover) { Status = PxeBcCacheDhcp4Packet (&Private->PxeReply.Dhcp4.Packet.Ack, Response); if (EFI_ERROR(Status)) { goto ON_EXIT; } CopyMem (&Mode->PxeDiscover, &Token.Packet->Dhcp4, Token.Packet->Length); } else { Status = PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Response); if (EFI_ERROR(Status)) { goto ON_EXIT; } } } else { // // Not found the right PXE reply packet. // Status = EFI_NOT_FOUND; } } ON_EXIT: if (Token.ResponseList != NULL) { FreePool (Token.ResponseList); } if (Token.Packet != NULL) { FreePool (Token.Packet); } return Status; } /** Switch the Ip4 policy to static. @param[in] Private The pointer to PXEBC_PRIVATE_DATA. @retval EFI_SUCCESS The policy is already configured to static. @retval Others Other error as indicated.. **/ EFI_STATUS PxeBcSetIp4Policy ( IN PXEBC_PRIVATE_DATA *Private ) { EFI_STATUS Status; EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; EFI_IP4_CONFIG2_POLICY Policy; UINTN DataSize; Ip4Config2 = Private->Ip4Config2; DataSize = sizeof (EFI_IP4_CONFIG2_POLICY); Status = Ip4Config2->GetData ( Ip4Config2, Ip4Config2DataTypePolicy, &DataSize, &Policy ); if (EFI_ERROR (Status)) { return Status; } if (Policy != Ip4Config2PolicyStatic) { Policy = Ip4Config2PolicyStatic; Status= Ip4Config2->SetData ( Ip4Config2, Ip4Config2DataTypePolicy, sizeof (EFI_IP4_CONFIG2_POLICY), &Policy ); if (EFI_ERROR (Status)) { return Status; } } return EFI_SUCCESS; } /** Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information. @param[in] Private Pointer to PxeBc private data. @param[in] Dhcp4 Pointer to the EFI_DHCP4_PROTOCOL @retval EFI_SUCCESS The D.O.R.A process successfully finished. @retval Others Failed to finish the D.O.R.A process. **/ EFI_STATUS PxeBcDhcp4Dora ( IN PXEBC_PRIVATE_DATA *Private, IN EFI_DHCP4_PROTOCOL *Dhcp4 ) { EFI_PXE_BASE_CODE_MODE *PxeMode; EFI_DHCP4_CONFIG_DATA Config; EFI_DHCP4_MODE_DATA Mode; EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_OPTION_MAX_NUM]; UINT8 Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE]; UINT32 OptCount; EFI_STATUS Status; ASSERT (Dhcp4 != NULL); Status = EFI_SUCCESS; PxeMode = Private->PxeBc.Mode; // // Build option list for the request packet. // OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, FALSE); ASSERT (OptCount> 0); ZeroMem (&Mode, sizeof (EFI_DHCP4_MODE_DATA)); ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA)); Config.OptionCount = OptCount; Config.OptionList = OptList; Config.Dhcp4Callback = PxeBcDhcp4CallBack; Config.CallbackContext = Private; Config.DiscoverTryCount = PXEBC_DHCP_RETRIES; Config.DiscoverTimeout = mPxeDhcpTimeout; // // Configure the DHCPv4 instance for PXE boot. // Status = Dhcp4->Configure (Dhcp4, &Config); if (EFI_ERROR (Status)) { goto ON_EXIT; } // // Initialize the record fields for DHCPv4 offer in private data. // Private->IsProxyRecved = FALSE; Private->OfferNum = 0; ZeroMem (Private->OfferCount, sizeof (Private->OfferCount)); ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex)); Status = Dhcp4->Start (Dhcp4, NULL); if (EFI_ERROR (Status)) { if (Status == EFI_ICMP_ERROR) { PxeMode->IcmpErrorReceived = TRUE; } if (Status == EFI_TIMEOUT && Private->OfferNum > 0) { Status = EFI_NO_RESPONSE; } goto ON_EXIT; } // // Get the acquired IPv4 address and store them. // Status = Dhcp4->GetModeData (Dhcp4, &Mode); if (EFI_ERROR (Status)) { goto ON_EXIT; } ASSERT (Mode.State == Dhcp4Bound); CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS)); CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS)); CopyMem (&PxeMode->StationIp, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS)); CopyMem (&PxeMode->SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS)); Status = PxeBcFlushStationIp (Private, &Private->StationIp, &Private->SubnetMask); if (EFI_ERROR (Status)) { goto ON_EXIT; } // // Check the selected offer whether BINL retry is needed. // Status = PxeBcHandleDhcp4Offer (Private); AsciiPrint ("\n Station IP address is "); PxeBcShowIp4Addr (&Private->StationIp.v4); AsciiPrint ("\n"); ON_EXIT: if (EFI_ERROR (Status)) { Dhcp4->Stop (Dhcp4); Dhcp4->Configure (Dhcp4, NULL); } else { ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA)); Dhcp4->Configure (Dhcp4, &Config); Private->IsAddressOk = TRUE; } return Status; }