/*++ Copyright (c) 2001 Microsoft Corporation Module Name: file.c Abstract: This module contains kd host machine file I/O support. Author: Matt Holle (matth) April-2001 Revision History: --*/ #include "bd.h" #include "bootlib.h" #include "kddll.h" #define TRANSFER_LENGTH 8192 #define KD_MAX_REMOTE_FILES 16 // // Keep track of all the remote files. typedef struct _KD_REMOTE_FILE { ULONG64 RemoteHandle; } KD_REMOTE_FILE, *PKD_REMOTE_FILE; KD_REMOTE_FILE BdRemoteFiles[KD_MAX_REMOTE_FILES]; // KD_CONTEXT KdContext; // temporary buffer used for transferring data back and forth. // UCHAR TransferBuffer[TRANSFER_LENGTH]; UCHAR BdFileTransferBuffer[TRANSFER_LENGTH]; ARC_STATUS BdCreateRemoteFile( OUT PHANDLE Handle, OUT PULONG64 Length, OPTIONAL IN PCHAR FileName, IN ACCESS_MASK DesiredAccess, IN ULONG FileAttributes, IN ULONG ShareAccess, IN ULONG CreateDisposition, IN ULONG CreateOptions ) { DBGKD_FILE_IO Irp; ULONG DownloadedFileIndex; ULONG Index; STRING MessageData; STRING MessageHeader; ULONG ReturnCode; ULONG PacketLength; ANSI_STRING aString; UNICODE_STRING uString; if( !BdDebuggerEnabled ) { return STATUS_DEBUGGER_INACTIVE; } if( (!FileName) || (strlen(FileName) > PACKET_MAX_SIZE) ) { DbgPrint( "BdCreateRemoteFile: Bad parameter\n" ); return STATUS_INVALID_PARAMETER; } if (BdDebuggerNotPresent != FALSE) { return STATUS_DEBUGGER_INACTIVE; } // // Look for an open slot. // for (DownloadedFileIndex = 0; DownloadedFileIndex < KD_MAX_REMOTE_FILES; DownloadedFileIndex++) { if (BdRemoteFiles[DownloadedFileIndex].RemoteHandle == 0) { break; } } if (DownloadedFileIndex >= KD_MAX_REMOTE_FILES) { DbgPrint( "BdCreateRemoteFile: No more empty handles available for this file.\n" ); Irp.Status = STATUS_NO_MEMORY; goto Exit; } // // Fix up the packet that we'll send to the kernel debugger. // Irp.ApiNumber = DbgKdCreateFileApi; Irp.u.CreateFile.DesiredAccess = DesiredAccess; Irp.u.CreateFile.FileAttributes = FileAttributes; Irp.u.CreateFile.ShareAccess = ShareAccess; Irp.u.CreateFile.CreateDisposition = CreateDisposition; Irp.u.CreateFile.CreateOptions = CreateOptions; Irp.u.CreateFile.Handle = 0; Irp.u.CreateFile.Length = 0; MessageHeader.Length = sizeof(Irp); MessageHeader.MaximumLength = sizeof(Irp); MessageHeader.Buffer = (PCHAR)&Irp; // // Send him a unicode file name. // RtlInitString( &aString, FileName ); uString.Buffer = (PWCHAR)BdFileTransferBuffer; uString.MaximumLength = sizeof(BdFileTransferBuffer); RtlAnsiStringToUnicodeString( &uString, &aString, FALSE ); MessageData.Length = (USHORT)((strlen(FileName)+1) * sizeof(WCHAR)); MessageData.Buffer = BdFileTransferBuffer; // // Send packet to the kernel debugger on the host machine and ask him to // send us a handle to the file. // BdSendPacket(PACKET_TYPE_KD_FILE_IO, &MessageHeader, &MessageData); if (BdDebuggerNotPresent != FALSE) { Irp.Status = STATUS_DEBUGGER_INACTIVE; goto Exit; } // // We asked for the handle, now receive it. // MessageData.MaximumLength = sizeof(BdFileTransferBuffer); MessageData.Buffer = (PCHAR)BdFileTransferBuffer; ReturnCode = BD_PACKET_TIMEOUT; Index = 0; while( (ReturnCode == BD_PACKET_TIMEOUT) && (Index < 10) ) { ReturnCode = BdReceivePacket(PACKET_TYPE_KD_FILE_IO, &MessageHeader, &MessageData, &PacketLength); Index++; } // // BdReceivePacket *may* return BD_PACKET_RECEIVED eventhough the kernel // debugger failed to actually find the file we wanted. Therefore, we // need to check the Irp.Status value too before assuming we got the // information we wanted. // // Note: don't check for Irp.u.CreateFile.Length == 0 because we don't // want to exclude downloading zero-length files. // if( (ReturnCode == BD_PACKET_RECEIVED) && (NT_SUCCESS(Irp.Status)) ) { Irp.Status = STATUS_SUCCESS; } else { Irp.Status = STATUS_INVALID_PARAMETER; } Exit: if (NT_SUCCESS(Irp.Status)) { BdRemoteFiles[DownloadedFileIndex].RemoteHandle = Irp.u.CreateFile.Handle; // Add one so that zero is reserved for invalid-handle. *Handle = UlongToHandle(DownloadedFileIndex + 1); if (ARGUMENT_PRESENT(Length)) { *Length = Irp.u.CreateFile.Length; } } return Irp.Status; } ARC_STATUS BdReadRemoteFile( IN HANDLE Handle, IN ULONG64 Offset, OUT PVOID Buffer, IN ULONG Length, OUT PULONG Completed ) { DBGKD_FILE_IO Irp; ULONG Index; ULONG _Completed = 0; if( !BdDebuggerEnabled ) { return STATUS_DEBUGGER_INACTIVE; } Index = HandleToUlong(Handle) - 1; if( (Index >= KD_MAX_REMOTE_FILES) || (BdRemoteFiles[Index].RemoteHandle == 0) ) { DbgPrint( "BdReadRemoteFile: Bad parameters!\n" ); Irp.Status = STATUS_INVALID_PARAMETER; goto Exit; } Irp.ApiNumber = DbgKdReadFileApi; Irp.Status = STATUS_SUCCESS; Irp.u.ReadFile.Handle = BdRemoteFiles[Index].RemoteHandle; Irp.u.ReadFile.Offset = Offset; while (Length > 0) { STRING MessageData; STRING MessageHeader; ULONG ReturnCode; ULONG RecvLength; if (Length > PACKET_MAX_SIZE - sizeof(Irp)) { Irp.u.ReadFile.Length = PACKET_MAX_SIZE - sizeof(Irp); } else { Irp.u.ReadFile.Length = Length; } MessageHeader.Length = sizeof(Irp); MessageHeader.MaximumLength = sizeof(Irp); MessageHeader.Buffer = (PCHAR)&Irp; // // Send packet to the kernel debugger on the host machine. // BdSendPacket(PACKET_TYPE_KD_FILE_IO, &MessageHeader, NULL); // // Receive packet from the kernel debugger on the host machine. // MessageData.MaximumLength = (USHORT)Irp.u.ReadFile.Length; MessageData.Buffer = Buffer; do { ReturnCode = BdReceivePacket(PACKET_TYPE_KD_FILE_IO, &MessageHeader, &MessageData, &RecvLength); } while (ReturnCode == BD_PACKET_TIMEOUT); if (ReturnCode == BD_PACKET_RECEIVED) { if (!NT_SUCCESS(Irp.Status)) { break; } _Completed += RecvLength; Buffer = (PVOID)((PUCHAR)Buffer + RecvLength); Irp.u.ReadFile.Offset += RecvLength; Length -= RecvLength; } } *Completed = _Completed; Exit: return Irp.Status; } ARC_STATUS BdCloseRemoteFile( IN HANDLE Handle ) { DBGKD_FILE_IO Irp; ULONG Index; if( !BdDebuggerEnabled ) { return STATUS_DEBUGGER_INACTIVE; } Index = HandleToUlong(Handle) - 1; if (Index >= KD_MAX_REMOTE_FILES) { return STATUS_INVALID_PARAMETER; } if (BdRemoteFiles[Index].RemoteHandle == 0) { Irp.Status = STATUS_INVALID_PARAMETER; goto Exit; } Irp.ApiNumber = DbgKdCloseFileApi; Irp.u.CloseFile.Handle = BdRemoteFiles[Index].RemoteHandle; for (;;) { STRING MessageData; STRING MessageHeader; ULONG ReturnCode; ULONG RecvLength; MessageHeader.Length = sizeof(Irp); MessageHeader.MaximumLength = sizeof(Irp); MessageHeader.Buffer = (PCHAR)&Irp; // // Send packet to the kernel debugger on the host machine. // BdSendPacket(PACKET_TYPE_KD_FILE_IO, &MessageHeader, NULL); // // Receive packet from the kernel debugger on the host machine. // MessageData.MaximumLength = BD_MESSAGE_BUFFER_SIZE; MessageData.Buffer = (PCHAR)BdMessageBuffer; do { ReturnCode = BdReceivePacket(PACKET_TYPE_KD_FILE_IO, &MessageHeader, &MessageData, &RecvLength); } while (ReturnCode == BD_PACKET_TIMEOUT); if (ReturnCode == BD_PACKET_RECEIVED) { break; } } if (NT_SUCCESS(Irp.Status)) { BdRemoteFiles[Index].RemoteHandle = 0; } Exit: return Irp.Status; } ARC_STATUS BdPullRemoteFile( IN PCHAR FileName, IN ULONG FileAttributes, IN ULONG CreateDisposition, IN ULONG CreateOptions, IN ULONG FileId ) { ARC_STATUS Status = ESUCCESS; PUCHAR BaseFilePointer = NULL; PUCHAR WorkingMemoryPointer = NULL; ULONG64 Length = 0; HANDLE RemoteHandle = NULL; ULONG64 Offset = 0; ULONG basePage = 0; PBL_FILE_TABLE fileTableEntry = NULL; if( !BdDebuggerEnabled ) { return STATUS_DEBUGGER_INACTIVE; } // Open the remote file for reading. Status = BdCreateRemoteFile(&RemoteHandle, &Length, FileName, FILE_GENERIC_READ, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, 0); if (!NT_SUCCESS(Status)) { // // File probably doesn't exist on the debugger. // goto Exit; } // // Allocate memory for the file, then download it. // Status = BlAllocateAlignedDescriptor( LoaderFirmwareTemporary, 0, (ULONG)((Length + PAGE_SIZE - 1) >> PAGE_SHIFT), 0, &basePage ); if ( Status != ESUCCESS ) { DbgPrint( "BdPullRemoteFile: BlAllocateAlignedDescriptor failed! (%x)\n", Status ); goto Exit; } // // Keep track of our pointers. // BaseFilePointer will point to the starting address of the block // we're about to download the file into. // // Working MemoryPointer will move through memory as we download small // chunks of the file. // BaseFilePointer = (PUCHAR)ULongToPtr( (basePage << PAGE_SHIFT) ); WorkingMemoryPointer = BaseFilePointer; // // Download the file. // Offset = 0; while( Offset < Length ) { ULONG ReqLength, ReqCompleted; if((Length - Offset) > TRANSFER_LENGTH) { ReqLength = TRANSFER_LENGTH; } else { ReqLength = (ULONG)(Length - Offset); } Status = BdReadRemoteFile( RemoteHandle, Offset, WorkingMemoryPointer, ReqLength, &ReqCompleted ); if (!NT_SUCCESS(Status) || ReqCompleted == 0) { DbgPrint( "BdPullRemoteFile: BdReadRemoteFile failed! (%x)\n", Status ); goto Exit; } // Increment our working pointer so we copy the next chunk // into the next spot in memory. WorkingMemoryPointer += ReqLength; Offset += ReqLength; } // // We got the file, so setup the BL_FILE_TABLE // entry for this file. We'll pretend that we got this file // off the network because that's pretty close, and it allows // us to conveniently record the memory block where we're about // to download this file. // { extern BL_DEVICE_ENTRY_TABLE NetDeviceEntryTable; fileTableEntry = &BlFileTable[FileId]; fileTableEntry->Flags.Open = 1; fileTableEntry->DeviceId = NET_DEVICE_ID; fileTableEntry->u.NetFileContext.FileSize = (ULONG)Length; fileTableEntry->u.NetFileContext.InMemoryCopy = BaseFilePointer; fileTableEntry->Position.QuadPart = 0; fileTableEntry->Flags.Read = 1; fileTableEntry->DeviceEntryTable = &NetDeviceEntryTable; RtlZeroMemory( fileTableEntry->StructureContext, sizeof(NET_STRUCTURE_CONTEXT) ); // // If we've called NetIntialize before (like if we're really booting from // the net, or if we've come through here before), then he returns immediately // so the call isn't expensive. // // If we're not booting from the net, and we've never called NetInitialize before, // then this will do nothing but setup his function table and return quickly. // NetInitialize(); } DbgPrint( "BD: Loaded remote file %s\n", FileName ); Exit: if (RemoteHandle != NULL) { BdCloseRemoteFile(RemoteHandle); } return Status; }