windows-nt/Source/XPSP1/NT/base/boot/lib/netboot.c
2020-09-26 16:20:57 +08:00

766 lines
17 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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