|
|
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
netboot.c
Abstract:
This module implements the net boot file system used by the operating system loader.
Author:
Chuck Lenzmeier (chuckl) 09-Jan-1997
Revision History:
--*/
#include "bootlib.h"
#include "stdio.h"
#ifdef UINT16
#undef UINT16
#endif
#ifdef INT16
#undef INT16
#endif
#include <dhcp.h>
#include <netfs.h>
#include <pxe_cmn.h>
#include <pxe_api.h>
#include <udp_api.h>
#include <tftp_api.h>
#if defined(_IA64_)
#include "bootia64.h"
#else
#include "bootx86.h"
#endif
#ifndef BOOL
typedef int BOOL; #endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef BYTE
typedef unsigned char BYTE; #endif
#ifndef LPBYTE
typedef BYTE *LPBYTE; #endif
#define MAX_PATH 260
//
// Define global data.
//
BOOLEAN BlBootingFromNet = FALSE;
BOOLEAN NetBootInitialized = FALSE;
PARC_OPEN_ROUTINE NetRealArcOpenRoutine; PARC_CLOSE_ROUTINE NetRealArcCloseRoutine;
BL_DEVICE_ENTRY_TABLE NetDeviceEntryTable;
BOOTFS_INFO NetBootFsInfo={L"net"};
#if defined(REMOTE_BOOT_SECURITY)
ULONG TftpSecurityHandle = 0; #endif // defined(REMOTE_BOOT_SECURITY)
BOOLEAN NetBootTftpUsedPassword2;
//
// We cache the last file opened, in case we get a request to open it again.
// We don't save a copy of the data, just a pointer to the data read by that
// open. So if the original open is closed before the next open for the
// same file comes in, we won't get a cache hit. But this system works for
// reading compressed files, which is the situation we care about. In that
// case a file is opened once and then re-opened twice more before the
// original open is closed.
//
ULONG CachedFileDeviceId = 0; UCHAR CachedFilePath[MAX_PATH]; ULONG CachedFileSize = 0; PUCHAR CachedFileData = NULL;
extern ARC_STATUS GetParametersFromRom ( VOID );
PBL_DEVICE_ENTRY_TABLE IsNetFileStructure ( IN ULONG DeviceId, IN PVOID StructureContext )
/*++
Routine Description:
This routine determines if the partition on the specified channel contains a net file system volume.
Arguments:
DeviceId - Supplies the file table index for the device on which read operations are to be performed.
StructureContext - Supplies a pointer to a net file structure context.
Return Value:
A pointer to the net entry table is returned if the partition is recognized as containing a net volume. Otherwise, NULL is returned.
--*/
{ PNET_STRUCTURE_CONTEXT NetStructureContext;
DPRINT( TRACE, ("IsNetFileStructure\n") );
if ( !BlBootingFromNet || (DeviceId != NET_DEVICE_ID) ) { return NULL; }
//
// Clear the file system context block for the specified channel and
// establish a pointer to the context structure that can be used by other
// routines
//
NetStructureContext = (PNET_STRUCTURE_CONTEXT)StructureContext; RtlZeroMemory(NetStructureContext, sizeof(NET_STRUCTURE_CONTEXT));
//
// Return the address of the table.
//
return &NetDeviceEntryTable;
} // IsNetFileStructure
ARC_STATUS NetInitialize ( VOID )
/*++
Routine Description:
This routine initializes the net boot filesystem.
Arguments:
None.
Return Value:
ESUCCESS.
--*/
{ NTSTATUS status;
DPRINT( TRACE, ("NetInitialize\n") ); //DbgBreakPoint( );
if( NetBootInitialized ) { return ESUCCESS; }
//
// Initialize the file entry table. Note that we need to do
// this even if we aren't booting from the net because we may
// use the 'Net' I/O functions to lay on top of any files that
// we download through the debugger port. So for that case,
// we need access to all these functions here (see bd\file.c)
//
NetDeviceEntryTable.Close = NetClose; NetDeviceEntryTable.Mount = NetMount; NetDeviceEntryTable.Open = NetOpen; NetDeviceEntryTable.Read = NetRead; NetDeviceEntryTable.GetReadStatus = NetGetReadStatus; NetDeviceEntryTable.Seek = NetSeek; NetDeviceEntryTable.Write = NetWrite; NetDeviceEntryTable.GetFileInformation = NetGetFileInformation; NetDeviceEntryTable.SetFileInformation = NetSetFileInformation; NetDeviceEntryTable.Rename = NetRename; NetDeviceEntryTable.GetDirectoryEntry = NetGetDirectoryEntry; NetDeviceEntryTable.BootFsInfo = &NetBootFsInfo;
if( !BlBootingFromNet ) { return ESUCCESS; }
NetBootInitialized = TRUE;
DPRINT( LOUD, ("NetInitialize: booting from net\n") ); //DPRINT( LOUD, (" NetInitialize at %08x\n", NetInitialize) );
//DPRINT( LOUD, (" NetOpen at %08x\n", NetOpen) );
//DbgBreakPoint( );
//
// Hook the ArcOpen and ArcClose routines.
//
NetRealArcOpenRoutine = SYSTEM_BLOCK->FirmwareVector[OpenRoutine]; SYSTEM_BLOCK->FirmwareVector[OpenRoutine] = NetArcOpen;
NetRealArcCloseRoutine = SYSTEM_BLOCK->FirmwareVector[CloseRoutine]; SYSTEM_BLOCK->FirmwareVector[CloseRoutine] = NetArcClose;
//
// Get boot parameters from the boot ROM.
//
status = GetParametersFromRom( );
if ( status != ESUCCESS ) { return status; }
return ESUCCESS; }
VOID NetTerminate ( VOID )
/*++
Routine Description:
This routine shuts down the net boot filesystem.
Arguments:
None.
Return Value:
ESUCCESS.
--*/
{
#if defined(_X86_)
#if defined(REMOTE_BOOT_SECURITY)
if ( TftpSecurityHandle != 0 ) { TftpLogoff(NetServerIpAddress, TftpSecurityHandle); TftpSecurityHandle = 0; } #endif // defined(REMOTE_BOOT_SECURITY)
//
// let's not set the receive status if the card isn't active.
//
RomSetReceiveStatus( 0 ); #endif // defined(_X86_)
#ifdef EFI
extern VOID EfiNetTerminate(); EfiNetTerminate(); #endif
return;
} // NetTerminate
ARC_STATUS NetArcClose ( IN ULONG FileId ) { DPRINT( TRACE, ("NetArcClose\n") );
if ( FileId != NET_DEVICE_ID ) { return NetRealArcCloseRoutine( FileId ); }
return ESUCCESS;
} // NetArcClose
ARC_STATUS NetArcOpen ( IN CHAR * FIRMWARE_PTR OpenPath, IN OPEN_MODE OpenMode, OUT ULONG * FIRMWARE_PTR FileId ) { DPRINT( TRACE, ("NetArcOpen\n") );
if ( _strnicmp(OpenPath, "net(", 4) != 0 ) { return NetRealArcOpenRoutine( OpenPath, OpenMode, FileId ); }
*FileId = NET_DEVICE_ID;
return ESUCCESS;
} // NetArcOpen
ARC_STATUS NetClose ( IN ULONG FileId ) { PBL_FILE_TABLE fileTableEntry; DPRINT( TRACE, ("NetClose FileId = %d\n", FileId) );
fileTableEntry = &BlFileTable[FileId];
{ DPRINT( REAL_LOUD, ("NetClose: id %d, freeing memory at 0x%08x, %d bytes\n", FileId, fileTableEntry->u.NetFileContext.InMemoryCopy, fileTableEntry->u.NetFileContext.FileSize) ); BlFreeDescriptor( (ULONG)((ULONG_PTR)fileTableEntry->u.NetFileContext.InMemoryCopy >> PAGE_SHIFT ));
//
// If the data read for this specific open was what was cached,
// then mark the cache empty.
//
if (fileTableEntry->u.NetFileContext.InMemoryCopy == CachedFileData) { CachedFileData = NULL; CachedFilePath[0] = '\0'; } }
fileTableEntry->Flags.Open = 0;
return EROFS;
} // NetClose
ARC_STATUS NetMount ( IN CHAR * FIRMWARE_PTR MountPath, IN MOUNT_OPERATION Operation ) { DPRINT( TRACE, ("NetMount\n") );
return EROFS;
} // NetMount
ARC_STATUS NetOpen ( IN CHAR * FIRMWARE_PTR OpenPath, IN OPEN_MODE OpenMode, OUT ULONG * FIRMWARE_PTR FileId ) { NTSTATUS ntStatus; ARC_STATUS arcStatus; // holds temp values, not the function return value
PBL_FILE_TABLE fileTableEntry; TFTP_REQUEST request; ULONG oldBase; ULONG oldLimit; PCHAR p; #if defined(REMOTE_BOOT_SECURITY)
static BOOLEAN NetBootTryTftpSecurity = FALSE; #endif // defined(REMOTE_BOOT_SECURITY)
DPRINT( TRACE, ("NetOpen FileId = %d\n", *FileId) );
DPRINT( LOUD, ("NetOpen: opening %s, id %d, mode %d\n", OpenPath, *FileId, OpenMode) ); fileTableEntry = &BlFileTable[*FileId];
if ( OpenMode != ArcOpenReadOnly ) { DPRINT( LOUD, ("NetOpen: invalid OpenMode\n") ); return EROFS; }
fileTableEntry->Flags.Open = 1; // Prevent GetCSCFileNameFromUNCPath using our entry
#if defined(REMOTE_BOOT_SECURITY)
//
// Login if we don't have a valid handle, using the on-disk secret.
//
if ((TftpSecurityHandle == 0) && NetBootTryTftpSecurity) {
ULONG FileId; RI_SECRET Secret; UCHAR Domain[RI_SECRET_DOMAIN_SIZE + 1]; UCHAR User[RI_SECRET_USER_SIZE + 1]; struct { UCHAR Owf[LM_OWF_PASSWORD_SIZE+NT_OWF_PASSWORD_SIZE]; } Passwords[2]; UCHAR Sid[RI_SECRET_SID_SIZE];
arcStatus = BlOpenRawDisk(&FileId);
if (arcStatus == ESUCCESS) {
arcStatus = BlReadSecret(FileId, &Secret); if (arcStatus == ESUCCESS) { BlParseSecret( Domain, User, Passwords[0].Owf, Passwords[0].Owf + LM_OWF_PASSWORD_SIZE, Passwords[1].Owf, Passwords[1].Owf + LM_OWF_PASSWORD_SIZE, Sid, &Secret); DPRINT(LOUD, ("Logging on to <%s><%s>\n", Domain, User));
//
// Try logging on with the first password, if that fails
// then try the second.
//
ntStatus = TftpLogin( Domain, User, Passwords[0].Owf, NetServerIpAddress, &TftpSecurityHandle); if (!NT_SUCCESS(ntStatus)) { DPRINT(LOUD, ("TftpLogin using password 2\n")); ntStatus = TftpLogin( Domain, User, Passwords[1].Owf, NetServerIpAddress, &TftpSecurityHandle); if (NT_SUCCESS(ntStatus)) { NetBootTftpUsedPassword2 = TRUE; } }
} else {
ntStatus = STATUS_OBJECT_PATH_NOT_FOUND; }
arcStatus = BlCloseRawDisk(FileId);
//
// We are inside the if() for successfully opening the raw
// disk, so we are not diskless. On these machines we must
// fail the open at this point.
//
if (!NT_SUCCESS(ntStatus)) { DPRINT( ERROR, ("TftpLogin failed %lx\n", ntStatus) ); return EACCES; }
} else {
NetBootTryTftpSecurity = FALSE; // so we don't try to open it again
}
} #endif // defined(REMOTE_BOOT_SECURITY)
DPRINT( LOUD, ("NetOpen: opening %s\n", OpenPath) );
oldBase = BlUsableBase; oldLimit = BlUsableLimit; BlUsableBase = BL_DRIVER_RANGE_LOW; BlUsableLimit = BL_DRIVER_RANGE_HIGH;
//
// If this request matches the cached file, then just copy that data.
//
if ((fileTableEntry->DeviceId == CachedFileDeviceId) && (strcmp(OpenPath, CachedFilePath) == 0) && (CachedFileData != NULL)) {
ULONG basePage;
arcStatus = BlAllocateAlignedDescriptor( LoaderFirmwareTemporary, 0, (CachedFileSize + PAGE_SIZE - 1) >> PAGE_SHIFT, 0, &basePage );
BlUsableBase = oldBase; BlUsableLimit = oldLimit;
if ( arcStatus != ESUCCESS ) { fileTableEntry->Flags.Open = 0; // Free entry we didn't use
return EROFS; }
DPRINT( REAL_LOUD, ("NetOpen: Using cache for file %s\n", CachedFilePath) );
fileTableEntry->u.NetFileContext.InMemoryCopy = (PUCHAR)ULongToPtr( (basePage << PAGE_SHIFT) ); memcpy(fileTableEntry->u.NetFileContext.InMemoryCopy, CachedFileData, CachedFileSize); fileTableEntry->u.NetFileContext.FileSize = CachedFileSize;
} else {
request.RemoteFileName = OpenPath; request.ServerIpAddress = NetServerIpAddress; request.MemoryAddress = NULL; request.MaximumLength = 0; request.BytesTransferred = 0xbadf00d; request.Operation = TFTP_RRQ; request.MemoryType = LoaderFirmwareTemporary; #if defined(REMOTE_BOOT_SECURITY)
request.SecurityHandle = TftpSecurityHandle; #endif // defined(REMOTE_BOOT_SECURITY)
request.ShowProgress = FALSE;
ntStatus = TftpGetPut( &request ); DPRINT( REAL_LOUD, ("NetOpen: TftpGetPut(get) status: %x, bytes: %x\n", ntStatus, request.BytesTransferred) );
BlUsableBase = oldBase; BlUsableLimit = oldLimit;
if ( !NT_SUCCESS(ntStatus) ) { if ( request.MemoryAddress != NULL ) { DPRINT( REAL_LOUD, ("NetOpen: freeing memory at 0x%08x, %d bytes\n", request.MemoryAddress, request.MaximumLength) ); BlFreeDescriptor( (ULONG)((ULONG_PTR)request.MemoryAddress >> PAGE_SHIFT )); } fileTableEntry->Flags.Open = 0; // Free entry we didn't use
if ( ntStatus == STATUS_INSUFFICIENT_RESOURCES ) { return ENOMEM; } return EROFS; }
fileTableEntry->u.NetFileContext.FileSize = request.BytesTransferred; fileTableEntry->u.NetFileContext.InMemoryCopy = request.MemoryAddress;
//
// We always cache the last file that was actually read from
// the network.
//
strcpy(CachedFilePath, OpenPath); CachedFileDeviceId = fileTableEntry->DeviceId; CachedFileSize = request.BytesTransferred; CachedFileData = request.MemoryAddress;
}
fileTableEntry->Position.QuadPart = 0;
fileTableEntry->Flags.Read = 1;
return ESUCCESS;
} // NetOpen
ARC_STATUS NetRead ( IN ULONG FileId, OUT VOID * FIRMWARE_PTR Buffer, IN ULONG Length, OUT ULONG * FIRMWARE_PTR Count ) { PBL_FILE_TABLE fileTableEntry; PNET_FILE_CONTEXT context; PUCHAR source;
fileTableEntry = &BlFileTable[FileId]; context = &fileTableEntry->u.NetFileContext;
{ source = context->InMemoryCopy + fileTableEntry->Position.LowPart; if ( (fileTableEntry->Position.LowPart + Length) > context->FileSize ) { Length = context->FileSize - fileTableEntry->Position.LowPart; }
RtlCopyMemory( Buffer, source, Length ); *Count = Length;
fileTableEntry->Position.LowPart += Length; }
DPRINT( REAL_LOUD, ("NetRead: id %d, length %d, count %d, new pos %x\n", FileId, Length, *Count, fileTableEntry->Position.LowPart) );
return ESUCCESS;
} // NetRead
ARC_STATUS NetGetReadStatus ( IN ULONG FileId ) { DPRINT( TRACE, ("NetGetReadStatus\n") );
return ESUCCESS;
} // NetGetReadStatus
ARC_STATUS NetSeek ( IN ULONG FileId, IN LARGE_INTEGER * FIRMWARE_PTR Offset, IN SEEK_MODE SeekMode ) { PBL_FILE_TABLE fileTableEntry; LARGE_INTEGER newPosition;
//DPRINT( TRACE, ("NetSeek\n") );
fileTableEntry = &BlFileTable[FileId];
{ if ( SeekMode == SeekAbsolute ) { newPosition = *Offset; } else if ( SeekMode == SeekRelative ) { newPosition.QuadPart = fileTableEntry->Position.QuadPart + Offset->QuadPart; } else { return EROFS; }
DPRINT( REAL_LOUD, ("NetSeek: id %d, mode %d, offset %x, new pos %x\n", FileId, SeekMode, Offset->LowPart, newPosition.LowPart) );
if ( newPosition.QuadPart > fileTableEntry->u.NetFileContext.FileSize ) { return EROFS; }
fileTableEntry->Position = newPosition; }
return ESUCCESS;
} // NetSeek
ARC_STATUS NetWrite ( IN ULONG FileId, IN VOID * FIRMWARE_PTR Buffer, IN ULONG Length, OUT ULONG * FIRMWARE_PTR Count ) { DPRINT( TRACE, ("NetWrite\n") );
return EROFS;
} // NetWrite
ARC_STATUS NetGetFileInformation ( IN ULONG FileId, OUT FILE_INFORMATION * FIRMWARE_PTR Buffer ) { PBL_FILE_TABLE fileTableEntry; //DPRINT( TRACE, ("NetGetFileInformation\n") );
fileTableEntry = &BlFileTable[FileId];
{ Buffer->EndingAddress.QuadPart = fileTableEntry->u.NetFileContext.FileSize; Buffer->CurrentPosition.QuadPart = fileTableEntry->Position.QuadPart; DPRINT( REAL_LOUD, ("NetGetFileInformation returning size %x, position %x\n", Buffer->EndingAddress.LowPart, Buffer->CurrentPosition.LowPart) );
return ESUCCESS; }
} // NetGetFileInformation
ARC_STATUS NetSetFileInformation ( IN ULONG FileId, IN ULONG AttributeFlags, IN ULONG AttributeMask ) { DPRINT( TRACE, ("NetSetFileInformation\n") );
return EROFS;
} // NetSetFileInformation
ARC_STATUS NetRename ( IN ULONG FileId, IN CHAR * FIRMWARE_PTR NewFileName ) { DPRINT( TRACE, ("NetRename\n") );
return EROFS;
} // NetRename
ARC_STATUS NetGetDirectoryEntry ( IN ULONG FileId, IN DIRECTORY_ENTRY * FIRMWARE_PTR DirEntry, IN ULONG NumberDir, OUT ULONG * FIRMWARE_PTR CountDir ) { DPRINT( TRACE, ("NetGetDirectoryEntry\n") );
return EROFS;
} // NetGetDirectoryEntry
|