/*++ Copyright (c) 1997 Microsoft Corporation Module Name: tftp.c Abstract: Boot loader TFTP routines. Author: Chuck Lenzmeier (chuckl) December 27, 1996 Revision History: Notes: --*/ #include "precomp.h" #pragma hdrstop // // This removes macro redefinitions which appear because we define __RPC_DOS__, // but rpc.h defines __RPC_WIN32__ // #pragma warning(disable:4005) // // As of 12/17/98, SECURITY_DOS is *not* defined - adamba // #if defined(SECURITY_DOS) // // These appear because we defined SECURITY_DOS // #define __far #define __pascal #define __loadds #endif #include #include #include #if defined(_X86_) #include #endif #if defined(SECURITY_DOS) // // PSECURITY_STRING is not supposed to be used when SECURITY_DOS is // defined -- it should be a WCHAR*. Unfortunately ntlmsp.h breaks // this rule and even uses the SECURITY_STRING structure, which there // is really no equivalent for in 16-bit mode. // typedef SEC_WCHAR * SECURITY_STRING; // more-or-less the intention where it is used typedef SEC_WCHAR * PSECURITY_STRING; #endif #include #if DBG ULONG NetDebugFlag = DEBUG_ERROR | DEBUG_CONN_ERROR | //DEBUG_LOUD | //DEBUG_REAL_LOUD | //DEBUG_STATISTICS | //DEBUG_SEND_RECEIVE | //DEBUG_TRACE | //DEBUG_ARP | //DEBUG_INITIAL_BREAK | 0; #endif // // Global variables // CONNECTION NetTftpConnection; UCHAR NetTftpPacket[3][MAXIMUM_TFTP_PACKET_LENGTH]; // // Local declarations. // NTSTATUS TftpGet ( IN PCONNECTION Connection, IN PTFTP_REQUEST Request ); NTSTATUS TftpPut ( IN PCONNECTION Connection, IN PTFTP_REQUEST Request ); NTSTATUS TftpGetPut ( IN PTFTP_REQUEST Request ) { NTSTATUS status; PCONNECTION connection = NULL; ULONG FileSize; ULONG basePage; #if 0 && DBG LARGE_INTEGER startTime; LARGE_INTEGER endTime; LARGE_INTEGER elapsedTime; LARGE_INTEGER frequency; ULONG seconds; ULONG secondsFraction; ULONG bps; ULONG bpsFraction; #endif #ifndef EFI // // We don't need to do any of this initialization if // we're in EFI. // FileSize = Request->MaximumLength; status = ConnInitialize( &connection, Request->Operation, Request->ServerIpAddress, TFTP_PORT, Request->RemoteFileName, 0, &FileSize ); if ( !NT_SUCCESS(status) ) { return status; } #if 0 && DBG IF_DEBUG(STATISTICS) { startTime = KeQueryPerformanceCounter( &frequency ); } #endif if ( Request->Operation == TFTP_RRQ ) { if ( Request->MemoryAddress != NULL ) { if ( Request->MaximumLength < FileSize ) { ConnError( connection, connection->RemoteHost, connection->RemotePort, TFTP_ERROR_UNDEFINED, "File too big" ); return STATUS_INSUFFICIENT_RESOURCES; } } else { // // NB: (ChuckL) Removed code added by MattH to check for // allocation >= 1/3 of (BlUsableLimit - BlUsableBase) // because calling code now sets BlUsableLimit to 1 GB // or higher. // status = BlAllocateAlignedDescriptor( Request->MemoryType, 0, BYTES_TO_PAGES(FileSize), 0, &basePage ); if (status != ESUCCESS) { ConnError( connection, connection->RemoteHost, connection->RemotePort, TFTP_ERROR_UNDEFINED, "File too big" ); return STATUS_INSUFFICIENT_RESOURCES; } Request->MemoryAddress = (PUCHAR)(KSEG0_BASE | (basePage << PAGE_SHIFT)); Request->MaximumLength = FileSize; DPRINT( REAL_LOUD, ("TftpGetPut: allocated %d bytes at 0x%08x\n", Request->MaximumLength, Request->MemoryAddress) ); } status = TftpGet( connection, Request ); } else { status = TftpPut( connection, Request ); } #else // #ifndef EFI if ( Request->Operation == TFTP_RRQ ) { status = TftpGet( connection, Request ); } else { status = TftpPut( connection, Request ); } if( status != STATUS_SUCCESS ) { status = STATUS_INSUFFICIENT_RESOURCES; } #endif // #ifndef EFI if ( !NT_SUCCESS(status) ) { return status; } return status; } // TftpGetPut //#if 0 #ifdef EFI extern VOID FlipToPhysical ( ); extern VOID FlipToVirtual ( ); NTSTATUS TftpGet ( IN OUT PCONNECTION Connection, IN PTFTP_REQUEST Request ) { EFI_STATUS Status; CHAR16 *Size = NULL; PVOID MyBuffer = NULL; EFI_IP_ADDRESS MyServerIpAddress; INTN Count = 0; INTN BufferSizeX = sizeof(CHAR16); ULONG basePage; UINTN BlockSize = 512; // // They sent us an IP address as a ULONG. We need to convert // that into an EFI_IP_ADDRESS. // for( Count = 0; Count < 4; Count++ ) { MyServerIpAddress.v4.Addr[Count] = PXEClient->Mode->ProxyOffer.Dhcpv4.BootpSiAddr[Count]; } // // Get the file size, allocate some memory, then get the file. // FlipToPhysical(); Status = PXEClient->Mtftp( PXEClient, EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, Size, TRUE, &BufferSizeX, &BlockSize, &MyServerIpAddress, Request->RemoteFileName, 0, FALSE ); FlipToVirtual(); if( Status != EFI_SUCCESS ) { return (NTSTATUS)Status; } Status = BlAllocateAlignedDescriptor( Request->MemoryType, 0, (ULONG) BYTES_TO_PAGES(BufferSizeX), 0, &basePage ); if ( Status != ESUCCESS ) { if( BdDebuggerEnabled ) { DbgPrint( "TftpGet: BlAllocate failed! (%d)\n", Status ); } return STATUS_INSUFFICIENT_RESOURCES; } Request->MemoryAddress = (PUCHAR)(KSEG0_BASE | ((ULONGLONG)basePage << PAGE_SHIFT) ); Request->MaximumLength = (ULONG)BufferSizeX; // // Make sure we send EFI a physical address. // MyBuffer = (PVOID)((ULONGLONG)(Request->MemoryAddress) & ~KSEG0_BASE); FlipToPhysical(); Status = PXEClient->Mtftp( PXEClient, EFI_PXE_BASE_CODE_TFTP_READ_FILE, MyBuffer, TRUE, &BufferSizeX, NULL, &MyServerIpAddress, Request->RemoteFileName, 0, FALSE ); FlipToVirtual(); if( Status != EFI_SUCCESS ) { if( BdDebuggerEnabled ) { DbgPrint( "TftpGet: GetFile failed! (%d)\n", Status ); } return (NTSTATUS)Status; } Request->BytesTransferred = (ULONG)BufferSizeX; return (NTSTATUS)Status; } // TftpGet NTSTATUS TftpPut ( IN OUT PCONNECTION Connection, IN PTFTP_REQUEST Request ) { EFI_STATUS Status; EFI_IP_ADDRESS MyServerIpAddress; INTN Count = 0; PVOID MyBuffer = NULL; // // They sent us an IP address as a ULONG. We need to convert // that into an EFI_IP_ADDRESS. // for( Count = 0; Count < 4; Count++ ) { MyServerIpAddress.v4.Addr[Count] = PXEClient->Mode->ProxyOffer.Dhcpv4.BootpSiAddr[Count]; } // // Make sure we send EFI a physical address. // MyBuffer = (PVOID)((ULONGLONG)(Request->MemoryAddress) & ~KSEG0_BASE); FlipToPhysical(); Status = PXEClient->Mtftp( PXEClient, EFI_PXE_BASE_CODE_TFTP_WRITE_FILE, MyBuffer, TRUE, (UINTN *)(&Request->MaximumLength), NULL, &MyServerIpAddress, Request->RemoteFileName, 0, FALSE ); FlipToVirtual(); if( Status != EFI_SUCCESS ) { if( BdDebuggerEnabled ) { DbgPrint( "TftpPut: WriteFile failed! (%d)\n", Status ); } } return (NTSTATUS)Status; } // TftpPut #else //#ifdef EFI NTSTATUS TftpGet ( IN OUT PCONNECTION Connection, IN PTFTP_REQUEST Request ) { NTSTATUS status; PTFTP_PACKET packet; ULONG length; ULONG offset; PUCHAR packetData; ULONG lastProgressPercent = -1; ULONG currentProgressPercent; DPRINT( TRACE, ("TftpGet\n") ); offset = 0; if ( Request->ShowProgress ) { BlUpdateProgressBar(0); } do { status = ConnReceive( Connection, &packet ); if ( !NT_SUCCESS(status) ) { break; } length = Connection->CurrentLength - 4; packetData = packet->Data; if ( (offset + length) > Request->MaximumLength ) { length = Request->MaximumLength - offset; } RtlCopyMemory( Request->MemoryAddress + offset, packetData, length ); offset += length; if ( Request->ShowProgress ) { currentProgressPercent = (ULONG)(((ULONGLONG)offset * 100) / Request->MaximumLength); if ( currentProgressPercent != lastProgressPercent ) { BlUpdateProgressBar( currentProgressPercent ); } lastProgressPercent = currentProgressPercent; } // // End the loop when we get a packet smaller than the max size -- // the extra check is to handle the first packet (length == offset) // since we get NTLMSSP_MESSAGE_SIGNATURE_SIZE bytes less. // } while ( (length == Connection->BlockSize)); Request->BytesTransferred = offset; return status; } // TftpGet NTSTATUS TftpPut ( IN OUT PCONNECTION Connection, IN PTFTP_REQUEST Request ) { NTSTATUS status; PTFTP_PACKET packet; ULONG length; ULONG offset; DPRINT( TRACE, ("TftpPut\n") ); offset = 0; do { packet = ConnPrepareSend( Connection ); length = Connection->BlockSize; if ( (offset + length) > Request->MaximumLength ) { length = Request->MaximumLength - offset; } RtlCopyMemory( packet->Data, Request->MemoryAddress + offset, length ); status = ConnSend( Connection, length ); if ( !NT_SUCCESS(status) ) { break; } offset += length; } while ( length == Connection->BlockSize ); Request->BytesTransferred = offset; if ( NT_SUCCESS(status) ) { status = ConnWaitForFinalAck( Connection ); } return status; } // TftpPut #endif // #if defined(_IA64_)