| /** @file | |
| Copyright (c) 2004 - 2007, Intel Corporation | |
| All rights reserved. This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| Module Name: | |
| pxe_bc_udp.c | |
| Abstract: | |
| **/ | |
| #include "Bc.h" | |
| // | |
| // ////////////////////////////////////////////////////////////////////// | |
| // | |
| // Udp Write Routine - called by base code - e.g. TFTP - already locked | |
| // | |
| /** | |
| @return EFI_SUCCESS := | |
| @return EFI_INVALID_PARAMETER := | |
| @return other := | |
| **/ | |
| EFI_STATUS | |
| UdpWrite ( | |
| IN PXE_BASECODE_DEVICE *Private, | |
| IN UINT16 OpFlags, | |
| IN EFI_IP_ADDRESS *DestIpPtr, | |
| IN EFI_PXE_BASE_CODE_UDP_PORT *DestPortPtr, | |
| IN EFI_IP_ADDRESS *GatewayIpPtr, OPTIONAL | |
| IN EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL | |
| IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL | |
| IN UINTN *HeaderSizePtr, OPTIONAL | |
| IN VOID *HeaderPtr, OPTIONAL | |
| IN UINTN *BufferSizeptr, | |
| IN VOID *BufferPtr | |
| ) | |
| { | |
| UINTN TotalLength; | |
| UINTN HeaderSize; | |
| EFI_PXE_BASE_CODE_UDP_PORT DefaultSrcPort; | |
| // | |
| // | |
| // | |
| HeaderSize = (HeaderSizePtr != NULL) ? *HeaderSizePtr : 0; | |
| DefaultSrcPort = 0; | |
| // | |
| // check parameters | |
| // | |
| if (BufferSizeptr == NULL || | |
| BufferPtr == NULL || | |
| DestIpPtr == NULL || | |
| DestPortPtr == NULL || | |
| (HeaderSizePtr != NULL && *HeaderSizePtr == 0) || | |
| (HeaderSize != 0 && HeaderPtr == NULL) || | |
| (GatewayIpPtr != NULL && !IS_INADDR_UNICAST(GatewayIpPtr)) || | |
| (OpFlags &~(EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT)) | |
| ) { | |
| DEBUG ( | |
| (DEBUG_WARN, | |
| "\nUdpWrite() Exit #1 %xh (%r)", | |
| EFI_INVALID_PARAMETER, | |
| EFI_INVALID_PARAMETER) | |
| ); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| TotalLength = *BufferSizeptr + HeaderSize + sizeof (UDPV4_HEADER); | |
| if (TotalLength > 0x0000ffff) { | |
| DEBUG ( | |
| (DEBUG_WARN, | |
| "\nUdpWrite() Exit #2 %xh (%r)", | |
| EFI_BAD_BUFFER_SIZE, | |
| EFI_BAD_BUFFER_SIZE) | |
| ); | |
| return EFI_BAD_BUFFER_SIZE; | |
| } | |
| if (SrcIpPtr == NULL) { | |
| SrcIpPtr = &Private->EfiBc.Mode->StationIp; | |
| } | |
| if (SrcPortPtr == NULL) { | |
| SrcPortPtr = &DefaultSrcPort; | |
| OpFlags |= EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT; | |
| } | |
| if (OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) { | |
| *SrcPortPtr = Private->RandomPort; | |
| if (++Private->RandomPort == 0) { | |
| Private->RandomPort = PXE_RND_PORT_LOW; | |
| } | |
| } | |
| #define IpTxBuffer ((IPV4_BUFFER *) Private->TransmitBufferPtr) | |
| // | |
| // build pseudo header and udp header in transmit buffer | |
| // | |
| #define Udpv4Base ((UDPV4_HEADERS *) (IpTxBuffer->u.Data - sizeof (UDPV4_PSEUDO_HEADER))) | |
| Udpv4Base->Udpv4PseudoHeader.SrcAddr.L = SrcIpPtr->Addr[0]; | |
| Udpv4Base->Udpv4PseudoHeader.DestAddr.L = DestIpPtr->Addr[0]; | |
| Udpv4Base->Udpv4PseudoHeader.Zero = 0; | |
| Udpv4Base->Udpv4PseudoHeader.Protocol = PROT_UDP; | |
| Udpv4Base->Udpv4PseudoHeader.TotalLength = HTONS (TotalLength); | |
| Udpv4Base->Udpv4Header.SrcPort = HTONS (*SrcPortPtr); | |
| Udpv4Base->Udpv4Header.DestPort = HTONS (*DestPortPtr); | |
| Udpv4Base->Udpv4Header.TotalLength = Udpv4Base->Udpv4PseudoHeader.TotalLength; | |
| Udpv4Base->Udpv4Header.Checksum = 0; | |
| if (HeaderSize != 0) { | |
| CopyMem (IpTxBuffer->u.Udp.Data, HeaderPtr, HeaderSize); | |
| } | |
| HeaderSize += sizeof (UDPV4_HEADER); | |
| Udpv4Base->Udpv4Header.Checksum = IpChecksum2 ( | |
| (UINT16 *) Udpv4Base, | |
| HeaderSize + sizeof (UDPV4_PSEUDO_HEADER), | |
| (UINT16 *) BufferPtr, | |
| (UINT16) *BufferSizeptr | |
| ); | |
| if (Udpv4Base->Udpv4Header.Checksum == 0) { | |
| Udpv4Base->Udpv4Header.Checksum = 0xffff; | |
| // | |
| // transmit zero checksum as ones complement | |
| // | |
| } | |
| return Ip4Send ( | |
| Private, | |
| OpFlags, | |
| PROT_UDP, | |
| Udpv4Base->Udpv4PseudoHeader.SrcAddr.L, | |
| Udpv4Base->Udpv4PseudoHeader.DestAddr.L, | |
| (GatewayIpPtr) ? GatewayIpPtr->Addr[0] : 0, | |
| HeaderSize, | |
| BufferPtr, | |
| *BufferSizeptr | |
| ); | |
| } | |
| // | |
| // ////////////////////////////////////////////////////////// | |
| // | |
| // BC Udp Write Routine | |
| // | |
| /** | |
| @return EFI_SUCCESS := | |
| @return other := | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BcUdpWrite ( | |
| IN EFI_PXE_BASE_CODE_PROTOCOL *This, | |
| IN UINT16 OpFlags, | |
| IN EFI_IP_ADDRESS *DestIpPtr, | |
| IN EFI_PXE_BASE_CODE_UDP_PORT *DestPortPtr, | |
| IN EFI_IP_ADDRESS *GatewayIpPtr, OPTIONAL | |
| IN EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL | |
| IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL | |
| IN UINTN *HeaderSizePtr, OPTIONAL | |
| IN VOID *HeaderPtr, OPTIONAL | |
| IN UINTN *BufferSizeptr, | |
| IN VOID *BufferPtr | |
| ) | |
| { | |
| EFI_STATUS StatCode; | |
| PXE_BASECODE_DEVICE *Private; | |
| // | |
| // Lock the instance data and make sure started | |
| // | |
| StatCode = EFI_SUCCESS; | |
| if (This == NULL) { | |
| DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); | |
| if (Private == NULL) { | |
| DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| EfiAcquireLock (&Private->Lock); | |
| if (This->Mode == NULL || !This->Mode->Started) { | |
| DEBUG ((DEBUG_ERROR, "BC was not started.")); | |
| EfiReleaseLock (&Private->Lock); | |
| return EFI_NOT_STARTED; | |
| } | |
| Private->Function = EFI_PXE_BASE_CODE_FUNCTION_UDP_WRITE; | |
| // | |
| // Issue BC command | |
| // | |
| StatCode = UdpWrite ( | |
| Private, | |
| OpFlags, | |
| DestIpPtr, | |
| DestPortPtr, | |
| GatewayIpPtr, | |
| SrcIpPtr, | |
| SrcPortPtr, | |
| HeaderSizePtr, | |
| HeaderPtr, | |
| BufferSizeptr, | |
| BufferPtr | |
| ); | |
| // | |
| // Unlock the instance data | |
| // | |
| EfiReleaseLock (&Private->Lock); | |
| return StatCode; | |
| } | |
| // | |
| // ///////////////////////////////////////////////////////////////////// | |
| // | |
| // Udp Read Routine - called by base code - e.g. TFTP - already locked | |
| // | |
| /** | |
| @return EFI_SUCCESS := | |
| @return EFI_INVALID_PARAMETER := | |
| @return other := | |
| **/ | |
| EFI_STATUS | |
| UdpRead ( | |
| IN PXE_BASECODE_DEVICE *Private, | |
| IN UINT16 OpFlags, | |
| IN OUT EFI_IP_ADDRESS *DestIpPtr, OPTIONAL | |
| IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPortPtr, OPTIONAL | |
| IN OUT EFI_IP_ADDRESS *SrcIpPtr, OPTIONAL | |
| IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPortPtr, OPTIONAL | |
| IN UINTN *HeaderSizePtr, OPTIONAL | |
| IN VOID *HeaderPtr, OPTIONAL | |
| IN OUT UINTN *BufferSizeptr, | |
| IN VOID *BufferPtr, | |
| EFI_EVENT TimeoutEvent | |
| ) | |
| { | |
| EFI_STATUS StatCode; | |
| EFI_IP_ADDRESS TmpSrcIp; | |
| EFI_IP_ADDRESS TmpDestIp; | |
| UINTN BufferSize; | |
| UINTN HeaderSize; | |
| // | |
| // combination structure of pseudo header/udp header | |
| // | |
| #pragma pack (1) | |
| struct { | |
| UDPV4_PSEUDO_HEADER Udpv4PseudoHeader; | |
| UDPV4_HEADER Udpv4Header; | |
| UINT8 ProtHdr[64]; | |
| } Hdrs; | |
| #pragma pack () | |
| HeaderSize = (HeaderSizePtr != NULL) ? *HeaderSizePtr : 0; | |
| // | |
| // read [with filtering] | |
| // check parameters | |
| // | |
| if (BufferSizeptr == NULL || | |
| BufferPtr == NULL || | |
| (HeaderSize != 0 && HeaderPtr == NULL) || | |
| (OpFlags &~UDP_FILTER_MASK) | |
| // | |
| // if filtering on a particular IP/Port, need it | |
| // | |
| || | |
| (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) && SrcIpPtr == NULL) || | |
| (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) && SrcPortPtr == NULL) || | |
| (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) && DestPortPtr == NULL) | |
| ) { | |
| DEBUG ((DEBUG_INFO, "\nUdpRead() Exit #1 Invalid Parameter")); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // in case we loop | |
| // | |
| BufferSize = *BufferSizeptr; | |
| // | |
| // we need source and dest IPs for pseudo header | |
| // | |
| if (SrcIpPtr == NULL) { | |
| SrcIpPtr = &TmpSrcIp; | |
| } | |
| if (DestIpPtr == NULL) { | |
| DestIpPtr = &TmpDestIp; | |
| TmpDestIp = Private->EfiBc.Mode->StationIp; | |
| } | |
| #if SUPPORT_IPV6 | |
| if (Private->EfiBc.Mode->UsingIpv6) { | |
| // | |
| // %%TBD | |
| // | |
| } | |
| #endif | |
| for (;;) { | |
| *BufferSizeptr = BufferSize; | |
| StatCode = IpReceive ( | |
| Private, | |
| OpFlags, | |
| SrcIpPtr, | |
| DestIpPtr, | |
| PROT_UDP, | |
| &Hdrs.Udpv4Header, | |
| HeaderSize + sizeof Hdrs.Udpv4Header, | |
| BufferPtr, | |
| BufferSizeptr, | |
| TimeoutEvent | |
| ); | |
| if (StatCode == EFI_SUCCESS || StatCode == EFI_BUFFER_TOO_SMALL) { | |
| UINT16 SPort; | |
| UINT16 DPort; | |
| SPort = NTOHS (Hdrs.Udpv4Header.SrcPort); | |
| DPort = NTOHS (Hdrs.Udpv4Header.DestPort); | |
| // | |
| // do filtering | |
| // | |
| if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) && *SrcPortPtr != SPort) { | |
| continue; | |
| } | |
| if (!(OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) && *DestPortPtr != DPort) { | |
| continue; | |
| } | |
| // | |
| // check checksum | |
| // | |
| if (StatCode == EFI_SUCCESS && Hdrs.Udpv4Header.Checksum) { | |
| Hdrs.Udpv4PseudoHeader.SrcAddr.L = SrcIpPtr->Addr[0]; | |
| Hdrs.Udpv4PseudoHeader.DestAddr.L = DestIpPtr->Addr[0]; | |
| Hdrs.Udpv4PseudoHeader.Zero = 0; | |
| Hdrs.Udpv4PseudoHeader.Protocol = PROT_UDP; | |
| Hdrs.Udpv4PseudoHeader.TotalLength = Hdrs.Udpv4Header.TotalLength; | |
| if (Hdrs.Udpv4Header.Checksum == 0xffff) { | |
| Hdrs.Udpv4Header.Checksum = 0; | |
| } | |
| if (IpChecksum2 ( | |
| (UINT16 *) &Hdrs.Udpv4PseudoHeader, | |
| HeaderSize + sizeof (Hdrs.Udpv4PseudoHeader) + sizeof (Hdrs.Udpv4Header), | |
| (UINT16 *) BufferPtr, | |
| *BufferSizeptr | |
| )) { | |
| DEBUG ( | |
| (DEBUG_INFO, | |
| "\nUdpRead() Hdrs.Udpv4PseudoHeader == %Xh", | |
| Hdrs.Udpv4PseudoHeader) | |
| ); | |
| DEBUG ( | |
| (DEBUG_INFO, | |
| "\nUdpRead() Header size == %d", | |
| HeaderSize + sizeof (Hdrs.Udpv4PseudoHeader)) | |
| ); | |
| DEBUG ( | |
| (DEBUG_INFO, | |
| "\nUdpRead() BufferPtr == %Xh", | |
| BufferPtr) | |
| ); | |
| DEBUG ( | |
| (DEBUG_INFO, | |
| "\nUdpRead() Buffer size == %d", | |
| *BufferSizeptr) | |
| ); | |
| DEBUG ((DEBUG_INFO, "\nUdpRead() Exit #2 Device Error")); | |
| return EFI_DEVICE_ERROR; | |
| } | |
| } | |
| // | |
| // all passed | |
| // | |
| if (SrcPortPtr != NULL) { | |
| *SrcPortPtr = SPort; | |
| } | |
| if (DestPortPtr != NULL) { | |
| *DestPortPtr = DPort; | |
| } | |
| if (HeaderSize != 0) { | |
| CopyMem (HeaderPtr, Hdrs.ProtHdr, HeaderSize); | |
| } | |
| } | |
| if ((StatCode != EFI_SUCCESS) && (StatCode != EFI_TIMEOUT)) { | |
| DEBUG ( | |
| (DEBUG_INFO, | |
| "\nUdpRead() Exit #3 %Xh %r", | |
| StatCode, | |
| StatCode) | |
| ); | |
| } | |
| return StatCode; | |
| } | |
| } | |
| // | |
| // ////////////////////////////////////////////////////////// | |
| // | |
| // BC Udp Read Routine | |
| // | |
| /** | |
| @return EFI_SUCCESS := | |
| @return other := | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BcUdpRead ( | |
| IN EFI_PXE_BASE_CODE_PROTOCOL *This, | |
| IN UINT16 OpFlags, | |
| IN OUT EFI_IP_ADDRESS *DestIp, OPTIONAL | |
| IN OUT EFI_PXE_BASE_CODE_UDP_PORT *DestPort, OPTIONAL | |
| IN OUT EFI_IP_ADDRESS *SrcIp, OPTIONAL | |
| IN OUT EFI_PXE_BASE_CODE_UDP_PORT *SrcPort, OPTIONAL | |
| IN UINTN *HeaderSize, OPTIONAL | |
| IN VOID *HeaderPtr, OPTIONAL | |
| IN OUT UINTN *BufferSize, | |
| IN VOID *BufferPtr | |
| ) | |
| { | |
| EFI_STATUS StatCode; | |
| PXE_BASECODE_DEVICE *Private; | |
| // | |
| // Lock the instance data and make sure started | |
| // | |
| StatCode = EFI_SUCCESS; | |
| if (This == NULL) { | |
| DEBUG ((DEBUG_ERROR, "BC *This pointer == NULL")); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Private = CR (This, PXE_BASECODE_DEVICE, EfiBc, PXE_BASECODE_DEVICE_SIGNATURE); | |
| if (Private == NULL) { | |
| DEBUG ((DEBUG_ERROR, "PXE_BASECODE_DEVICE poiner == NULL")); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| EfiAcquireLock (&Private->Lock); | |
| if (This->Mode == NULL || !This->Mode->Started) { | |
| DEBUG ((DEBUG_ERROR, "BC was not started.")); | |
| EfiReleaseLock (&Private->Lock); | |
| return EFI_NOT_STARTED; | |
| } | |
| Private->Function = EFI_PXE_BASE_CODE_FUNCTION_UDP_READ; | |
| // | |
| // Issue BC command | |
| // | |
| StatCode = UdpRead ( | |
| Private, | |
| OpFlags, | |
| DestIp, | |
| DestPort, | |
| SrcIp, | |
| SrcPort, | |
| HeaderSize, | |
| HeaderPtr, | |
| BufferSize, | |
| BufferPtr, | |
| 0 | |
| ); | |
| // | |
| // Unlock the instance data and return | |
| // | |
| EfiReleaseLock (&Private->Lock); | |
| return StatCode; | |
| } | |
| /* eof - pxe_bc_udp.c */ |