Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3501 lines
102 KiB

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
ramdisk.c
Abstract:
Provides the ARC emulation routines for I/O to a RAM disk device.
Author:
Chuck Lenzmeier (chuckl) 29-Apr-2001
Revision History:
Bassam Tabbara (bassamt) 06-Aug-2001 Added Ramdisk Building Support
--*/
#include "bootlib.h"
#include "arccodes.h"
#include "stdlib.h"
#include "string.h"
#if defined(_X86_)
#include "bootx86.h"
#endif
#if defined(_IA64_)
#include "bootia64.h"
#endif
#include "ramdisk.h"
#include "netfs.h"
#include "bmbuild.h"
#include "ntexapi.h"
#include "haldtect.h"
#include "pci.h"
#include "pbios.h"
#include "bldr.h"
#include <sdistructs.h>
//
// Debug helpers
//
#define ERR 0
#define INFO 1
#define VERBOSE 2
#define PAINFUL 3
#define DBGPRINT(lvl, _fmt_) if (RamdiskDebug && lvl <= RamdiskDebugLevel) DbgPrint _fmt_
#define DBGLVL(x) (RamdiskDebug && RamdiskDebugLevel == x)
BOOLEAN RamdiskDebug = TRUE;
BOOLEAN RamdiskDebugLevel = INFO;
BOOLEAN RamdiskBreak = FALSE;
//
// Macros
//
#define BL_INVALID_FILE_ID (ULONG)-1
#define TEST_BIT(value, b) (((value) & (b)) == (b))
#define ROUND2(_val, _round) (((_val) + ((_round) - 1)) & ~((_round) - 1))
//
// PCI Device struct as persisted in registry by ntdetect.com
//
#include <pshpack1.h>
typedef struct _PCIDEVICE {
USHORT BusDevFunc;
PCI_COMMON_CONFIG Config;
} PCIDEVICE, *PPCIDEVICE;
#include <poppack.h>
//
// Externs
//
extern PVOID InfFile;
extern BOOLEAN GraphicsMode;
extern BOOLEAN BlShowProgressBar;
extern BOOLEAN BlOutputDots;
extern BOOLEAN DisplayLogoOnBoot;
//
// Global Ramdisk options.
// NOTE: All Ip addresses and ports are in network byte order.
//
BOOLEAN RamdiskBuild = FALSE;
//
// Used if downloading a ramdisk directly. RamdiskBuild = FALSE
//
PCHAR RamdiskPath = NULL;
ULONG RamdiskTFTPAddr = 0; // network byte order
ULONG RamdiskMTFTPAddr = 0; // network byte order
USHORT RamdiskMTFTPCPort = 0; // network byte order
USHORT RamdiskMTFTPSPort = 0; // network byte order
USHORT RamdiskMTFTPTimeout = 5;
USHORT RamdiskMTFTPDelay = 5;
LONGLONG RamdiskMTFTPFileSize = 0;
LONGLONG RamdiskMTFTPChunkSize = 0;
//
// Used if Building a ramdisk. RamdiskBuild = TRUE
//
#define RAMDISK_MAX_SERVERS 10
#define RAMDISK_MAX_TIMEOUT 60
#define RAMDISK_UI_WAIT 3
GUID RamdiskGuid = {0,0,0,0};
ULONG RamdiskDiscovery = 0xFFFFFFFF;
ULONG RamdiskMCastAddr = 0; // network byte order
ULONG RamdiskServerCount = 0;
ULONG RamdiskServers[RAMDISK_MAX_SERVERS]; // network byte order
USHORT RamdiskBuildPort = BMBUILD_SERVER_PORT_DEFAULT;
USHORT RamdiskTimeout = 4;
USHORT RamdiskRetry = 5;
//
// Globals
//
BOOLEAN RamdiskActive = FALSE;
ULONG RamdiskBasePage = 0;
LONGLONG RamdiskFileSize = 0;
ULONG RamdiskFileSizeInPages = 0;
ULONG RamdiskImageOffset = 0;
LONGLONG RamdiskImageLength = 0;
ULONG_PTR SdiAddress = 0;
ULONG RamdiskMaxPacketSize = 0;
ULONG RamdiskXID = 0;
BL_DEVICE_ENTRY_TABLE RamdiskEntryTable =
{
(PARC_CLOSE_ROUTINE)RamdiskClose,
(PARC_MOUNT_ROUTINE)RamdiskMount,
(PARC_OPEN_ROUTINE)RamdiskOpen,
(PARC_READ_ROUTINE)RamdiskRead,
(PARC_READ_STATUS_ROUTINE)RamdiskReadStatus,
(PARC_SEEK_ROUTINE)RamdiskSeek,
(PARC_WRITE_ROUTINE)RamdiskWrite,
(PARC_GET_FILE_INFO_ROUTINE)RamdiskGetFileInfo,
(PARC_SET_FILE_INFO_ROUTINE)RamdiskSetFileInfo,
(PRENAME_ROUTINE)RamdiskRename,
(PARC_GET_DIRECTORY_ENTRY_ROUTINE)RamdiskGetDirectoryEntry,
(PBOOTFS_INFO)NULL
};
//
// forward decls
//
PVOID
MapRamdisk (
IN LONGLONG Offset,
OUT PLONGLONG AvailableLength
);
ARC_STATUS
RamdiskParseOptions (
IN PCHAR LoadOptions
);
ARC_STATUS
RamdiskInitializeFromPath(
);
ARC_STATUS
RamdiskBuildAndInitialize(
);
VOID
RamdiskFatalError(
IN ULONG Message1,
IN ULONG Message2
);
ARC_STATUS
RamdiskInitialize(
IN PCHAR LoadOptions,
IN BOOLEAN SdiBoot
)
/*++
Routine Description:
This function will initiate the boot from a RAMDISK. Depending
on the options passed in the the boot will either happen from
a static RAMDISK (using the /RDPATH option) or from a dynamic
RAMDISK (using the /RDBUILD option).
Arguments:
LoadOptions - boot.ini parameters
SdiBoot - indicates whether this is an SDI boot. If it is, LoadOptions
is ignored. The global variable SdiAddress gives the pointer to
the SDI image.
Return Value:
none
--*/
{
ARC_STATUS status;
BOOLEAN OldOutputDots = FALSE;
BOOLEAN OldShowProgressBar = FALSE;
ULONG oldBase;
ULONG oldLimit;
//
// Debug Break on entry
//
if (RamdiskBreak) {
DbgBreakPoint();
}
//
// If the ramdisk has already been initialized, just return. We know the
// ramdisk has been initialized if SdiBoot is FALSE (implying that this is
// NOT the call from BlStartup(), but the call from BlOsLoader()) and
// RamdiskBasePage is not NULL (implying that we were previously called
// from BlStartup() to initialize the SDI boot.
//
if ( !SdiBoot && (RamdiskBasePage != 0) ) {
//
// Now that ntdetect has been run, we can free up the pages that
// we allocated earlier (see below).
//
BlFreeDescriptor( 0x10 );
return ESUCCESS;
}
//
// If this is an SDI boot, then we must have a pointer to the SDI image.
//
if ( SdiBoot && (SdiAddress == 0) ) {
RamdiskFatalError( RAMDISK_GENERAL_FAILURE,
RAMDISK_INVALID_OPTIONS );
return EINVAL;
}
//
// If this is not an SDI boot, parse all ramdisk options (if any).
//
if ( !SdiBoot ) {
status = RamdiskParseOptions ( LoadOptions );
if (status != ESUCCESS) {
RamdiskFatalError( RAMDISK_GENERAL_FAILURE,
RAMDISK_INVALID_OPTIONS );
return status;
}
}
#if defined(_IA64_)
// Ramdisk boot path not supported on IA64 as of yet
if ( RamdiskBuild ) {
return ESUCCESS;
}
#endif
//
// Show the progress bar in text mode
//
if ( RamdiskBuild || RamdiskPath ) {
// If booting from a ramdisk, graphics mode is off permanently
DisplayLogoOnBoot = FALSE;
GraphicsMode = FALSE;
OldShowProgressBar = BlShowProgressBar;
BlShowProgressBar = TRUE;
OldOutputDots = BlOutputDots;
BlOutputDots = TRUE;
}
#if defined(i386)
if ( RamdiskBuild ) {
//
// We will need to build the ramdisk first
//
ASSERT( RamdiskPath == NULL );
status = RamdiskBuildAndInitialize();
if (status != ESUCCESS) {
RamdiskFatalError( RAMDISK_GENERAL_FAILURE,
RAMDISK_BUILD_FAILURE );
return status;
}
}
#endif
if ( RamdiskPath ) {
//
// Initialize the Ramdisk from the RamdiskPath
//
status = RamdiskInitializeFromPath();
if (status != ESUCCESS) {
RamdiskFatalError( RAMDISK_GENERAL_FAILURE,
RAMDISK_BOOT_FAILURE );
return status;
}
} else if ( SdiBoot ) {
//
// This is an SDI boot. Find the ramdisk image within the SDI image
// and allocate the pages in which the ramdisk image resides.
//
ULONG basePage;
ULONG pageCount;
PSDI_HEADER sdiHeader;
ULONG i;
ULONG_PTR ramdiskAddress;
//
// Temporarily allocate the pages that will be occupied by ntdetect
// while it runs. BlDetectHardware() just assumes that these pages
// are free for loading ntdetect. But we're going to allocate and map
// the ramdisk image, which will result in the allocation of many
// page table pages, some of which might end up in the place where
// ntdetect will be loaded. So we allocate the ntdetect range here,
// then free it later (see above).
//
basePage = 0x10;
pageCount = 0x10;
status = BlAllocateAlignedDescriptor(
LoaderFirmwareTemporary,
basePage,
pageCount,
0,
&basePage
);
//
// Allocate the page that contains the SDI header. This will cause
// it to be mapped, which will allow us to read the header to find
// the ramdisk image.
//
oldBase = BlUsableBase;
oldLimit = BlUsableLimit;
BlUsableBase = BL_XIPROM_RANGE_LOW;
BlUsableLimit = BL_XIPROM_RANGE_HIGH;
basePage = (ULONG)(SdiAddress >> PAGE_SHIFT);
pageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES( SdiAddress, sizeof(SDI_HEADER) );
status = BlAllocateAlignedDescriptor(
LoaderFirmwareTemporary,
basePage,
pageCount,
0,
&basePage
);
BlUsableBase = oldBase;
BlUsableLimit = oldLimit;
//
// Find the ramdisk image by looking through the TOC in the SDI header.
//
sdiHeader = (PSDI_HEADER)SdiAddress;
for ( i = 0; i < SDI_TOCMAXENTRIES; i++ ) {
if ( sdiHeader->ToC[i].dwType == SDI_BLOBTYPE_PART ) {
break;
}
}
if ( i >= SDI_TOCMAXENTRIES ) {
RamdiskFatalError( RAMDISK_GENERAL_FAILURE,
RAMDISK_BOOT_FAILURE );
return ENOENT;
}
//
// Calculate the starting address and page of the ramdisk image, the
// length of the ramdisk image, and the offset within the starting page
// to the image. The offset should be 0, because everything in the SDI
// image should be page-aligned.
//
ramdiskAddress = (ULONG_PTR)(SdiAddress + sdiHeader->ToC[i].llOffset.QuadPart);
RamdiskBasePage = (ULONG)(ramdiskAddress >> PAGE_SHIFT);
RamdiskImageOffset = (ULONG)(ramdiskAddress - ((ULONG_PTR)RamdiskBasePage << PAGE_SHIFT));
RamdiskImageLength = sdiHeader->ToC[i].llSize.QuadPart;
RamdiskFileSizeInPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
ramdiskAddress,
RamdiskImageLength
);
RamdiskFileSize = (LONGLONG)RamdiskFileSizeInPages << PAGE_SHIFT;
//
// Release the page(s) occupied by the SDI header.
//
BlFreeDescriptor( basePage );
//
// Tell the memory allocator about the pages occupied by the ramdisk
// by allocating those pages.
//
oldBase = BlUsableBase;
oldLimit = BlUsableLimit;
BlUsableBase = BL_XIPROM_RANGE_LOW;
BlUsableLimit = BL_XIPROM_RANGE_HIGH;
basePage = RamdiskBasePage;
pageCount = RamdiskFileSizeInPages;
status = BlAllocateAlignedDescriptor(
LoaderXIPRom,
basePage,
pageCount,
0,
&basePage
);
BlUsableBase = oldBase;
BlUsableLimit = oldLimit;
ASSERT( status == ESUCCESS );
ASSERT( basePage == RamdiskBasePage );
DBGPRINT(VERBOSE, ("Ramdisk is active\n") );
RamdiskActive = TRUE;
}
//
// Restore old progress bar settings
//
if ( RamdiskBuild || RamdiskPath ) {
BlShowProgressBar = OldShowProgressBar;
BlOutputDots = OldOutputDots;
BlClearScreen();
}
return ESUCCESS;
}
ARC_STATUS
RamdiskReadImage(
PCHAR RamdiskPath
)
/*++
Routine Description:
This function will load a ramdisk image from the network
or another ARC boot device.
Arguments:
RamdiskPath - name of the file to load
Return Value:
status
--*/
{
ARC_STATUS status;
ULONG RamdiskDeviceId;
ULONG RamdiskFileId = BL_INVALID_FILE_ID;
PCHAR p;
FILE_INFORMATION fileInformation;
LARGE_INTEGER offset;
LONGLONG remainingLength;
ULONG oldBase;
ULONG oldLimit;
BOOLEAN retry = TRUE;
ULONG lastProgressPercent = 0;
BOOLEAN ForceDisplayFirstTime = TRUE; // force display initially
ULONG currentProgressPercent;
PUCHAR ip;
PTCHAR FormatString = NULL;
TCHAR Buffer[256];
//
// Show text progress bar
//
BlOutputStartupMsg(RAMDISK_DOWNLOAD);
BlUpdateProgressBar(0);
DBGPRINT(VERBOSE, ("RamdiskReadImage(%s)\n", RamdiskPath));
//
// Open the device that the RAM disk image is on.
//
p = strchr(RamdiskPath, '\\');
if (p == NULL) {
DBGPRINT(ERR, ("no \\ found in path\n"));
return EINVAL;
}
*p = 0;
try_again:
status = ArcOpen(RamdiskPath, ArcOpenReadWrite, &RamdiskDeviceId);
if (status != ESUCCESS) {
DBGPRINT(ERR, ("ArcOpen(%s) failed: %d\n", RamdiskPath, status));
if ( retry ) {
retry = FALSE;
_strlwr(RamdiskPath);
goto try_again;
}
*p = '\\';
return status;
}
*p++ = '\\';
//
// If the RAM disk image is on the network, use TftpGetPut to read it.
// Otherwise, use normal I/O.
//
oldBase = BlUsableBase;
oldLimit = BlUsableLimit;
BlUsableBase = BL_XIPROM_RANGE_LOW;
BlUsableLimit = BL_XIPROM_RANGE_HIGH;
#ifdef EFI // multicast ramdisk download only supported on non-EFI machines for now
if ( RamdiskDeviceId == NET_DEVICE_ID && RamdiskMTFTPAddr != 0 )
{
ArcClose( RamdiskDeviceId );
return EBADF;
}
#endif
if ( RamdiskDeviceId == NET_DEVICE_ID && RamdiskMTFTPAddr == 0) {
//
// Network device using UNICAST download. We will use the TFTP
// client implementation in TFTPLIB for the download.
//
TFTP_REQUEST request;
NTSTATUS ntStatus;
request.RemoteFileName = (PUCHAR)p;
request.ServerIpAddress = RamdiskTFTPAddr;
request.MemoryAddress = NULL;
request.MaximumLength = 0;
request.BytesTransferred = 0xbadf00d;
request.Operation = TFTP_RRQ;
request.MemoryType = LoaderXIPRom;
#if defined(REMOTE_BOOT_SECURITY)
request.SecurityHandle = TftpSecurityHandle;
#endif // defined(REMOTE_BOOT_SECURITY)
request.ShowProgress = TRUE;
//
// Print progress message
//
ip = (PUCHAR) &RamdiskTFTPAddr;
FormatString = BlFindMessage( RAMDISK_DOWNLOAD_NETWORK );
if ( FormatString != NULL ) {
_stprintf(Buffer, FormatString, ip[0], ip[1], ip[2], ip[3] );
BlOutputTrailerMsgStr( Buffer );
}
//
// Download the image using TFTP
//
DBGPRINT(VERBOSE, ("calling TftpGetPut(%s,0x%x)\n", p, NetServerIpAddress));
ntStatus = TftpGetPut( &request );
DBGPRINT(VERBOSE, ("status from TftpGetPut 0x%x\n", ntStatus));
BlUsableBase = oldBase;
BlUsableLimit = oldLimit;
if ( !NT_SUCCESS(ntStatus) ) {
if ( request.MemoryAddress != NULL ) {
BlFreeDescriptor( (ULONG)((ULONG_PTR)request.MemoryAddress & ~KSEG0_BASE) >> PAGE_SHIFT);
}
ArcClose( RamdiskDeviceId );
if ( ntStatus == STATUS_INSUFFICIENT_RESOURCES ) {
return ENOMEM;
}
return EROFS;
}
RamdiskBasePage = (ULONG)((ULONG_PTR)request.MemoryAddress & ~KSEG0_BASE) >> PAGE_SHIFT;
RamdiskFileSize = request.MaximumLength;
RamdiskFileSizeInPages = (ULONG) BYTES_TO_PAGES(RamdiskFileSize);
if ( (RamdiskImageLength == 0) ||
(RamdiskImageLength > (RamdiskFileSize - RamdiskImageOffset)) ) {
RamdiskImageLength = RamdiskFileSize - RamdiskImageOffset;
}
#ifndef EFI // multicast ramdisk download only supported on non-EFI machines for now
} else if ( RamdiskDeviceId == NET_DEVICE_ID && RamdiskMTFTPAddr != 0) {
LONGLONG FileOffset = 0;
LONGLONG VirtualAddressOfOffset;
ULONG DownloadSize;
USHORT ClientPort;
USHORT ServerPort;
ULONG iSession = 0;
//
// Network device and using multicast download. For multicast
// downloads we will use the MTFTP implementation in the ROM.
// A single MTFTP transfer is limited to 16-bit block counts.
// This translates to ~32MB for 512 block sizes and ~90MB for
// 1468 block sizes. In order to support larger files, we will
// use multiple MTFTP sessions to bring the file down in chunks.
// The MTFTP server will need to understand the chunking semantics.
//
//
// Print progress message
//
ip = (PUCHAR) &RamdiskMTFTPAddr;
FormatString = BlFindMessage( RAMDISK_DOWNLOAD_NETWORK_MCAST );
if ( FormatString != NULL ) {
_stprintf(Buffer, FormatString, ip[0], ip[1], ip[2], ip[3], SWAP_WORD( RamdiskMTFTPSPort ) );
BlOutputTrailerMsgStr( Buffer );
}
//
// Allocate the memory for the entire RAMDisk
//
RamdiskFileSize = RamdiskMTFTPFileSize;
RamdiskFileSizeInPages = (ULONG)BYTES_TO_PAGES(RamdiskFileSize);
if ( (RamdiskImageLength == 0) ||
(RamdiskImageLength > (RamdiskFileSize - RamdiskImageOffset)) ) {
RamdiskImageLength = RamdiskFileSize - RamdiskImageOffset;
}
DBGPRINT(INFO, ("Downloading Ramdisk using MTFTP. File Size=0x%I64x Chunk Size=0x%I64x\n", RamdiskFileSize, RamdiskMTFTPChunkSize ));
status = BlAllocateAlignedDescriptor(
LoaderXIPRom,
0,
RamdiskFileSizeInPages,
0,
&RamdiskBasePage
);
BlUsableBase = oldBase;
BlUsableLimit = oldLimit;
if (status != ESUCCESS) {
DBGPRINT(ERR, ("BlAllocateAlignedDescriptor(%d pages) failed: %d\n", RamdiskFileSizeInPages, status));
ArcClose( RamdiskDeviceId );
return status;
}
DBGPRINT(VERBOSE, ("Allocated %d pages at page %x for RAM disk\n", RamdiskFileSizeInPages, RamdiskBasePage ));
//
// Download the ramdisk file using MTFTP
//
if ( RamdiskMTFTPChunkSize == 0 ) {
RamdiskMTFTPChunkSize = RamdiskMTFTPFileSize;
}
// starting client and server port (in Intel byte order to
// allow increment operators to work )
ClientPort = SWAP_WORD( RamdiskMTFTPCPort );
ServerPort = SWAP_WORD( RamdiskMTFTPSPort );
while ( FileOffset < RamdiskFileSize ) {
//
// Call the ROM implementation to download a single chunk
//
VirtualAddressOfOffset = ((LONGLONG)KSEG0_BASE | (RamdiskBasePage << PAGE_SHIFT)) + FileOffset;
ip = (PUCHAR)&RamdiskMTFTPAddr;
DBGPRINT(INFO, ("MTFTP Session %d: %s from %u.%u.%u.%u sport=%d cport=%d offset=0x%I64x\n",
iSession, p,
ip[0], ip[1], ip[2], ip[3], ClientPort, ServerPort,
VirtualAddressOfOffset ));
//
// the high 32 bits are going to be lost when calling RomMtftpReadFile.
// find out now, if this is happening
//
ASSERT( (VirtualAddressOfOffset >> 32) == 0 );
status = RomMtftpReadFile ( (PUCHAR)p,
(PVOID)(ULONG)VirtualAddressOfOffset,
(ULONG)RamdiskMTFTPChunkSize,
RamdiskTFTPAddr,
RamdiskMTFTPAddr,
SWAP_WORD( ClientPort ),
SWAP_WORD( ServerPort ),
RamdiskMTFTPTimeout,
RamdiskMTFTPDelay,
&DownloadSize );
if ( status != ESUCCESS ) {
DBGPRINT(ERR, ("RomMtftpReadFile failed %d\n", status ));
BlFreeDescriptor( RamdiskBasePage );
ArcClose( RamdiskDeviceId );
return status;
}
#if 1 || INTEL_MTFTP_SERVER_TEST
p[strlen(p) - 1]++;
RamdiskMTFTPAddr += 0x01000000;
#else
ClientPort++;
ServerPort++;
#endif
FileOffset += DownloadSize;
iSession++;
// update progress bar
currentProgressPercent = (ULONG)(((LONGLONG)FileOffset * 100) / RamdiskFileSize);
if ( ForceDisplayFirstTime || (currentProgressPercent != lastProgressPercent) ) {
BlUpdateProgressBar( currentProgressPercent );
ForceDisplayFirstTime = FALSE;
}
lastProgressPercent = currentProgressPercent;
}
DBGPRINT(INFO, ("MTFTP Download complete. 0x%I64x bytes transferred using %d sessions\n", RamdiskFileSize, iSession));
#endif
} else {
//
// Open the RAM disk image.
//
status = BlOpen( RamdiskDeviceId, p, ArcOpenReadOnly, &RamdiskFileId );
if (status != ESUCCESS) {
DBGPRINT(ERR, ("BlOpen(%s) failed: %d\n", p, status));
ArcClose( RamdiskDeviceId );
return status;
}
//
// Get the size of the RAM disk image.
//
status = BlGetFileInformation( RamdiskFileId, &fileInformation );
if (status != ESUCCESS) {
DBGPRINT(ERR, ("BlGetFileInformation(%s) failed: %d\n", p, status));
BlClose( RamdiskFileId );
ArcClose( RamdiskDeviceId );
return status;
}
RamdiskFileSize = fileInformation.EndingAddress.QuadPart;
RamdiskFileSizeInPages = (ULONG) BYTES_TO_PAGES(RamdiskFileSize);
if ( (RamdiskImageLength == 0) ||
(RamdiskImageLength > (RamdiskFileSize - RamdiskImageOffset)) ) {
RamdiskImageLength = RamdiskFileSize - RamdiskImageOffset;
}
//
// Allocate pages to hold the RAM disk image.
//
status = BlAllocateAlignedDescriptor(
LoaderXIPRom,
0,
RamdiskFileSizeInPages,
0,
&RamdiskBasePage
);
BlUsableBase = oldBase;
BlUsableLimit = oldLimit;
if (status != ESUCCESS) {
DBGPRINT(ERR, ("BlAllocateAlignedDescriptor(%d pages) failed: %d\n", RamdiskFileSizeInPages, status));
BlClose( RamdiskFileId );
ArcClose( RamdiskDeviceId );
return status;
}
DBGPRINT(VERBOSE, ("Allocated %d pages at page %x for RAM disk\n", RamdiskFileSizeInPages, RamdiskBasePage ));
//
// Read the RAM disk image into memory.
//
#define MAX_DISK_READ (1024 * 1024)
offset.QuadPart = 0;
remainingLength = RamdiskFileSize;
while ( offset.QuadPart < RamdiskFileSize ) {
LONGLONG availableLength;
ULONG readLength;
PVOID va;
ULONG count;
va = MapRamdisk( offset.QuadPart, &availableLength );
if ( remainingLength > availableLength ) {
readLength = (ULONG)availableLength;
} else {
readLength = (ULONG)remainingLength;
}
if ( readLength > MAX_DISK_READ ) {
readLength = MAX_DISK_READ;
}
status = BlSeek( RamdiskFileId, &offset, SeekAbsolute );
if ( status != ESUCCESS ) {
DBGPRINT(ERR, ("Unable to seek RAM disk image: %d\n", status));
BlClose( RamdiskFileId );
ArcClose( RamdiskDeviceId );
return status;
}
status = BlRead( RamdiskFileId, va, readLength, &count );
if ( (status != ESUCCESS) || (count != readLength) ) {
DBGPRINT(ERR, ( "Unable to read RAM disk image: status %d count %x (wanted %x)\n", status, count, readLength) );
BlClose( RamdiskFileId );
ArcClose( RamdiskDeviceId );
return status;
}
offset.QuadPart += readLength;
remainingLength -= readLength;
// update progress bar
currentProgressPercent = (ULONG)(((LONGLONG)offset.QuadPart * 100) / RamdiskFileSize);
if ( ForceDisplayFirstTime || (currentProgressPercent != lastProgressPercent) ) {
BlUpdateProgressBar( currentProgressPercent );
ForceDisplayFirstTime = FALSE;
}
lastProgressPercent = currentProgressPercent;
}
DBGPRINT(VERBOSE, ( "Done reading ramdisk\n" ) );
BlClose( RamdiskFileId );
RamdiskFileId = BL_INVALID_FILE_ID;
}
ArcClose( RamdiskDeviceId );
return status;
} // RamdiskReadImage
ARC_STATUS
RamdiskInitializeFromPath(
)
/*++
Routine Description:
This function will load a ramdisk image from the network
or another ARC boot device.
Arguments:
none
Return Value:
status
--*/
{
ARC_STATUS status;
ASSERT( RamdiskPath );
DBGPRINT(VERBOSE, ("RamdiskInitializeFromPath(%s)\n", RamdiskPath));
status = RamdiskReadImage( RamdiskPath );
if ( status == ESUCCESS ) {
DBGPRINT(VERBOSE, ("Ramdisk is active\n") );
RamdiskActive = TRUE;
}
return status;
} // RamdiskInitializeFromPath
ARC_STATUS
RamdiskClose(
IN ULONG FileId
)
/*++
Routine Description:
Closes the specified device
Arguments:
FileId - Supplies file id of the device to be closed
Return Value:
ESUCCESS - Device closed successfully
!ESUCCESS - Device was not closed.
--*/
{
if (BlFileTable[FileId].Flags.Open == 0) {
#if DBG
BlPrint(TEXT("ERROR - Unopened fileid %lx closed\r\n"),FileId);
#endif
}
BlFileTable[FileId].Flags.Open = 0;
return(ESUCCESS);
}
ARC_STATUS
RamdiskOpen(
IN PCHAR OpenPath,
IN OPEN_MODE OpenMode,
OUT PULONG FileId
)
/*++
Routine Description:
Opens a RAM disk for raw sector access.
Arguments:
OpenPath - Supplies a pointer to the name of the RAM disk.
OpenMode - Supplies the mode of the open
FileId - Supplies a pointer to a variable that specifies the file
table entry that is filled in if the open is successful.
Return Value:
ESUCCESS is returned if the open operation is successful. Otherwise,
an unsuccessful status is returned that describes the reason for failure.
--*/
{
ULONG Key;
PDRIVE_CONTEXT Context;
UNREFERENCED_PARAMETER( OpenMode );
//BlPrint(TEXT("RamdiskOpen entered\r\n"));
if ( !RamdiskActive ) {
//BlPrint(TEXT("RamdiskOpen: not active\r\n"));
return EBADF;
}
if(FwGetPathMnemonicKey(OpenPath,"ramdisk",&Key)) {
DBGPRINT(VERBOSE, ("RamdiskOpen: not a ramdisk path\n"));
return EBADF;
}
if ( Key != 0 ) {
DBGPRINT(ERR, ("RamdiskOpen: not ramdisk 0\n"));
return EBADF;
}
//
// Find an available FileId descriptor to open the device with
//
*FileId=2;
while (BlFileTable[*FileId].Flags.Open != 0) {
*FileId += 1;
if(*FileId == BL_FILE_TABLE_SIZE) {
DBGPRINT(ERR, ("RamdiskOpen: no file table entry available\n"));
return(ENOENT);
}
}
//
// We found an entry we can use, so mark it as open.
//
BlFileTable[*FileId].Flags.Open = 1;
BlFileTable[*FileId].DeviceEntryTable = &RamdiskEntryTable;
Context = &(BlFileTable[*FileId].u.DriveContext);
Context->Drive = (UCHAR)Key;
Context->xInt13 = TRUE;
DBGPRINT(VERBOSE, ("RamdiskOpen: exit success\n"));
return(ESUCCESS);
}
ARC_STATUS
RamdiskSeek (
IN ULONG FileId,
IN PLARGE_INTEGER Offset,
IN SEEK_MODE SeekMode
)
/*++
Routine Description:
Changes the current offset of the file specified by FileId
Arguments:
FileId - specifies the file on which the current offset is to
be changed.
Offset - New offset into file.
SeekMode - Either SeekAbsolute or SeekRelative
SeekEndRelative is not supported
Return Value:
ESUCCESS - Operation completed succesfully
EBADF - Operation did not complete successfully.
--*/
{
switch (SeekMode) {
case SeekAbsolute:
BlFileTable[FileId].Position = *Offset;
break;
case SeekRelative:
BlFileTable[FileId].Position.QuadPart += Offset->QuadPart;
break;
default:
#if DBG
BlPrint(TEXT("SeekMode %lx not supported\r\n"),SeekMode);
#endif
return(EACCES);
}
return(ESUCCESS);
}
ARC_STATUS
RamdiskWrite(
IN ULONG FileId,
OUT PVOID Buffer,
IN ULONG Length,
OUT PULONG Count
)
/*++
Routine Description:
Writes sectors directly to an open RAM disk.
Arguments:
FileId - Supplies the file to write to
Buffer - Supplies buffer with data to write
Length - Supplies number of bytes to write
Count - Returns actual bytes written
Return Value:
ESUCCESS - write completed successfully
!ESUCCESS - write failed
--*/
{
PUCHAR buffer;
LONGLONG offset;
ULONG remainingLength;
LONGLONG availableLength;
ULONG bytesWritten;
ULONG bytesThisPage;
PVOID va;
DBGPRINT(ERR, ("RamdiskWrite entered\n"));
//DbgBreakPoint();
buffer = Buffer;
offset = BlFileTable[FileId].Position.QuadPart;
remainingLength = Length;
if ( offset >= RamdiskImageLength ) {
return EINVAL;
}
if ( remainingLength > (RamdiskImageLength - offset) ) {
remainingLength = (ULONG)(RamdiskImageLength - offset);
}
bytesWritten = 0;
while ( remainingLength != 0 ) {
va = MapRamdisk( RamdiskImageOffset + offset, &availableLength );
bytesThisPage = remainingLength;
if ( remainingLength > availableLength ) {
bytesThisPage = (ULONG)availableLength;
}
memcpy( va, buffer, bytesThisPage );
offset += bytesThisPage;
buffer += bytesThisPage;
remainingLength -= bytesThisPage;
bytesWritten += bytesThisPage;
}
BlFileTable[FileId].Position.QuadPart += bytesWritten;
*Count = bytesWritten;
return ESUCCESS;
}
ARC_STATUS
RamdiskRead(
IN ULONG FileId,
OUT PVOID Buffer,
IN ULONG Length,
OUT PULONG Count
)
/*++
Routine Description:
Reads sectors directly from an open RAM disk.
Arguments:
FileId - Supplies the file to read from
Buffer - Supplies buffer to read into
Length - Supplies number of bytes to read
Count - Returns actual bytes read
Return Value:
ESUCCESS - read completed successfully
!ESUCCESS - read failed
--*/
{
PUCHAR buffer;
LONGLONG offset;
ULONG remainingLength;
LONGLONG availableLength;
ULONG bytesRead;
ULONG bytesThisPage;
PVOID va;
buffer = Buffer;
offset = BlFileTable[FileId].Position.QuadPart;
DBGPRINT(VERBOSE, ( "RamdiskRead: offset %x, length %x, buffer %p\n", (ULONG)offset, Length, buffer ));
remainingLength = Length;
if ( offset >= RamdiskImageLength ) {
DBGPRINT(ERR, ( "RamdiskRead: read beyond EOF\n" ) );
return EINVAL;
}
if ( remainingLength > (RamdiskImageLength - offset) ) {
remainingLength = (ULONG)(RamdiskImageLength - offset);
}
bytesRead = 0;
while ( remainingLength != 0 ) {
va = MapRamdisk( RamdiskImageOffset + offset, &availableLength );
DBGPRINT(VERBOSE, ( "Mapped offset %x, va %p, availableLength %x\n", (ULONG)offset, va, availableLength ) );
bytesThisPage = remainingLength;
if ( remainingLength > availableLength ) {
bytesThisPage = (ULONG)availableLength;
}
memcpy( buffer, va, bytesThisPage );
offset += bytesThisPage;
buffer += bytesThisPage;
remainingLength -= bytesThisPage;
bytesRead += bytesThisPage;
}
BlFileTable[FileId].Position.QuadPart += bytesRead;
*Count = bytesRead;
return ESUCCESS;
}
ARC_STATUS
RamdiskGetFileInfo(
IN ULONG FileId,
OUT PFILE_INFORMATION Finfo
)
/*++
Routine Description:
Returns file information about a RAMDISK file.
Arguments:
FileId - id of the file
Finfo - file information structure to be filled in
Return Value:
ESUCCESS - write completed successfully
!ESUCCESS - write failed
--*/
{
RtlZeroMemory(Finfo, sizeof(FILE_INFORMATION));
Finfo->EndingAddress.QuadPart = RamdiskImageLength;
Finfo->CurrentPosition.QuadPart = BlFileTable[FileId].Position.QuadPart;
Finfo->Type = DiskPeripheral;
return ESUCCESS;
}
ARC_STATUS
RamdiskMount(
IN CHAR * FIRMWARE_PTR MountPath,
IN MOUNT_OPERATION Operation
)
{
UNREFERENCED_PARAMETER( MountPath );
UNREFERENCED_PARAMETER( Operation );
DBGPRINT(VERBOSE, ( "RamdiskMount called\n" ));
return EINVAL;
}
ARC_STATUS
RamdiskReadStatus(
IN ULONG FileId
)
{
UNREFERENCED_PARAMETER( FileId );
DBGPRINT(VERBOSE, ( "RamdiskReadStatus called\n" ) );
return EINVAL;
}
ARC_STATUS
RamdiskSetFileInfo (
IN ULONG FileId,
IN ULONG AttributeFlags,
IN ULONG AttributeMask
)
{
UNREFERENCED_PARAMETER( FileId );
UNREFERENCED_PARAMETER( AttributeFlags );
UNREFERENCED_PARAMETER( AttributeMask );
DBGPRINT(VERBOSE, ( "RamdiskSetFileInfo called\n" ));
return EINVAL;
}
ARC_STATUS
RamdiskRename (
IN ULONG FileId,
IN CHAR * FIRMWARE_PTR NewName
)
{
UNREFERENCED_PARAMETER( FileId );
UNREFERENCED_PARAMETER( NewName );
DBGPRINT(VERBOSE, ( "RamdiskRename called\n" ));
return EINVAL;
}
ARC_STATUS
RamdiskGetDirectoryEntry (
IN ULONG FileId,
OUT PDIRECTORY_ENTRY Buffer,
IN ULONG Length,
OUT ULONG * FIRMWARE_PTR Count
)
{
UNREFERENCED_PARAMETER( FileId );
UNREFERENCED_PARAMETER( Buffer );
UNREFERENCED_PARAMETER( Length );
UNREFERENCED_PARAMETER( Count );
DBGPRINT(VERBOSE, ( "RamdiskGetDirectoryEntry called\n" ));
return EINVAL;
}
PVOID
MapRamdisk (
LONGLONG Offset,
PLONGLONG AvailableLength
)
{
LONGLONG VirtualAddressOfOffset;
VirtualAddressOfOffset = ((LONGLONG)(KSEG0_BASE | (RamdiskBasePage << PAGE_SHIFT))) + Offset;
*AvailableLength = RamdiskFileSize - Offset;
#if defined(_X86_)
//
// the high 32 bits of physicalAddressOfOffset are
// going to be lost when returning the address as a pvoid.
// find out if this is happening now.
//
ASSERT( (VirtualAddressOfOffset >> 32) == 0 );
return (PVOID)(ULONG)VirtualAddressOfOffset;
#else
return (PVOID)VirtualAddressOfOffset;
#endif
}
PCHAR
RamdiskGetOptionValue(
IN PCHAR LoadOptions,
IN PCHAR OptionName
)
/*++
Routine Description:
Parse the load options string returning a value of one of the
options.
Format supported: /OPTIONNAME=VALUE
Note there is no space before or after the '='.
Value is terminated with a '\r','\n',' ','/', or '\t'
Arguments:
LoadOptions - Loader options from boot.ini. Must be all caps.
OptionName - Name of the option to find.
Return Value:
Pointer to a value string that has been allocated with
BlAllocateHeap or NULL if the option has not found.
--*/
{
PCHAR retValue = NULL;
PCHAR value;
PCHAR p;
ULONG n;
ASSERT( LoadOptions );
ASSERT( OptionName );
if ( (p = strstr( LoadOptions, OptionName )) != 0 ) {
value = strchr( p , '=' );
if (value) {
value++;
for (p = value; *p; p++) {
if (*p == ' ') break;
if (*p == '/') break;
if (*p == '\n') break;
if (*p == '\r') break;
if (*p == '\t') break;
}
n = (ULONG)(p - value);
retValue = (PCHAR)BlAllocateHeap( n+1 );
if ( retValue ) {
strncpy( retValue, value, n );
}
}
}
return retValue;
}
ULONG
RamdiskParseIPAddr(
IN PCHAR psz
)
/*++
Routine Description:
parses an ip address from a string
Arguments: [psz] - Ip address string
Returns: ipaddress (in network byte order) or 0.
--*/
{
ULONG nAddr = 0;
ULONG nDigit = 0;
ULONG cDigits = 0;
for (; (psz!= NULL && *psz != 0); psz++) {
if (*psz >= '0' && *psz <= '9') {
nDigit = nDigit * 10 + *psz - '0';
if ( nDigit > 255 ) {
return 0;
}
}
else if (*psz == '.') {
nAddr = (nAddr << 8) | nDigit;
nDigit = 0;
cDigits++;
} else {
break;
}
}
if (cDigits != 3) {
return 0;
}
nAddr = (nAddr << 8) | nDigit;
return SWAP_DWORD( nAddr );
}
BOOLEAN
RamdiskHexStringToDword(
IN PCHAR psz,
OUT PULONG RetValue,
IN USHORT cDigits,
IN CHAR chDelim
)
/*++
Routine Description:
scan psz for a number of hex digits (at most 8); update psz
return value in Value; check for chDelim;
Arguments: [psz] - the hex string to convert
[Value] - the returned value
[cDigits] - count of digits
Returns: TRUE for success
--*/
{
USHORT Count;
ULONG Value;
Value = 0;
for (Count = 0; Count < cDigits; Count++, psz++)
{
if (*psz >= '0' && *psz <= '9') {
Value = (Value << 4) + *psz - '0';
} else if (*psz >= 'A' && *psz <= 'F') {
Value = (Value << 4) + *psz - 'A' + 10;
} else if (*psz >= 'a' && *psz <= 'f') {
Value = (Value << 4) + *psz - 'a' + 10;
} else {
return(FALSE);
}
}
*RetValue = Value;
if (chDelim != 0) {
return *psz++ == chDelim;
} else {
return TRUE;
}
}
BOOLEAN
RamdiskUUIDFromString(
IN PCHAR psz,
OUT LPGUID pguid
)
/**
Routine Description:
Parse UUID such as 00000000-0000-0000-0000-000000000000
Arguments:
[psz] - Supplies the UUID string to convert
[pguid] - Returns the GUID.
Returns: TRUE if successful
**/
{
ULONG dw;
if (!RamdiskHexStringToDword(psz, &pguid->Data1, sizeof(ULONG)*2, '-')) {
return FALSE;
}
psz += sizeof(ULONG)*2 + 1;
if (!RamdiskHexStringToDword(psz, &dw, sizeof(USHORT)*2, '-')) {
return FALSE;
}
psz += sizeof(USHORT)*2 + 1;
pguid->Data2 = (USHORT)dw;
if (!RamdiskHexStringToDword(psz, &dw, sizeof(USHORT)*2, '-')) {
return FALSE;
}
psz += sizeof(USHORT)*2 + 1;
pguid->Data3 = (USHORT)dw;
if (!RamdiskHexStringToDword(psz, &dw, sizeof(UCHAR)*2, 0)) {
return FALSE;
}
psz += sizeof(UCHAR)*2;
pguid->Data4[0] = (UCHAR)dw;
if (!RamdiskHexStringToDword(psz, &dw, sizeof(UCHAR)*2, '-')) {
return FALSE;
}
psz += sizeof(UCHAR)*2+1;
pguid->Data4[1] = (UCHAR)dw;
if (!RamdiskHexStringToDword(psz, &dw, sizeof(UCHAR)*2, 0)) {
return FALSE;
}
psz += sizeof(UCHAR)*2;
pguid->Data4[2] = (UCHAR)dw;
if (!RamdiskHexStringToDword(psz, &dw, sizeof(UCHAR)*2, 0)) {
return FALSE;
}
psz += sizeof(UCHAR)*2;
pguid->Data4[3] = (UCHAR)dw;
if (!RamdiskHexStringToDword(psz, &dw, sizeof(UCHAR)*2, 0)) {
return FALSE;
}
psz += sizeof(UCHAR)*2;
pguid->Data4[4] = (UCHAR)dw;
if (!RamdiskHexStringToDword(psz, &dw, sizeof(UCHAR)*2, 0)) {
return FALSE;
}
psz += sizeof(UCHAR)*2;
pguid->Data4[5] = (UCHAR)dw;
if (!RamdiskHexStringToDword(psz, &dw, sizeof(UCHAR)*2, 0)) {
return FALSE;
}
psz += sizeof(UCHAR)*2;
pguid->Data4[6] = (UCHAR)dw;
if (!RamdiskHexStringToDword(psz, &dw, sizeof(UCHAR)*2, 0)) {
return FALSE;
}
psz += sizeof(UCHAR)*2;
pguid->Data4[7] = (UCHAR)dw;
return TRUE;
}
BOOLEAN
RamdiskGUIDFromString(
IN PCHAR psz,
OUT LPGUID pguid
)
/**
Routine Description:
Parse GUID such as {00000000-0000-0000-0000-000000000000}
Arguments:
[psz] - Supplies the UUID string to convert
[pguid] - Returns the GUID.
Returns: TRUE if successful
**/
{
if (*psz == '{' ) {
psz++;
}
if (RamdiskUUIDFromString(psz, pguid) != TRUE) {
return FALSE;
}
psz += 36;
if (*psz == '}' ) {
psz++;
}
if (*psz != '\0') {
return FALSE;
}
return TRUE;
}
ARC_STATUS
RamdiskParseOptions (
IN PCHAR LoadOptions
)
/*++
Routine Description:
Parses all the Ramdisk params from the boot.ini option string.
Arguments:
LoadOptions - Loader options from boot.ini. Must be all caps.
/RDPATH - Indicates that the boot ramdisk should be downloaded
from the specified path. This option takes
precedence over RDBUILD.
Example: /RDPATH=net(0)\boot\ramdisk.dat
/RDMTFTPADDR - Specifies the Multicast Address where the ramdisk
image should be downloaded from. If not specified
a unicast download from the PXE boot server will
be performed.
/RDMTFTPCPORT - Specifies the Multicast Client port to use.
/RDMTFTPSPORT - Specifies the Multicast Server port to use.
/RDMTFTPDELAY - Specifies the delay before starting a new MTFTP session.
/RDMTFTPTIMEOUT - Specifies the timeout before restarting a MTFTP session.
/RDIMAGEOFFSET - Specifies the offset into the downloaded file at which the
actual disk image begins. If not specified, 0 is used.
/RDIMAGELENGTH - Specifies the length of the actual disk image. If not
specified, the size of the downloaded file minus the offset
to the image (RDIMAGEOFFSET) is used.
/RDFILESIZE - Specifies the size of the file to be downloaded.
/RDCHUNKSIZE - Specifies the size of each file chunck when more than
one MTFTP session is required to download a large file. If the
file is to be downloaded with one chunk this option is omitted
or is set to zero.
This is used to workaround a size limitation in the MTFTP
protcol. MTFTP currently has 16-bit block counts, therefore
when using 512 byte blocks we are limited to ~32MB files.
Example 1: assume we want to download a 85MB file
using 512 byte TFTP block sizes.
/RDMTFTPADDR=224.1.1.1 /RDMTFTPCPORT=100 /RDMTFTPSPORT=200
/RDCHUNKSIZE=31457280 /RDFILESIZE=89128960
1st MTFTP session on CPort=100, SPort=200 Size=31457280 (30MB)
2nd MTFTP session on CPort=101, SPort=201 Size=31457280 (30MB)
3rd MTFTP session on CPort=102, SPort=202 Size=26214400 (25MB)
Example 2: assume we want to download a 300MB file
using 1468 byte TFTP block sizes.
/RDMTFTPADDR=224.1.1.2 /RDMTFTPCPORT=100 /RDMTFTPSPORT=200
/RDCHUNKSIZE=94371840 /RDFILESIZE=314572800
1st MTFTP session on CPort=100, SPort=200 Size=94371840 (90MB)
2nd MTFTP session on CPort=101, SPort=201 Size=94371840 (90MB)
3rd MTFTP session on CPort=102, SPort=202 Size=94371840 (90MB)
4th MTFTP session on CPort=103, SPort=203 Size=31457280 (30MB)
/RDBUILD - Indicates that the boot ramdisk should be built
from the build server. This is ignored if the RDPATH
option is set.
Example: /RDBUILD
/RDGUID - Specifies the GUID of the configuration to be built
by the build server.
Example: /RDGUID={54C7D140-09EF-11D1-B25A-F5FE627ED95E}
/RDDISCOVERY - Indicates what address discovery packets should be sent to.
If this option doesn't exist, then we will not do discovery
and default to using the list of servers in RDSERVERS. If
Examples:
/RDDISCOVERY=255.255.255.255
This will send a broadcast packet to the local
network that the machine is connected to.
/RDDISCOVERY=224.100.100.100
This will send a multicast packet to the
address specified.
/RDSERVERS Specifies a list of Build Servers to send build
requests to. This will override any setting that
RDDISCOVERY has set. A maximum of 10 servers are supported.
Example: /RDSERVERS={10.0.0.3, 10.0.0.4}
/RDSERVERPORT Specifies the default port to send build packets to.
If this is not set, it defaults to 4012.
Example: /RDSERVERPORT=5623
/RDTIMEOUT Specifies the timeout period to wait for a response in
seconds. Default is 4 secs.
Example: /RDTIMEOUT=10
/RDRETRY Specifies the number of times to retry finding a build
server. Default is 5 times.
Example: /RDRETRY=5
Return Value:
ESUCCESS - read completed successfully
!ESUCCESS - read failed
--*/
{
PCHAR value;
PUCHAR p;
USHORT i;
if ( LoadOptions == NULL ) {
return ESUCCESS;
}
//
// Get RDPATH and its associated options
//
RamdiskPath = RamdiskGetOptionValue( LoadOptions, "RDPATH" );
if (RamdiskPath) {
value = RamdiskGetOptionValue( LoadOptions, "RDIMAGEOFFSET" );
if (value) RamdiskImageOffset = atoi( value );
value = RamdiskGetOptionValue( LoadOptions, "RDIMAGELENGTH" );
if (value) RamdiskImageLength = _atoi64( value );
//
// By Default the PXE Boot Server is the TFTP address
//
RamdiskTFTPAddr = NetServerIpAddress;
//
// Get the MTFTP Address used to download the image.
// if not specified, the image will be downloaded
// from the same place as ntldr (i.e. the PXE
// boot server).
//
value = RamdiskGetOptionValue( LoadOptions, "RDMTFTPADDR" );
if ( value ) {
RamdiskMTFTPAddr = RamdiskParseIPAddr( value );
value = RamdiskGetOptionValue( LoadOptions, "RDMTFTPCPORT" );
if ( value ) RamdiskMTFTPCPort = SWAP_WORD( (USHORT)atoi( value ) );
value = RamdiskGetOptionValue( LoadOptions, "RDMTFTPSPORT" );
if (value) RamdiskMTFTPSPort = SWAP_WORD( (USHORT)atoi( value ) );
value = RamdiskGetOptionValue( LoadOptions, "RDMTFTPDELAY" );
if (value) RamdiskMTFTPDelay = (USHORT)atoi( value );
value = RamdiskGetOptionValue( LoadOptions, "RDMTFTPTIMEOUT" );
if (value) RamdiskMTFTPTimeout = (USHORT)atoi( value );
value = RamdiskGetOptionValue( LoadOptions, "RDFILESIZE" );
if (value) RamdiskMTFTPFileSize = _atoi64( value );
value = RamdiskGetOptionValue( LoadOptions, "RDCHUNKSIZE" );
if (value) RamdiskMTFTPChunkSize = _atoi64( value );
// Validate options
if ( RamdiskMTFTPAddr == 0 ||
RamdiskMTFTPCPort == 0 ||
RamdiskMTFTPSPort == 0 ||
RamdiskMTFTPDelay == 0 ||
RamdiskMTFTPTimeout == 0 ||
RamdiskMTFTPFileSize == 0 ||
RamdiskMTFTPChunkSize > RamdiskMTFTPFileSize ) {
return EINVAL;
}
}
if (DBGLVL(INFO)) {
DbgPrint( "RAMDISK options:\n");
DbgPrint( "RDPATH = %s\n", RamdiskPath);
p = (PUCHAR) &RamdiskMTFTPAddr;
DbgPrint( "RDMTFTPADDR = %u.%u.%u.%u\n", p[0], p[1], p[2], p[3]);
DbgPrint( "RDMTFTPCPORT = %d\n", SWAP_WORD( RamdiskMTFTPCPort ));
DbgPrint( "RDMTFTPSPORT = %d\n", SWAP_WORD( RamdiskMTFTPSPort ));
DbgPrint( "RDMTFTPDELAY = %d\n", RamdiskMTFTPDelay);
DbgPrint( "RDMTFTPTIMEOUT = %d\n", RamdiskMTFTPTimeout);
DbgPrint( "RDFILESIZE = 0x%0I64x bytes\n", RamdiskMTFTPFileSize );
DbgPrint( "RDCHUNKSIZE = 0x%0I64x bytes\n", RamdiskMTFTPChunkSize );
DbgPrint( "RDIMAGEOFFSET = 0x%x bytes\n", RamdiskImageOffset );
DbgPrint( "RDIMAGELENGTH = 0x%0I64x bytes\n", RamdiskImageLength );
}
// we are done if RDPATH was specified.
return ESUCCESS;
}
//
// Check if RDBUILD exists
//
if ( strstr( LoadOptions, "RDBUILD" ) ) {
RamdiskBuild = TRUE;
value = RamdiskGetOptionValue( LoadOptions, "RDGUID" );
if ( value == NULL ||
RamdiskGUIDFromString( value, &RamdiskGuid ) == FALSE ) {
return EINVAL;
}
value = RamdiskGetOptionValue( LoadOptions, "RDDISCOVERY" );
if ( value ) RamdiskDiscovery = RamdiskParseIPAddr( value );
value = RamdiskGetOptionValue(LoadOptions, "RDSERVERPORT");
if (value) RamdiskBuildPort = SWAP_WORD((USHORT)atoi(value));
value = RamdiskGetOptionValue( LoadOptions, "RDSERVERS" );
if ( value && *value == '{' ) {
PCHAR e = strchr( value, '}' );
if ( e && (ULONG)(e - value) > 7 ) { // at least seven characters for X.X.X.X
while ( value && value < e && RamdiskServerCount < RAMDISK_MAX_SERVERS) {
value++;
RamdiskServers[RamdiskServerCount] = RamdiskParseIPAddr( value );
RamdiskServerCount++;
value = strchr(value, ',' );
}
}
}
value = RamdiskGetOptionValue( LoadOptions, "RDTIMEOUT" );
if (value) RamdiskTimeout = (USHORT)atoi( value );
value = RamdiskGetOptionValue( LoadOptions, "RDRETRY" );
if (value) RamdiskRetry = (USHORT)atoi( value );
// Validate options
if (((RamdiskDiscovery == 0) && (RamdiskServerCount == 0)) ||
(RamdiskBuildPort == 0) ||
(RamdiskTimeout == 0) ||
(RamdiskRetry == 0)) {
return EINVAL;
}
//
// Print out debug information
//
if (DBGLVL(INFO)) {
DbgPrint("RDBUILD options:\n");
DbgPrint("RDGUID = {%x-%x-%x-%x%x%x%x%x%x%x%x}\n",
RamdiskGuid.Data1, RamdiskGuid.Data2,
RamdiskGuid.Data3,
RamdiskGuid.Data4[0], RamdiskGuid.Data4[1],
RamdiskGuid.Data4[2], RamdiskGuid.Data4[3],
RamdiskGuid.Data4[4], RamdiskGuid.Data4[5],
RamdiskGuid.Data4[6], RamdiskGuid.Data4[7]);
p = (PUCHAR) &RamdiskDiscovery;
DbgPrint("RDDISCOVERY = %u.%u.%u.%u\n", p[0], p[1], p[2], p[3]);
DbgPrint("RDBUILDPORT = %d\n", RamdiskBuildPort);
DbgPrint("RDSERVERS = %d\n", RamdiskServerCount);
for (i = 0; i < RamdiskServerCount; i++) {
p = (PUCHAR) &RamdiskServers[i];
DbgPrint("RDSERVER[%d] = %u.%u.%u.%u\n", i, p[0], p[1], p[2], p[3]);
}
DbgPrint("RDTIMEOUT = %d\n", RamdiskTimeout);
DbgPrint("RDRETRY = %d\n", RamdiskRetry);
}
}
return ESUCCESS;
}
#if defined(i386) // RDBUILD is only supported on x86 machines for now
VOID
RamdiskDeviceInfoToString(
DEVICE_INFO * Device,
PCHAR DeviceString
)
/*++
Routine Description:
This routine generates a string representation of the Device info for
debugging purposes.
Arguments:
Device - Pointer to the device info structure
DeviceString - a pointer to a buffer that will hold the final string. The buffer
must be at least 128 * sizeof(CHAR) bytes.
Return Value:
NONE.
--*/
{
const CHAR HexToCharTable[17] = "0123456789ABCDEF";
if (Device->DeviceType == BMBUILD_DEVICE_TYPE_PCI) {
sprintf ( DeviceString,
"%d.%d.%d PCI\\VEN_%04X&DEV_%04X&SUBSYS_%04X%04X&REV_%02X&CC_%02X%02X%02X",
BUSDEVFUNC_TO_BUS( Device->info.pci.BusDevFunc ),
BUSDEVFUNC_TO_DEVICE( Device->info.pci.BusDevFunc ),
BUSDEVFUNC_TO_FUNCTION( Device->info.pci.BusDevFunc ),
Device->info.pci.VendorID,
Device->info.pci.DeviceID,
Device->info.pci.SubVendorID,
Device->info.pci.SubDeviceID,
Device->info.pci.RevisionID,
Device->info.pci.BaseClass,
Device->info.pci.SubClass,
Device->info.pci.ProgIntf );
} else if (Device->DeviceType == BMBUILD_DEVICE_TYPE_PCI_BRIDGE ) {
sprintf ( DeviceString,
"%d.%d.%d PCI\\VEN_%04X&DEV_%04X&REV_%02X&CC_%02X%02X%02X Bridge %d->%d Sub = %d",
BUSDEVFUNC_TO_BUS( Device->info.pci_bridge.BusDevFunc ),
BUSDEVFUNC_TO_DEVICE( Device->info.pci_bridge.BusDevFunc ),
BUSDEVFUNC_TO_FUNCTION( Device->info.pci_bridge.BusDevFunc ),
Device->info.pci_bridge.VendorID,
Device->info.pci_bridge.DeviceID,
Device->info.pci_bridge.RevisionID,
Device->info.pci_bridge.BaseClass,
Device->info.pci_bridge.SubClass,
Device->info.pci_bridge.ProgIntf,
Device->info.pci_bridge.PrimaryBus,
Device->info.pci_bridge.SecondaryBus,
Device->info.pci_bridge.SubordinateBus );
} else if (Device->DeviceType == BMBUILD_DEVICE_TYPE_PNP) {
CHAR ProductIDStr[8];
PUCHAR id = (PUCHAR)&Device->info.pnp.EISADevID;
ProductIDStr[0] = (id[0] >> 2) + 0x40;
ProductIDStr[1] = (((id[0] & 0x03) << 3) | (id[1] >> 5)) + 0x40;
ProductIDStr[2] = (id[1] & 0x1f) + 0x40;
ProductIDStr[3] = HexToCharTable[id[2] >> 4];
ProductIDStr[4] = HexToCharTable[id[2] & 0x0F];
ProductIDStr[5] = HexToCharTable[id[3] >> 4];
ProductIDStr[6] = HexToCharTable[id[3] & 0x0F];
ProductIDStr[7] = 0x00;
sprintf( DeviceString,
"%d %s CC_%02X%02X%02X",
Device->info.pnp.CardSelNum,
ProductIDStr,
Device->info.pnp.BaseClass,
Device->info.pnp.SubClass,
Device->info.pnp.ProgIntf );
}
}
VOID
RamdiskWait(
ULONG WaitTime
)
/*++
Routine Description:
This routine will spin a loop so you can wait for the specified time.
Arguments:
WaitTime - The time to wait in seconds
Return Value:
NONE.
--*/
{
ULONG startTime = SysGetRelativeTime();
while ((SysGetRelativeTime() - startTime) < WaitTime) {
}
}
VOID
RamdiskPrintBuildProgress(
ULONG MsgId,
ULONG BuildServerIpAddress
)
/*++
Routine Description:
This routine will look up the passed in message id and display it on the screen
using the address as arguments.
Arguments:
MsgId - The Id of the message to display
BuildServerIpAddress - The ip address to use as agruments to the message
Return Value:
NONE.
--*/
{
PUCHAR address;
PTCHAR formatString = NULL;
TCHAR buffer[256];
//
// look up format message
//
formatString = BlFindMessage(MsgId);
if (formatString != NULL) {
//
// print progress message
//
address = (PUCHAR) &BuildServerIpAddress;
_stprintf(buffer, formatString, address[0], address[1], address[2], address[3]);
BlOutputTrailerMsgStr(buffer);
}
}
ARC_STATUS
RamdiskBuildDiscover(
IN BMBUILD_DISCOVER_PACKET * Discover,
IN ULONG DiscoverLengthMax,
OUT ULONG * DiscoverLength
)
/*++
Routine Description:
This routine will build a discover packet based on the Ramdisk parameters in the
boot.ini.
Arguments:
Discover - The buffer to fill in with data
DiscoverLengthMax - The maximum size the discovery packet could be
DiscoverLength - The final size of the discovery packet
Return Value:
ESUCCESS - If the packet was filled in correctly.
EINVAL - If the packet couldn't be filled in correctly
--*/
{
ULONG GuidLength;
PUCHAR Guid;
ASSERT(Discover);
ASSERT(DiscoverLength);
if (DiscoverLengthMax < sizeof(BMBUILD_DISCOVER_PACKET)) {
return EINVAL;
}
RtlZeroMemory(Discover, DiscoverLengthMax);
//
// Set the protocol defaults
//
Discover->Version = BMBUILD_PACKET_VERSION;
Discover->OpCode = BMBUILD_OPCODE_DISCOVER;
//
// Get the SMBIOS UUID (or PXE MAC address)
//
GetGuid(&Guid, &GuidLength);
ASSERT(GuidLength == sizeof(Discover->MachineGuid));
if (GuidLength == sizeof(Discover->MachineGuid)) {
memcpy(&Discover->MachineGuid, Guid, GuidLength);
}
//
// Set the product guid from boot.ini
//
memcpy(&Discover->ProductGuid, &RamdiskGuid, sizeof(GUID));
//
// Debug prints
//
if (DBGLVL(INFO)) {
DbgPrint("RAMDISK Build Discover\n");
DbgPrint("MachineGuid = {%x-%x-%x-%x%x%x%x%x%x%x%x}\n",
Discover->MachineGuid.Data1, Discover->MachineGuid.Data2,
Discover->MachineGuid.Data3,
Discover->MachineGuid.Data4[0], Discover->MachineGuid.Data4[1],
Discover->MachineGuid.Data4[2], Discover->MachineGuid.Data4[3],
Discover->MachineGuid.Data4[4], Discover->MachineGuid.Data4[5],
Discover->MachineGuid.Data4[6], Discover->MachineGuid.Data4[7]);
DbgPrint("ProductGuid = {%x-%x-%x-%x%x%x%x%x%x%x%x}\n",
Discover->ProductGuid.Data1, Discover->ProductGuid.Data2,
Discover->ProductGuid.Data3,
Discover->ProductGuid.Data4[0], Discover->ProductGuid.Data4[1],
Discover->ProductGuid.Data4[2], Discover->ProductGuid.Data4[3],
Discover->ProductGuid.Data4[4], Discover->ProductGuid.Data4[5],
Discover->ProductGuid.Data4[6], Discover->ProductGuid.Data4[7]);
}
*DiscoverLength = sizeof(BMBUILD_DISCOVER_PACKET);
return ESUCCESS;
}
ARC_STATUS
RamdiskBuildRequest(
IN PBMBUILD_REQUEST_PACKET pRequest,
IN ULONG RequestLengthMax,
OUT ULONG * pLength
)
/*++
Routine Description:
This routine will build a request packet based on the Ramdisk parameters in the
boot.ini and the machine configuration.
Arguments:
pRequest - a pointer to a buffer that will hold the request
RequestLengthMax - the maximum size that the request packet can be
pLength - the final size of the request packet
Return Value:
ESUCCESS - If the packet was filled in correctly.
!ESUCCESS - If the packet couldn't be filled in correctly
--*/
{
ARC_STATUS status;
PDEVICE_INFO pDevice;
t_PXENV_UNDI_GET_NIC_TYPE PxeNicType;
PCONFIGURATION_COMPONENT_DATA Node = NULL;
PCONFIGURATION_COMPONENT_DATA CurrentNode = NULL;
PCONFIGURATION_COMPONENT_DATA ResumeNode = NULL;
PPCIDEVICE pPCIDevice;
PPNP_BIOS_INSTALLATION_CHECK pPNPBios;
PPNP_BIOS_DEVICE_NODE pDevNode;
PCM_PARTIAL_RESOURCE_LIST pPartialList;
PUCHAR pCurr;
USHORT cDevices;
USHORT i;
ULONG lengthRemaining;
ULONG GuidLength;
PUCHAR Guid;
PCHAR HalName;
ULONG HalNameSize;
BOOLEAN fNICFound = FALSE;
ASSERT(pRequest);
ASSERT(RequestLengthMax);
ASSERT(pLength);
RtlZeroMemory(pRequest, RequestLengthMax);
//
// Set the protocol defaults
//
pRequest->Version = BMBUILD_PACKET_VERSION;
pRequest->OpCode = BMBUILD_OPCODE_REQUEST;
//
// Get the SMBIOS UUID (or PXE MAC address)
//
GetGuid(&Guid, &GuidLength);
ASSERT(GuidLength == sizeof(pRequest->MachineGuid));
if (GuidLength == sizeof(pRequest->MachineGuid)) {
memcpy(&pRequest->MachineGuid, Guid, GuidLength);
}
//
// Set the product guid from boot.ini
//
memcpy(&pRequest->ProductGuid, &RamdiskGuid, sizeof(GUID));
pRequest->Flags = 0;
#ifdef _IA64_
pRequest->Architecture = PROCESSOR_ARCHITECTURE_IA64;
#else
pRequest->Architecture = PROCESSOR_ARCHITECTURE_INTEL;
#endif
//
// Detect the appropriate HAL using TextMode Setup methods.
//
#ifdef DOWNLOAD_TXTSETUP_SIF
status = SlInitIniFile( "net(0)",
0,
"boot\\txtsetup.sif",
&InfFile,
NULL,
NULL,
&x);
#endif
//
// The device list start right after the fixed portion of the packet
pRequest->DeviceCount = 0;
//
// Make sure that the device array starts on a valid boundary
//
ASSERT(ROUND2(BMBUILD_FIELD_OFFSET(BMBUILD_REQUEST_PACKET, Data), 4) < 0xFFFF);
pRequest->DeviceOffset = ROUND2(BMBUILD_FIELD_OFFSET(BMBUILD_REQUEST_PACKET, Data), 4);
pDevice = (PDEVICE_INFO)((PUCHAR)pRequest + pRequest->DeviceOffset);
//
// Get the PXE NIC information
//
RtlZeroMemory(&PxeNicType, sizeof(PxeNicType));
status = RomGetNicType(&PxeNicType);
if ((status != PXENV_EXIT_SUCCESS) || (PxeNicType.Status != PXENV_EXIT_SUCCESS)) {
DBGPRINT(ERR, ("RAMDISK ERROR: Couldn't get the NIC type from PXE. Failed with %x, status = %x\n", status, PxeNicType.Status));
return ENODEV;
}
//
// Fill in PCI Device information
//
Node = KeFindConfigurationEntry(FwConfigurationTree,
PeripheralClass,
RealModePCIEnumeration,
NULL);
ASSERT(Node != NULL);
if (Node == NULL) {
return ENODEV;
}
ASSERT(Node->ComponentEntry.ConfigurationDataLength > 0);
ASSERT(Node->ConfigurationData != NULL);
pPCIDevice = (PPCIDEVICE)((PUCHAR)Node->ConfigurationData + sizeof(CM_PARTIAL_RESOURCE_LIST));
cDevices = (USHORT)(Node->ComponentEntry.ConfigurationDataLength - sizeof (CM_PARTIAL_RESOURCE_LIST)) / sizeof (PCIDEVICE);
if (cDevices > BMBUILD_MAX_DEVICES(RequestLengthMax)) {
DBGPRINT(ERR, ("RAMDISK ERROR: Too many PCI devices to fit in a request\n"));
return EINVAL;
}
for (i = 0; i < cDevices; i++ ) {
//
// check if this is a bridge or a normal device
//
if ((pPCIDevice->Config.HeaderType & (~PCI_MULTIFUNCTION) ) == PCI_BRIDGE_TYPE) {
//
// Bridge.
//
pDevice[i].DeviceType = BMBUILD_DEVICE_TYPE_PCI_BRIDGE;
pDevice[i].info.pci_bridge.BusDevFunc = pPCIDevice->BusDevFunc;
pDevice[i].info.pci_bridge.VendorID = pPCIDevice->Config.VendorID;
pDevice[i].info.pci_bridge.DeviceID = pPCIDevice->Config.DeviceID;
pDevice[i].info.pci_bridge.BaseClass = pPCIDevice->Config.BaseClass;
pDevice[i].info.pci_bridge.SubClass = pPCIDevice->Config.SubClass;
pDevice[i].info.pci_bridge.ProgIntf = pPCIDevice->Config.ProgIf;
pDevice[i].info.pci_bridge.RevisionID = pPCIDevice->Config.RevisionID;
pDevice[i].info.pci_bridge.PrimaryBus = pPCIDevice->Config.u.type1.PrimaryBus;
pDevice[i].info.pci_bridge.SecondaryBus = pPCIDevice->Config.u.type1.SecondaryBus;
pDevice[i].info.pci_bridge.SubordinateBus = pPCIDevice->Config.u.type1.SubordinateBus;
} else {
//
// Non-bridge PCI device
//
pDevice[i].DeviceType = BMBUILD_DEVICE_TYPE_PCI;
pDevice[i].info.pci.BusDevFunc = pPCIDevice->BusDevFunc;
pDevice[i].info.pci.VendorID = pPCIDevice->Config.VendorID;
pDevice[i].info.pci.DeviceID = pPCIDevice->Config.DeviceID;
pDevice[i].info.pci.BaseClass = pPCIDevice->Config.BaseClass;
pDevice[i].info.pci.SubClass = pPCIDevice->Config.SubClass;
pDevice[i].info.pci.ProgIntf = pPCIDevice->Config.ProgIf;
pDevice[i].info.pci.RevisionID = pPCIDevice->Config.RevisionID;
pDevice[i].info.pci.SubVendorID = pPCIDevice->Config.u.type0.SubVendorID;
pDevice[i].info.pci.SubDeviceID = pPCIDevice->Config.u.type0.SubSystemID;
//
// Check if this device is the PXE boot device
//
if ((PxeNicType.NicType == 2) &&
(PxeNicType.pci_pnp_info.pci.BusDevFunc == pPCIDevice->BusDevFunc)) {
pRequest->PrimaryNicIndex = i;
fNICFound = TRUE;
}
}
pPCIDevice++;
}
pRequest->DeviceCount = pRequest->DeviceCount + cDevices;
pDevice += cDevices;
//
// Fill in PNP Device information (if there)
//
Node = NULL;
while ((CurrentNode = KeFindConfigurationNextEntry(
FwConfigurationTree,
AdapterClass,
MultiFunctionAdapter,
NULL,
&ResumeNode)) != 0) {
if (!(strcmp(CurrentNode->ComponentEntry.Identifier,"PNP BIOS"))) {
Node = CurrentNode;
break;
}
ResumeNode = CurrentNode;
}
if (Node != NULL) {
//
// Set the PnP BIOS devices if found
//
ASSERT(Node->ComponentEntry.ConfigurationDataLength > 0);
ASSERT(Node->ConfigurationData != NULL);
pPartialList = (PCM_PARTIAL_RESOURCE_LIST)Node->ConfigurationData;
pPNPBios = (PPNP_BIOS_INSTALLATION_CHECK)((PUCHAR)Node->ConfigurationData + sizeof(CM_PARTIAL_RESOURCE_LIST));
pCurr = (PUCHAR)pPNPBios + pPNPBios->Length;
lengthRemaining = pPartialList->PartialDescriptors[0].u.DeviceSpecificData.DataSize - pPNPBios->Length;
for (cDevices = 0; lengthRemaining > sizeof(PNP_BIOS_DEVICE_NODE); cDevices++) {
if ((pRequest->DeviceCount + cDevices + 1) > BMBUILD_MAX_DEVICES(RamdiskMaxPacketSize)) {
DBGPRINT(ERR, ("RAMDISK ERROR: Too many PNP devices to fit in a request\n"));
return EINVAL;
}
pDevNode = (PPNP_BIOS_DEVICE_NODE)pCurr;
if (pDevNode->Size > lengthRemaining) {
DBGPRINT(ERR, ("PNP Node # %d, invalid size (%d), length remaining (%d)\n",
pDevNode->Node, pDevNode->Size, lengthRemaining));
ASSERT(FALSE);
// REVIEW: [bassamt] Should I fail here?
break;
}
pDevice->DeviceType = BMBUILD_DEVICE_TYPE_PNP;
pDevice->info.pnp.EISADevID = pDevNode->ProductId;
pDevice->info.pnp.BaseClass = pDevNode->DeviceType[0];
pDevice->info.pnp.SubClass = pDevNode->DeviceType[1];
pDevice->info.pnp.ProgIntf = pDevNode->DeviceType[2];
pDevice->info.pnp.CardSelNum = pDevNode->Node;
if ((PxeNicType.NicType == 3) &&
(PxeNicType.pci_pnp_info.pnp.EISA_Dev_ID == pDevNode->ProductId) &&
(PxeNicType.pci_pnp_info.pnp.CardSelNum == pDevNode->Node)) {
pRequest->PrimaryNicIndex = pRequest->DeviceCount + cDevices;
fNICFound = TRUE;
}
pCurr += pDevNode->Size;
lengthRemaining -= pDevNode->Size;
pDevice++;
}
pRequest->DeviceCount = pRequest->DeviceCount + cDevices;
}
//
// We better have found the primary NIC or the packet is invalid
//
if (!fNICFound) {
DBGPRINT(ERR, ("RAMDISK ERROR: Could not find the primary NIC\n"));
return ENODEV;
}
//
// The hal starts right after the array of device infos
//
pRequest->HalDataOffset = pRequest->DeviceOffset + (pRequest->DeviceCount * sizeof(DEVICE_INFO));
//
// Figure out which hal to use
//
HalName = SlDetectHal();
if (HalName == NULL) {
DBGPRINT(ERR, ("RAMDISK ERROR: Couldn't get the HAL name.\n"));
return ENODEV;
}
HalNameSize = strlen(HalName);
if (HalNameSize > (RequestLengthMax - pRequest->HalDataOffset)) {
DBGPRINT(ERR, ("RAMDISK ERROR: HAL name, %s, is too big for request, size = %d.\n",
HalName, RequestLengthMax - pRequest->HalDataOffset));
return ENOMEM;
}
//
// Copy over the hal
//
memcpy((PUCHAR)pRequest + pRequest->HalDataOffset, HalName, HalNameSize);
pRequest->HalDataLength = RESET_SIZE_AT_USHORT_MAX(HalNameSize);
//
// Return the length
//
*pLength = pRequest->HalDataOffset + pRequest->HalDataLength;
//
// Debug prints
//
if (DBGLVL(INFO)) {
DbgPrint("RAMDISK Build Request\n");
DbgPrint("Architecture = %d\n", pRequest->Architecture);
DbgPrint("MachineGuid = {%x-%x-%x-%x%x%x%x%x%x%x%x}\n",
pRequest->MachineGuid.Data1, pRequest->MachineGuid.Data2,
pRequest->MachineGuid.Data3,
pRequest->MachineGuid.Data4[0], pRequest->MachineGuid.Data4[1],
pRequest->MachineGuid.Data4[2], pRequest->MachineGuid.Data4[3],
pRequest->MachineGuid.Data4[4], pRequest->MachineGuid.Data4[5],
pRequest->MachineGuid.Data4[6], pRequest->MachineGuid.Data4[7]);
DbgPrint("ProductGuid = {%x-%x-%x-%x%x%x%x%x%x%x%x}\n",
pRequest->ProductGuid.Data1, pRequest->ProductGuid.Data2,
pRequest->ProductGuid.Data3,
pRequest->ProductGuid.Data4[0], pRequest->ProductGuid.Data4[1],
pRequest->ProductGuid.Data4[2], pRequest->ProductGuid.Data4[3],
pRequest->ProductGuid.Data4[4], pRequest->ProductGuid.Data4[5],
pRequest->ProductGuid.Data4[6], pRequest->ProductGuid.Data4[7]);
DbgPrint("HALName = %s\n", HalName);
DbgPrint("Flags = 0x%x\n", pRequest->Flags);
DbgPrint("DeviceCount = %d\n", pRequest->DeviceCount);
pDevice = (PDEVICE_INFO)( (PUCHAR)pRequest + pRequest->DeviceOffset );
for (i = 0; i < pRequest->DeviceCount; i++ ) {
CHAR DeviceString[128];
RamdiskDeviceInfoToString( pDevice, DeviceString );
DbgPrint("[%d] %s %s\n", i, DeviceString, (i == pRequest->PrimaryNicIndex? "PRIMARY NIC" : ""));
pDevice++;
}
}
return ESUCCESS;
}
ARC_STATUS
RamdiskSendDiscoverAndWait(
IN PBMBUILD_DISCOVER_PACKET Discover,
IN ULONG DiscoverSize,
IN ULONG Timeout
)
/*++
Routine Description:
This routine will send a discovery packet on the network in
accordance with the RamdiskDiscovery parameters. It will then
wait for the specified timeout period and collect the responses
from the build servers. It will add each of these servers to the
RamdiskServers list. If we don't get any responses in the timeout
period, we will return EIO.
Arguments:
Discover - Discover packet to send out
DiscoverSize - the size of the discover packet
Timeout - timeout interval
Return Value:
ESUCCESS - received at least one response from a build server
EIO - if we timed out waiting for a response from a build server
EINVAL - couldn't send the discover packet
--*/
{
ULONG waitStartTime;
BMBUILD_ACCEPT_PACKET accept;
PUCHAR address = (PUCHAR) &RamdiskDiscovery;
BOOLEAN receivedAccept;
ULONG length;
ULONG remoteHost;
USHORT remotePort;
ASSERT(RamdiskServerCount < RAMDISK_MAX_SERVERS);
ASSERT(RamdiskDiscovery != 0);
ASSERT(RamdiskBuildPort != 0);
//
// Send the discovery packet to the destination address
//
length = RomSendUdpPacket(Discover, DiscoverSize, RamdiskDiscovery, RamdiskBuildPort);
if (length != DiscoverSize) {
DBGPRINT(ERR, ("FAILED to send discovery packet to %u.%u.%u.%u:%u\n",
address[0], address[1], address[2], address[3],
SWAP_WORD(RamdiskBuildPort)));
return EINVAL;
}
DBGPRINT(INFO, ("Waiting for response (Timeout = %d secs).\n", Timeout));
//
// Wait for the responses. We will wait for the timeout period and
// collect the ACCEPT packets we get within this timeout.
//
waitStartTime = SysGetRelativeTime();
receivedAccept = FALSE;
while ((SysGetRelativeTime() - waitStartTime) < Timeout) {
length = RomReceiveUdpPacket(&accept, sizeof(accept), 0, &remoteHost, &remotePort);
if (length != 0) {
//
// Make sure the packet is one of the ones we expect.
//
if ((remoteHost == 0) || (remoteHost == 0xFFFFFFFF) || (RamdiskBuildPort != remotePort)) {
PUCHAR bad = (PUCHAR) &remoteHost;
//
// Recieved a packet from the wrong server/port
//
DBGPRINT(ERR, ("RamdiskSendDiscoverPacketAndWait: received an unexpected packet, "
"expected %u, received %u.%u.%u.%u:%u\n",
SWAP_WORD(RamdiskBuildPort),
bad[0], bad[1], bad[2], bad[3], SWAP_WORD(remotePort)));
} else if (length < sizeof(BMBUILD_ACCEPT_PACKET)) {
//
// Recieved a packet that's too small
//
DBGPRINT(ERR, ("RamdiskSendDiscoverPacketAndWait: packet size too small, %d\n", length));
} else if ((accept.Version != BMBUILD_PACKET_VERSION) ||
(accept.OpCode != BMBUILD_OPCODE_ACCEPT) ||
(accept.XID != Discover->XID)) {
//
// The packet is corrupt
//
address = (PUCHAR) &remoteHost;
DBGPRINT(ERR, ("RamdiskSendDiscoverPacketAndWait: expected ACCEPT with XID %d, "
"received Version %d, OpCode %d, XID %d from %u.%u.%u.%u:%u\n",
Discover->XID, accept.Version, accept.OpCode, accept.XID,
address[0], address[1], address[2], address[3], SWAP_WORD(remotePort)));
} else {
address = (PUCHAR) &remoteHost;
DBGPRINT(INFO, ("Received ACCEPT packet XID = %d from %u.%u.%u.%u:%u\n",
accept.XID, address[0], address[1], address[2], address[3],
SWAP_WORD(remotePort)));
//
// We have a valid packet from a build server. Add it to
// the list.
//
receivedAccept = TRUE;
ASSERT(RamdiskServerCount < RAMDISK_MAX_SERVERS);
RamdiskServers[RamdiskServerCount] = remoteHost;
RamdiskServerCount++;
//
// If we filled up the maximum number of servers
// then we are done
//
if (RamdiskServerCount == RAMDISK_MAX_SERVERS) {
break;
}
}
}
}
//
// Did we get anything
//
if (receivedAccept) {
return ESUCCESS;
} else {
address = (PUCHAR) &RamdiskDiscovery;
DBGPRINT(ERR, ("Timed out waiting for accepts using %u.%u.%u.%u:%u "
"(Timeout = %d secs).\n", address[0], address[1], address[2],
address[3], SWAP_WORD(RamdiskBuildPort), Timeout));
return EIO;
}
}
ARC_STATUS
RamdiskDiscoverBuildServer(
)
/*++
Routine Description:
This routine will discover a list of build server based on the
ramdisk build parameters listed in boot.ini. If we already have
a list of build server listed in boot.ini, we will use that list
as the possible build servers. If we get a response from any
build server, we will stop the discover stage and use the list
of servers we have heard back from as the list of possible build
servers to use. We will retry a number of times to get responses.
If we get no response after retrying, we will fail.
Arguments:
None
Return Value:
ESUCCESS - We have a list of build servers
EIO - We timed out waiting for responses from the build servers
otherwise, something else failed
--*/
{
ARC_STATUS status;
USHORT localPort;
BMBUILD_DISCOVER_PACKET discover;
ULONG discoverLength;
ULONG iRetry;
ULONG timeout;
ULONG lastProgressPercent = 0;
BOOLEAN forceDisplayFirstTime = TRUE;
ULONG currentProgressPercent;
PUCHAR address = (PUCHAR) &RamdiskDiscovery;
//
// Short-circuit discovery if we already have a list of servers
//
if (RamdiskServerCount > 0) {
ASSERT(RamdiskServers[0] != 0);
ASSERT(RamdiskServers[0] != 0xFFFFFFFF);
return ESUCCESS;
}
ASSERT(RamdiskDiscovery != 0);
//
// Grab an unused port
//
localPort = UdpAssignUnicastPort();
DBGPRINT(INFO, ("Sending builder discovers using port %d.\n", SWAP_WORD(localPort)));
//
// Create discover packet
//
status = RamdiskBuildDiscover(&discover, sizeof(discover), &discoverLength);
if (status != ESUCCESS) {
return status;
}
//
// Start the discovery. Note that this will be repeated a number
// of times to account for network congestion and load on the servers.
//
BlOutputStartupMsg(RAMDISK_BUILD_DISCOVER);
BlUpdateProgressBar(0);
timeout = RamdiskTimeout;
for (iRetry = 0; iRetry < RamdiskRetry; iRetry++) {
//
// Each Discover gets its own transaction ID.
//
discover.XID = ++RamdiskXID;
//
// update progress bar. this happens here since a timed out packet
// might take some time.
//
currentProgressPercent = (iRetry * 100) / RamdiskRetry;
if (forceDisplayFirstTime || (currentProgressPercent != lastProgressPercent)) {
BlUpdateProgressBar(currentProgressPercent);
forceDisplayFirstTime = FALSE;
}
lastProgressPercent = currentProgressPercent;
DBGPRINT(INFO, ("Sending discovery packet XID = %d to %u.%u.%u.%u:%u. "
"Retry %d out of %d. Timeout = %d\n", discover.XID,
address[0], address[1], address[2], address[3],
SWAP_WORD(RamdiskBuildPort), iRetry, RamdiskRetry, timeout));
status = RamdiskSendDiscoverAndWait(&discover, discoverLength, timeout);
if (status == ESUCCESS) {
//
// we found at least one server. we are done.
//
BlUpdateProgressBar(100);
return ESUCCESS;
}
//
// double the timeout, but max out at RAMDISK_MAX_TIMEOUT seconds
//
if ((timeout * 2) < RAMDISK_MAX_TIMEOUT) {
timeout = timeout * 2;
} else {
timeout = RAMDISK_MAX_TIMEOUT;
}
}
BlUpdateProgressBar(100);
return EIO;
}
ARC_STATUS
RamdiskVerifyResponse(
BMBUILD_RESPONSE_PACKET * Response,
ULONG ResponseSize,
ULONG XID
)
/*++
Routine Description:
This routine will verify that the response packet is valid.
Arguments:
Response - the response to validate
ResponseSize - the size of the response packet
XID - the XID this packet is supposed to contain
Return Value:
ESUCCESS - This packet is valid
EINVAL - The packet is invalid/misformatted
--*/
{
if (ResponseSize < BMBUILD_RESPONSE_FIXED_PACKET_LENGTH) {
//
// Recieved a packet that's too small
//
DBGPRINT(ERR, ("RamdiskVerifyResponse: packet size too small, %d\n", ResponseSize));
return EINVAL;
} else if ((Response->Version != BMBUILD_PACKET_VERSION) ||
(Response->OpCode != BMBUILD_OPCODE_RESPONSE) ||
(Response->XID != XID)) {
//
// The packet is corrupt
//
DBGPRINT(ERR, ("RamdiskVerifyResponse: expected RESPONSE with XID %d, "
"received Version %d, OpCode %d, XID %d\n",
XID, Response->Version, Response->OpCode, Response->XID));
return EINVAL;
} else {
switch (Response->Status) {
case BMBUILD_S_REQUEST_COMPLETE:
if ((Response->ImagePathOffset < BMBUILD_RESPONSE_FIXED_PACKET_LENGTH) ||
(Response->ImagePathLength == 0) ||
((ULONG)(Response->ImagePathOffset + Response->ImagePathLength) > ResponseSize)) {
//
// The packet is corrupt
//
DBGPRINT(ERR, ("RamdiskVerifyResponse: the image path isn't correctly "
"formatted. ImageOffset = %d, Imagelength = %d, PacketLength = %d.\n",
Response->ImagePathOffset, Response->ImagePathLength, ResponseSize));
return EINVAL;
}
break;
case BMBUILD_S_REQUEST_PENDING:
case BMBUILD_E_WRONGVERSION:
case BMBUILD_E_BUSY:
case BMBUILD_E_ACCESSDENIED:
case BMBUILD_E_ILLEGAL_OPCODE:
case BMBUILD_E_PRODUCT_NOT_FOUND:
case BMBUILD_E_BUILD_FAILED:
case BMBUILD_E_INVALID_PACKET:
//
// No specific checks for these status codes
//
break;
default:
//
// The packet is corrupt
//
DBGPRINT(ERR, ("RamdiskVerifyResponse: unexpected RESPONSE status %d.\n", Response->Status));
return EINVAL;
break;
}
}
return ESUCCESS;
}
ARC_STATUS
RamdiskSendRequestAndWait(
IN BMBUILD_REQUEST_PACKET * Request,
IN ULONG RequestSize,
IN ULONG BuilderAddress,
IN ULONG Timeout,
IN BMBUILD_RESPONSE_PACKET * Response,
IN ULONG ResponseSizeMax
)
/*++
Routine Description:
This routine will send a request packet to the specified server.
It will wait for the specified timeout period for a reply from
the server. On successful return, the response parameter will
contain a valid response.
Arguments:
Request - the request packet to send out
RequestSize - the size of the request packet
BuilderAddress - the builder server to send the request to
Timeout - the amount of time to wait for a response, in seconds
Response - the response packet that should be filled in
ResponseSizeMax - the maximum size of the response packet
Return Value:
ESUCCESS - the response packet was received from the server and is valid
EIO - we timed out waiting for a response from the server
EINVAL - we couldn't send the packet to the server
--*/
{
ULONG waitStartTime;
PUCHAR address = (PUCHAR) &BuilderAddress;
ULONG length;
ULONG remoteHost;
USHORT remotePort;
ASSERT(Request != NULL);
ASSERT(RequestSize != 0);
ASSERT(BuilderAddress != 0);
ASSERT(BuilderAddress != 0xFFFFFFFF);
ASSERT(Timeout != 0);
ASSERT(Response != NULL);
ASSERT(ResponseSizeMax != 0);
ASSERT(RamdiskBuildPort != 0);
//
// Send the discovery packet to the destination address
//
length = RomSendUdpPacket(Request, RequestSize, BuilderAddress, RamdiskBuildPort);
if (length != RequestSize) {
DBGPRINT(ERR, ("FAILED to send request packet to %u.%u.%u.%u:%u\n",
address[0], address[1], address[2], address[3],
SWAP_WORD(RamdiskBuildPort)));
return EINVAL;
}
DBGPRINT(INFO, ("Waiting for response (Timeout = %d secs).\n", Timeout));
//
// Wait for the responses. We will wait for the timeout period and
// select the best ACCEPT we get within this timeout. The best accept
// is the one with the lowest build time.
//
waitStartTime = SysGetRelativeTime();
while ((SysGetRelativeTime() - waitStartTime) < Timeout) {
length = RomReceiveUdpPacket(Response, ResponseSizeMax, 0, &remoteHost, &remotePort);
if (length != 0) {
//
// Make sure the packet is one of the ones we expect.
//
if ((BuilderAddress != remoteHost) || (RamdiskBuildPort != remotePort)) {
PUCHAR bad = (PUCHAR) &remoteHost;
PUCHAR good = (PUCHAR) &BuilderAddress;
//
// Recieved a packet from the wrong server/port
//
DBGPRINT(ERR, ("RamdiskSendRequest: received an unexpected packet, "
"expected %u.%u.%u.%u:%u, received %u.%u.%u.%u:%u\n",
good[0], good[1], good[2], good[3], SWAP_WORD(RamdiskBuildPort),
bad[0], bad[1], bad[2], bad[3], SWAP_WORD(remotePort)));
} else if (RamdiskVerifyResponse(Response, length, Request->XID) == ESUCCESS) {
PUCHAR good = (PUCHAR) &remoteHost;
DBGPRINT(INFO, ("Received RESPONSE packet (%d bytes) XID = %d status "
"= %d from %u.%u.%u.%u:%u.\n", length, Response->XID,
Response->Status, good[0], good[1], good[2],
good[3], SWAP_WORD(remotePort)));
return ESUCCESS;
}
}
}
address = (PUCHAR) &BuilderAddress;
DBGPRINT(ERR, ("Timed out waiting for a response from %u.%u.%u.%u:%u "
"(Timeout = %d secs).\n", address[0], address[1], address[2],
address[3], SWAP_WORD(RamdiskBuildPort), Timeout));
//
// We timed out
//
return EIO;
}
ARC_STATUS
RamdiskGetResponse(
BMBUILD_RESPONSE_PACKET * Response,
ULONG ResponseSizeMax)
/*++
Routine Description:
This routine will get a response that contains a valid image path and download
parameter from a server. This routine will use the RamdiskServers list to send
requests to until it receives a valid response indicating that the image has
been built. It will use the networking parameters specified in boot.ini.
Arguments:
Response - the response packet that should be contain the image path and
downloading parameters
ResponseSizeMax - the maximum size of the response packet
Return Value:
ESUCCESS - the response packet contains the image path and downloading parameters
EIO - we timed out waiting for a response from the server
EINVAL - we couldn't send the packet to the server
otherwise - something else stopped us from receiving a valid response
--*/
{
ARC_STATUS status;
BMBUILD_REQUEST_PACKET * request;
ULONG requestSize;
USHORT localPort;
ULONG timeout;
ULONG iRetry;
ULONG iServers;
PUCHAR address;
ULONG progressMax;
ULONG lastProgressPercent = 0;
BOOLEAN forceDisplayFirstTime = TRUE;
ULONG currentProgressPercent;
request = BlAllocateHeap(RamdiskMaxPacketSize);
if (request == NULL) {
DBGPRINT(ERR, ("Failed to allocate request packet of size %d.\n", RamdiskMaxPacketSize));
return ENOMEM;
}
//
// Grab an unused port
//
localPort = UdpAssignUnicastPort();
DBGPRINT(INFO, ("Sending builder requests using port %d.\n", SWAP_WORD(localPort)));
//
// Build request packet
//
status = RamdiskBuildRequest(request, RamdiskMaxPacketSize, &requestSize);
if (status != ESUCCESS) {
return status;
}
//
// We will be sending a maximum of RamdiskRetry request packets
// to RamdiskServerCount servers
//
progressMax = RamdiskServerCount * RamdiskRetry;
//
// Reset the progress information
//
BlOutputStartupMsg(RAMDISK_BUILD_REQUEST);
BlUpdateProgressBar(0);
DBGPRINT(INFO, ("Requesting appropriate image for this computer...\n"));
for (iServers = 0; iServers < RamdiskServerCount; iServers++) {
//
// Set our initial time out
//
timeout = RamdiskTimeout;
RamdiskPrintBuildProgress(RAMDISK_BUILD_PROGRESS, RamdiskServers[iServers]);
for (iRetry = 0; iRetry < RamdiskRetry; iRetry++) {
//
// allocate a new transaction ID for this session
//
request->XID = ++RamdiskXID;
//
// update progress bar. this happens here since a timed out packet
// might take some time.
//
currentProgressPercent = ((iServers * RamdiskRetry + iRetry) * 100) / progressMax;
if (forceDisplayFirstTime || (currentProgressPercent != lastProgressPercent)) {
BlUpdateProgressBar(currentProgressPercent);
forceDisplayFirstTime = FALSE;
}
lastProgressPercent = currentProgressPercent;
address = (PUCHAR) &(RamdiskServers[iServers]);
DBGPRINT(INFO, ("Sending request packet XID = %d to %u.%u.%u.%u:%u. "
"Retry %d out of %d. Timeout = %d\n", request->XID,
address[0], address[1], address[2], address[3],
SWAP_WORD(RamdiskBuildPort), iRetry, RamdiskRetry, timeout));
status = RamdiskSendRequestAndWait(request, requestSize, RamdiskServers[iServers],
timeout, Response, ResponseSizeMax);
if (status == ESUCCESS) {
//
// Now that we have a valid response, check to see what we are
// supposed to do with it. We assume that any validation was
// already done in RamdiskSendRequestAndWait
//
address = (PUCHAR) &(RamdiskServers[iServers]);
if (Response->Status == BMBUILD_S_REQUEST_COMPLETE) {
DBGPRINT(INFO, ("Request is complete from server %u.%u.%u.%u:%u.\n",
address[0], address[1], address[2], address[3],
SWAP_WORD(RamdiskBuildPort)));
BlUpdateProgressBar(100);
return ESUCCESS;
} else if (Response->Status == BMBUILD_S_REQUEST_PENDING) {
DBGPRINT(INFO, ("Request is pending. Instructed to wait for %d secs "
"by server %u.%u.%u.%u:%u.\n", Response->WaitTime,
address[0], address[1], address[2], address[3],
SWAP_WORD(RamdiskBuildPort)));
RamdiskPrintBuildProgress(RAMDISK_BUILD_PROGRESS_PENDING, RamdiskServers[iServers]);
RamdiskWait(Response->WaitTime);
} else if (Response->Status == BMBUILD_E_BUSY) {
DBGPRINT(INFO, ("Server %u.%u.%u.%u:%u is busy. Waiting for %d secs.\n",
address[0], address[1], address[2], address[3],
SWAP_WORD(RamdiskBuildPort), RamdiskTimeout));
RamdiskPrintBuildProgress(RAMDISK_BUILD_PROGRESS_ERROR, RamdiskServers[iServers]);
RamdiskWait(RAMDISK_UI_WAIT);
} else {
RamdiskPrintBuildProgress(RAMDISK_BUILD_PROGRESS_ERROR, RamdiskServers[iServers]);
RamdiskWait(RAMDISK_UI_WAIT);
//
// Try a different server
//
break;
}
} else if (status == EIO) {
RamdiskPrintBuildProgress(RAMDISK_BUILD_PROGRESS_TIMEOUT, RamdiskServers[iServers]);
RamdiskWait(RAMDISK_UI_WAIT);
//
// double the timeout, but max out at RAMDISK_MAX_TIMEOUT seconds
//
if ((timeout * 2) < RAMDISK_MAX_TIMEOUT) {
timeout = timeout * 2;
} else {
timeout = RAMDISK_MAX_TIMEOUT;
}
} else {
RamdiskPrintBuildProgress(RAMDISK_BUILD_PROGRESS_ERROR, RamdiskServers[iServers]);
RamdiskWait(RAMDISK_UI_WAIT);
//
// Try a different server
//
break;
}
}
}
//
// We completely timed out
//
BlUpdateProgressBar(100);
return EIO;
}
ARC_STATUS
RamdiskBuildAndInitialize(
)
/*++
Routine Description:
This routine will communicate with a build server to build
a ramdisk and obtain a RDPATH.
Arguments:
Return Value:
ESUCCESS - image was successfully built and we have a valid RDPATH
!ESUCCESS - we failed to build the image
--*/
{
ARC_STATUS status;
PBMBUILD_RESPONSE_PACKET response = NULL;
PUCHAR imagePath;
PUCHAR address;
//
// Set the max packet size. This is calculated from the
// MTU size of the network (1500 for Ethernet) minus
// the IP and UDP headers ( which account to 28 bytes ).
//
RamdiskMaxPacketSize = NetMaxTranUnit - 28;
ASSERT(RamdiskMaxPacketSize > 0);
response = BlAllocateHeap(RamdiskMaxPacketSize);
if (response == NULL) {
DBGPRINT(ERR, ("Failed to allocate response packet of size %d.\n", RamdiskMaxPacketSize));
return ENOMEM;
}
RtlZeroMemory(response, RamdiskMaxPacketSize);
//
// Discover build server
//
status = RamdiskDiscoverBuildServer();
if (status != ESUCCESS) {
goto Error;
}
//
// Get the response from the build server
//
status = RamdiskGetResponse(response, RamdiskMaxPacketSize);
if (status != ESUCCESS) {
goto Error;
}
ASSERT (RamdiskPath == NULL);
//
// Set the MTFTP options
//
RamdiskTFTPAddr = response->TFTPAddr.Address;
RamdiskMTFTPAddr = response->MTFTPAddr.Address;
RamdiskMTFTPCPort = response->MTFTPCPort;
RamdiskMTFTPSPort = response->MTFTPSPort;
RamdiskMTFTPTimeout = response->MTFTPTimeout;
RamdiskMTFTPDelay = response->MTFTPDelay;
RamdiskMTFTPFileSize = response->MTFTPFileSize;
RamdiskMTFTPChunkSize = response->MTFTPChunkSize;
//
// Set the image offset and length
//
RamdiskImageOffset = response->ImageFileOffset;
RamdiskImageLength = response->ImageFileSize;
imagePath = (PUCHAR)((ULONG_PTR)response + response->ImagePathOffset);
RamdiskPath = BlAllocateHeap(response->ImagePathLength + sizeof("net(0)\\"));
if (RamdiskPath == NULL) {
DBGPRINT(ERR, ("Failed to allocate memory for RamdiskPath size %d.\n",
response->ImagePathLength + sizeof("net(0)\\")));
return ENOMEM;
}
strcpy(RamdiskPath, "net(0)\\");
memcpy(RamdiskPath + sizeof("net(0)\\") - 1, imagePath, response->ImagePathLength);
RamdiskPath[sizeof("net(0)\\") + response->ImagePathLength - 1] = '\0';
if (DBGLVL(INFO)) {
DbgPrint("RDPATH = %s\n", RamdiskPath);
address = (PUCHAR) &RamdiskTFTPAddr;
DbgPrint("RDTFTPADDR = %u.%u.%u.%u\n", address[0], address[1], address[2], address[3]);
address = (PUCHAR) &RamdiskMTFTPAddr;
DbgPrint("RDMTFTPADDR = %u.%u.%u.%u\n", address[0], address[1], address[2], address[3]);
DbgPrint("RDMTFTPCPORT = %d\n", SWAP_WORD( RamdiskMTFTPCPort));
DbgPrint("RDMTFTPSPORT = %d\n", SWAP_WORD( RamdiskMTFTPSPort));
DbgPrint("RDMTFTPDELAY = %d\n", RamdiskMTFTPDelay);
DbgPrint("RDMTFTPTIMEOUT = %d\n", RamdiskMTFTPTimeout);
DbgPrint("RDFILESIZE = 0x%0I64x bytes\n", RamdiskMTFTPFileSize);
DbgPrint("RDCHUNKSIZE = 0x%0I64x bytes\n", RamdiskMTFTPChunkSize);
DbgPrint("RDIMAGEOFFSET = 0x%x bytes\n", RamdiskImageOffset);
DbgPrint("RDIMAGELENGTH = 0x%0I64x bytes\n", RamdiskImageLength);
}
return ESUCCESS;
Error:
DBGPRINT(ERR, ("RamdiskBuildAndInitialize: Failed, %d.\n", status));
//
// We should be rebooting the machine here
//
return status;
}
#endif
VOID
RamdiskFatalError(
IN ULONG Message1,
IN ULONG Message2
)
/*++
Routine Description:
This function looks up a message to display at a error condition.
Arguments:
Message - message that describes the class of problem.
Return Value:
none
--*/
{
PTCHAR Text;
TCHAR Buffer[40];
ULONG Count;
BlClearScreen();
Text = BlFindMessage(Message1);
if (Text == NULL) {
_stprintf(Buffer,TEXT("%08lx\r\n"),Message1);
Text = Buffer;
}
ArcWrite(BlConsoleOutDeviceId,
Text,
(ULONG)_tcslen(Text)*sizeof(TCHAR),
&Count);
Text = BlFindMessage(Message2);
if (Text == NULL) {
_stprintf(Buffer,TEXT("%08lx\r\n"),Message2);
Text = Buffer;
}
ArcWrite(BlConsoleOutDeviceId,
Text,
(ULONG)_tcslen(Text)*sizeof(TCHAR),
&Count);
#if defined(ENABLE_LOADER_DEBUG) || DBG
#if (defined(_X86_) || defined(_ALPHA_) || defined(_IA64_)) && !defined(ARCI386) // everything but ARCI386
if(BdDebuggerEnabled) {
DbgBreakPoint();
}
#endif
#endif
return;
}
#if defined(_X86_)
VOID
RamdiskSdiBoot(
IN PCHAR SdiFile
)
{
ARC_STATUS status;
PSDI_HEADER sdiHeader;
PUCHAR startromAddress;
ULONG startromLength;
BOOLEAN OldShowProgressBar;
LONGLONG availableLength;
//
// Read the SDI image into memory.
//
RamdiskTFTPAddr = NetServerIpAddress;
RamdiskImageOffset = 0;
RamdiskImageLength = 0;
OldShowProgressBar = BlShowProgressBar;
BlShowProgressBar = TRUE;
status = RamdiskReadImage( SdiFile );
if ( status != ESUCCESS ) {
RamdiskFatalError( RAMDISK_GENERAL_FAILURE,
RAMDISK_BOOT_FAILURE );
return;
}
BlShowProgressBar = OldShowProgressBar;
//
// Copy startrom.com from the SDI image to 0x7c00.
//
sdiHeader = MapRamdisk( 0, &availableLength );
ASSERT( availableLength >= sizeof(SDI_HEADER) );
ASSERT( availableLength >=
(sdiHeader->liBootCodeOffset.QuadPart + sdiHeader->liBootCodeSize.QuadPart) );
ASSERT( sdiHeader->liBootCodeOffset.HighPart == 0 );
ASSERT( sdiHeader->liBootCodeSize.HighPart == 0 );
startromAddress = (PUCHAR)sdiHeader + sdiHeader->liBootCodeOffset.LowPart;
startromLength = sdiHeader->liBootCodeSize.LowPart;
RtlMoveMemory( (PVOID)0x7c00, startromAddress, startromLength );
//
// Shut down PXE.
//
if ( BlBootingFromNet ) {
NetTerminate();
}
//
// Inform boot debugger that the boot phase is complete.
//
#if defined(ENABLE_LOADER_DEBUG) || DBG
#if (defined(_X86_) || defined(_ALPHA_)) && !defined(ARCI386)
{
if (BdDebuggerEnabled == TRUE) {
DbgUnLoadImageSymbols(NULL, (PVOID)-1, 0);
}
}
#endif
#endif
REBOOT( (ULONG)sdiHeader | 3 );
return;
}
#endif