/*++ 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" #if defined(POST_XPSP) #include "bmbuild.h" #endif #include "ntexapi.h" #include "haldtect.h" #include "pci.h" #include "pbios.h" #include "bldr.h" #include // // 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 PCI_ITERATOR_IS_VALID(i) (i & 0x8000) #define PCI_ITERATOR_TO_BUS(i) (UCHAR)(((i) >> 8) & 0x7f) #define PCI_ITERATOR_TO_DEVICE(i) (UCHAR)(((i) >> 3) & 0x1f) #define PCI_ITERATOR_TO_FUNCTION(i) (UCHAR)(((i) >> 0) & 0x7) #define PCI_TO_ITERATOR(b,d,f) ((USHORT)(0x8000 | ((b)<<8) | ((d)<<3) | (f))) // // PCI Device struct as persisted in registry by ntdetect.com // #include typedef struct _PCIDEVICE { USHORT BusDevFunc; PCI_COMMON_CONFIG Config; } PCIDEVICE, *PPCIDEVICE; #include // // 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_DISCOVERY_MULTICAST 0x00000001 #define RAMDISK_DISCOVERY_BROADCAST 0x00000002 #define RAMDISK_DISCOVERY_UNICAST 0x00000004 #define RAMDISK_DISCOVERY_RESTRICT 0x00000008 GUID RamdiskGuid = {0,0,0,0}; ULONG RamdiskDiscovery = 0; ULONG RamdiskMCastAddr = 0; // network byte order ULONG RamdiskServerCount = 0; ULONG RamdiskServers[RAMDISK_MAX_SERVERS]; // network byte order USHORT RamdiskTimeout = 15; 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(POST_XPSP) #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 #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 ) { 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 >> PAGE_SHIFT )); } ArcClose( RamdiskDeviceId ); if ( ntStatus == STATUS_INSUFFICIENT_RESOURCES ) { return ENOMEM; } return EROFS; } RamdiskBasePage = (ULONG)((ULONG_PTR)request.MemoryAddress >> 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 physicalAddressOfOffset; 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)); 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 // physicalAddressOfOffset = ((LONGLONG)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, physicalAddressOfOffset )); // // the high 32 bits are going to be lost when calling RomMtftpReadFile. // find out now, if this is happening // ASSERT( (physicalAddressOfOffset >> 32) == 0 ); status = RomMtftpReadFile ( (PUCHAR)p, (PVOID)(ULONG)physicalAddressOfOffset, (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 ); 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 physicalAddressOfOffset; physicalAddressOfOffset = ((LONGLONG)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( (physicalAddressOfOffset >> 32) == 0 ); return (PVOID)(ULONG)physicalAddressOfOffset; #else return (PVOID)physicalAddressOfOffset; #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 RDBUILD 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 - Describes how we should discover the build server. Possible values include: M - A packet will be sent on the multicast address specified by /RDMCASTADDR. The first server to respond will be used. If /RDMCASTADDR is not specified multicast discovery is ignored. B - A packet will be broadcasted. First server to respond will be used. This is the default. U - A packet will be unicast to all the servers specified in the /RMSERVERS. Or if /RMSERVERS is not specified to the PXE Boot server. R - Will only use servers that are included in the /RMSERVERS. This option is used to filter servers when specified in conjunction with broadcast and Multicast discovery. It can also be used with unicast discovery to specify a list of servers to use. If more than one discovery option is specified then exactly one method will be selected according in the following order: 1) Multicast - M 2) Broadcast - B (Default) 3) Unicast U Examples: /RDDISCOVERY=U This will send a unicast packet to the same server as the PXE Boot server. /RDDISCOVERY=M /RDMCASTADDR=204.1.1.1 This will send a multicast packet to the address sepcified. /RDDISCOVERY=MBU /RDMCASTADDR=204.1.1.1 This will use multicast discovery to the address specified. If no boot server responds within the timeout period. Note that Broadcast and Unicast discovery are ignored. /RDDISCOVERY=BR /RDSERVERS={10.0.0.3, 10.0.0.4} This will send a broadcast packet but will only accept responses from 10.0.0.3 or 10.0.0.4 . /RDDISCOVERY=UR /RDSERVERS={10.0.0.3, 10.0.0.4} A discovery packet will be sent to the two servers specified and one of them will be selected. /RDMCASTADDR Specifies the Multicast address to use for Build Server multicast discovery. /RDSERVERS Specifies a list of Build Servers to accpet responses from. A maximum of 10 servers are supported. Example: /RDSERVERS={10.0.0.3, 10.0.0.4} /RDTIMEOUT Specifies the timeout period to wait for a response in seconds. Default is 10 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; PCHAR 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 = (PCHAR) &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; } #if defined(POST_XPSP) // // 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, "RDMCASTADDR" ); if ( value ) RamdiskMCastAddr = RamdiskParseIPAddr( value ); value = RamdiskGetOptionValue( LoadOptions, "RDSERVERS" ); if ( value && *value == '{' ) { PCHAR e = strchr( value, '}' ); p = value; if ( e && (ULONG)(e - p) > 7 ) { // at least seven characters for X.X.X.X while ( p && p < e && RamdiskServerCount < RAMDISK_MAX_SERVERS) { RamdiskServers[RamdiskServerCount] = RamdiskParseIPAddr( p + 1 ); RamdiskServerCount++; p = strchr( p + 1, ',' ); } } } value = RamdiskGetOptionValue( LoadOptions, "RDDISCOVERY" ); if ( value ) { // NOTE : order of these checks is important since // they override each other. if ( strchr( value, 'U' ) ) { RamdiskDiscovery = RAMDISK_DISCOVERY_UNICAST; } if ( strchr( value, 'B' ) ) { RamdiskDiscovery = RAMDISK_DISCOVERY_BROADCAST; } if ( strchr( value, 'M' ) && RamdiskMCastAddr != 0 ) { RamdiskDiscovery = RAMDISK_DISCOVERY_MULTICAST; } if ( strchr( value, 'R' ) && RamdiskServerCount > 0 ) { RamdiskDiscovery |= RAMDISK_DISCOVERY_RESTRICT; } } else { // default is broadcast discovery RamdiskDiscovery = RAMDISK_DISCOVERY_BROADCAST; } value = RamdiskGetOptionValue( LoadOptions, "RDTIMEOUT" ); if (value) RamdiskTimeout = (USHORT)atoi( value ); value = RamdiskGetOptionValue( LoadOptions, "RDRETRY" ); if (value) RamdiskRetry = (USHORT)atoi( value ); // // Normalize options // if ( !TEST_BIT( RamdiskDiscovery, RAMDISK_DISCOVERY_RESTRICT) && RamdiskServerCount > 0 ) { RamdiskServerCount = 0; } if ( RamdiskDiscovery == RAMDISK_DISCOVERY_UNICAST ) { RamdiskServerCount = 1; RamdiskServers[0] = NetServerIpAddress; } // // 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]); DbgPrint("RDDISCOVERY = %d\n", RamdiskDiscovery); p = (PCHAR) &RamdiskMCastAddr; DbgPrint("RDMCASTADDR = %u.%u.%u.%u\n", p[0], p[1], p[2], p[3]); DbgPrint("RDSERVERS = %d\n", RamdiskServerCount); for (i = 0; i < RamdiskServerCount; i++) { p = (PCHAR) &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); } } #endif return ESUCCESS; } #if defined(POST_XPSP) #if defined(i386) // RDBUILD is only supported on x86 machines for now VOID RamdiskDeviceInfoToString( PDEVICE_INFO pDevice, PCHAR DeviceString ) /*++ Routine Description: This routine generates a string representation of the Device info for debugging purposes. Arguments: pDefive - 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 (pDevice->DeviceType == BMBUILD_DEVICE_TYPE_PCI) { sprintf ( DeviceString, "%d.%d.%d PCI\\VEN_%04X&DEV_%04X&SUBSYS_%04X%04X&REV_%02X&CC_%02X%02X%02X", PCI_ITERATOR_TO_BUS( pDevice->info.pci.BusDevFunc ), PCI_ITERATOR_TO_DEVICE( pDevice->info.pci.BusDevFunc ), PCI_ITERATOR_TO_FUNCTION( pDevice->info.pci.BusDevFunc ), pDevice->info.pci.VendorID, pDevice->info.pci.DeviceID, pDevice->info.pci.SubDeviceID, pDevice->info.pci.SubVendorID, pDevice->info.pci.RevisionID, pDevice->info.pci.BaseClass, pDevice->info.pci.SubClass, pDevice->info.pci.ProgIntf ); } else if (pDevice->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", PCI_ITERATOR_TO_BUS( pDevice->info.pci_bridge.BusDevFunc ), PCI_ITERATOR_TO_DEVICE( pDevice->info.pci_bridge.BusDevFunc ), PCI_ITERATOR_TO_FUNCTION( pDevice->info.pci_bridge.BusDevFunc ), pDevice->info.pci_bridge.VendorID, pDevice->info.pci_bridge.DeviceID, pDevice->info.pci_bridge.RevisionID, pDevice->info.pci_bridge.BaseClass, pDevice->info.pci_bridge.SubClass, pDevice->info.pci_bridge.ProgIntf, pDevice->info.pci_bridge.PrimaryBus, pDevice->info.pci_bridge.SecondaryBus, pDevice->info.pci_bridge.SubordinateBus ); } else if (pDevice->DeviceType == BMBUILD_DEVICE_TYPE_PNP) { CHAR ProductIDStr[8]; PUCHAR id = (PUCHAR)&pDevice->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, "%s CC_%02X%02X%02X", ProductIDStr, pDevice->info.pnp.BaseClass, pDevice->info.pnp.SubClass, pDevice->info.pnp.ProgIntf ); } } ARC_STATUS RamdiskBuildRequest( IN PBMBUILD_REQUEST_PACKET pRequest ) /*++ Routine Description: This routine will form the build request packet. Arguments: pRequest - a pointer to a buffer that will hold the request Return Value: ESUCCESS - read completed successfully !ESUCCESS - read failed --*/ { 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 x; PCHAR HalName = NULL; BOOLEAN fNICFound = FALSE; pRequest->Version = BMBUILD_PACKET_VERSION; pRequest->OpCode = BMBUILD_OPCODE_REQUEST; pRequest->Length = BMBUILD_REQUEST_FIXED_PACKET_LENGTH; // // Get the SMBIOS UUID (or PXE MAC address) // GetGuid( (PUCHAR*)&pRequest->MachineGuid, &x ); ASSERT( x == sizeof( pRequest->MachineGuid ) ); memcpy( &pRequest->ProductGuid, &RamdiskGuid, sizeof( GUID ) ); #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 HalName = SlDetectHal(); ASSERT( HalName != NULL ); strcpy( (PCHAR) pRequest->Data, HalName ); pRequest->HalDataOffset = BMBUILD_FIELD_OFFSET(BMBUILD_REQUEST_PACKET, Data); pRequest->Flags = 0; pRequest->DeviceCount = 0; pRequest->DeviceOffset = RESET_SIZE_AT_USHORT_MAX((ULONG)pRequest->HalDataOffset + strlen( HalName ) + 1); 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 ); 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( RamdiskMaxPacketSize ) ) { 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 = 0; 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 = 0; 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 & 0x7FFF ) ) { 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; } // // Set the packet length // pRequest->Length += pRequest->DeviceCount * sizeof( DEVICE_INFO ); ASSERT ( pRequest->Length <= RamdiskMaxPacketSize ); // // 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 RamdiskSendDiscoverPacketAndWait( IN PBMBUILD_DISCOVER_PACKET pDiscover, IN ULONG DestinationAddress, IN ULONG Timeout, IN BOOLEAN fExcludeServers, IN ULONG DiscoveryStartTime, IN ULONG TotalExpectedWaitTime, OUT PULONG pBuildServerChosen ) /*++ Routine Description: This routine will send a discovery packet on the network in accordance with the RamdiskDiscovery paramters. It will then select wait for a timeout period for replies and select the best response. Arguments: pDiscover - Discover packet to send out DestinationAddress - destination address of the discovery packet Timeout - timeout interval fExcludeServers - if TRUE the response are filtered through the server list DiscoveryStartTime - time when discovery was started TotalExpectedWaitTime - total time we expect to be in discovery. for updating the progress bar pBuildServerChosen - pointer to storage that will hold the chosen server Return Value: ESUCCESS - successfully selected a boot server !ESUCCESS - no build server chosen --*/ { ULONG WaitStartTime; BMBUILD_ACCEPT_PACKET Accept; PUCHAR p = (PUCHAR) &DestinationAddress; ULONG lastProgressPercent = 0; BOOLEAN ForceDisplayFirstTime = TRUE; ULONG currentProgressPercent; USHORT BestBuildTime = 0xFFFF; USHORT iServer; ULONG Length; ULONG RemoteHost = 0; USHORT RemotePort = 0; // // No server chosen initially // *pBuildServerChosen = 0; // // Send the discovery packet to the destination address // if ( RomSendUdpPacket( (PVOID)pDiscover, sizeof( BMBUILD_DISCOVER_PACKET ), DestinationAddress, BMBUILD_SERVER_PORT ) != sizeof( BMBUILD_DISCOVER_PACKET ) ) { DBGPRINT(ERR, ("FAILED to send discovery packet to %u.%u.%u.%u:%u\n", p[0], p[1], p[2], p[3], SWAP_WORD( BMBUILD_SERVER_PORT ))); // // update progress bar. this happens here since a timed out packet // might take some time. // currentProgressPercent = ((SysGetRelativeTime() - DiscoveryStartTime ) * 100) / TotalExpectedWaitTime; if ( ForceDisplayFirstTime || (currentProgressPercent != lastProgressPercent) ) { BlUpdateProgressBar( currentProgressPercent ); ForceDisplayFirstTime = FALSE; } lastProgressPercent = currentProgressPercent; return EINVAL; } DBGPRINT(INFO, ("Sent discovery packet to %u.%u.%u.%u:%u\n", p[0], p[1], p[2], p[3], SWAP_WORD( BMBUILD_SERVER_PORT ))); // // 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( (PVOID)&Accept, sizeof(Accept), 0, &RemoteHost, &RemotePort); if ( Length == sizeof( BMBUILD_ACCEPT_PACKET ) && RemotePort == BMBUILD_SERVER_PORT && Accept.Version == BMBUILD_PACKET_VERSION && Accept.OpCode == BMBUILD_OPCODE_ACCEPT && Accept.Length == sizeof( BMBUILD_ACCEPT_PACKET ) - BMBUILD_COMMON_PACKET_LENGTH && Accept.XID == pDiscover->XID ) { ULONG AcceptedBuildServer = RemoteHost; ASSERT( RemoteHost != 0 ); ASSERT( RemoteHost != 0xFFFFFFFF ); p = (PUCHAR) &RemoteHost; DBGPRINT(INFO, ("Received ACCEPT packet XID = %d from %u.%u.%u.%u:%u\n", Accept.XID, p[0], p[1], p[2], p[3], SWAP_WORD( BMBUILD_SERVER_PORT ))); // // Exclude servers if we were asked to // if ( fExcludeServers ) { for (iServer = 0; iServer < RamdiskServerCount; iServer++) { if (RemoteHost == RamdiskServers[iServer]) { p = (PUCHAR) &RemoteHost; DBGPRINT(INFO, ("Ignoring ACCEPT packet from %u.%u.%u.%u:%u\n", p[0], p[1], p[2], p[3], SWAP_WORD( BMBUILD_SERVER_PORT ))); AcceptedBuildServer = 0; break; } } } // // We have a valid packet from a build server. Check // if it is the best one. "Best" is determined by // the first server to respond with the lowest // builtime. // if ( AcceptedBuildServer != 0 && Accept.BuildTime < BestBuildTime) { p = (PUCHAR) &AcceptedBuildServer; DBGPRINT(INFO, ("We picked server at %u.%u.%u.%u:%u for this build\n", p[0], p[1], p[2], p[3], SWAP_WORD( BMBUILD_SERVER_PORT ))); *pBuildServerChosen = AcceptedBuildServer; return ESUCCESS; } } // // update progress bar // currentProgressPercent = ((SysGetRelativeTime() - DiscoveryStartTime ) * 100) / TotalExpectedWaitTime; if ( ForceDisplayFirstTime || (currentProgressPercent != lastProgressPercent) ) { BlUpdateProgressBar( currentProgressPercent ); ForceDisplayFirstTime = FALSE; } lastProgressPercent = currentProgressPercent; } return ENOENT; } ARC_STATUS RamdiskDiscoverBuildServer( OUT PULONG BuildServerIpAddress ) /*++ Routine Description: This routine will send a discovery packet on the network in accordance with the RamdiskDiscovery paramters. It will then select a acceptance from one of the build servers that respond and return its IP Address. Arguments: BuildServerIpAddress - returned Build server IP address Return Value: ESUCCESS - successfully selected a boot server !ESUCCESS - no build server responded --*/ { #define DISCOVER_RETRIES 4 // 4 retries #define DISCOVER_TIMEOUT 4 // starting at 4 then 8, 16, and 32 #define DISCOVER_TOTAL_TIMEOUT_TIME (4+8+16+32) // total time that we could be waiting for responses #define DISCOVER_SEND_TIMEOUT 3 // Time it takes to send a packet to an invalid address BMBUILD_DISCOVER_PACKET Discover; BOOLEAN fExcludeServers = FALSE; ULONG iRetry; ULONG cRetries = DISCOVER_RETRIES; ULONG Timeout = DISCOVER_TIMEOUT; ULONG x; ULONG DestinationAddress = 0xFFFFFFFF; ULONG DiscoveryStartTime = SysGetRelativeTime(); ULONG TotalExpectedWaitTime; USHORT iServer; ASSERT( BuildServerIpAddress ); *BuildServerIpAddress = 0; // // Short-circuit discovery if we are unicasting to one server // if ( RamdiskDiscovery == RAMDISK_DISCOVERY_UNICAST && RamdiskServerCount == 1 ) { ASSERT( RamdiskServers[0] != 0 ); ASSERT( RamdiskServers[0] != 0xFFFFFFFF ); *BuildServerIpAddress = RamdiskServers[0]; return ESUCCESS; } // // Fill in Discover packet // Discover.Version = BMBUILD_PACKET_VERSION; Discover.OpCode = BMBUILD_OPCODE_DISCOVER; Discover.Length = sizeof( BMBUILD_DISCOVER_PACKET ) - BMBUILD_COMMON_PACKET_LENGTH; GetGuid( (PUCHAR*)&Discover.MachineGuid, &x ); ASSERT( x == sizeof( Discover.MachineGuid ) ); memcpy( &Discover.ProductGuid, &RamdiskGuid, sizeof( GUID ) ); // // 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); // // If we are doing multicast or broadcast discovery // if ( TEST_BIT( RamdiskDiscovery, RAMDISK_DISCOVERY_BROADCAST ) || TEST_BIT( RamdiskDiscovery, RAMDISK_DISCOVERY_MULTICAST ) ) { TotalExpectedWaitTime = DISCOVER_TOTAL_TIMEOUT_TIME; for (iRetry = 0; iRetry < cRetries; iRetry++) { // Each Discover / Accpet gets its own transaction ID. Discover.XID = ++RamdiskXID; DBGPRINT(INFO, ("Sending Discovery packet XID = %d. Retry %d out of %d. Timeout = %d\n", Discover.XID, iRetry, cRetries, Timeout)); if ( TEST_BIT( RamdiskDiscovery, RAMDISK_DISCOVERY_MULTICAST) ) { DestinationAddress = RamdiskMCastAddr; } else if ( TEST_BIT( RamdiskDiscovery, RAMDISK_DISCOVERY_BROADCAST ) ) { DestinationAddress = 0xFFFFFFFF; } if ( TEST_BIT( RamdiskDiscovery, RAMDISK_DISCOVERY_RESTRICT ) ) { ASSERT( RamdiskServerCount > 0 ); fExcludeServers = TRUE; } if ( RamdiskSendDiscoverPacketAndWait( &Discover, DestinationAddress, Timeout, fExcludeServers, DiscoveryStartTime, TotalExpectedWaitTime, BuildServerIpAddress ) == ESUCCESS ) { // we found a server. we are done. BlUpdateProgressBar( 100 ); return ESUCCESS; } // double the timeout Timeout = Timeout * 2; } } else { // // We are doing Unicast discovery to a fixed list of server addresses // in the order that they appear in the list. first to respond within // the timeout period wins // TotalExpectedWaitTime = RamdiskServerCount * ( RamdiskTimeout + DISCOVER_SEND_TIMEOUT ); for (iServer = 0; iServer < RamdiskServerCount; iServer++) { // Each Discover / Accpet gets its own transaction ID. Discover.XID = ++RamdiskXID; if (RamdiskSendDiscoverPacketAndWait( &Discover, RamdiskServers[iServer], RamdiskTimeout, FALSE, DiscoveryStartTime, TotalExpectedWaitTime, BuildServerIpAddress ) == ESUCCESS ) { // we found a server. we are done. BlUpdateProgressBar( 100 ); return ESUCCESS; } } } BlUpdateProgressBar( 100 ); return EINVAL; } VOID RamdiskWait( ULONG WaitTime ) { ULONG startTime = SysGetRelativeTime(); while ( (SysGetRelativeTime() - startTime) < WaitTime ) { } } VOID RamdiskPrintBuildProgress( ULONG uMsgId, ULONG BuildServerIpAddress ) { PUCHAR p; PTCHAR FormatString = NULL; TCHAR Buffer[256]; // // print progress message // p = (PUCHAR) &BuildServerIpAddress; FormatString = BlFindMessage( uMsgId ); if ( FormatString != NULL ) { _stprintf(Buffer, FormatString, p[0], p[1], p[2], p[3] ); BlOutputTrailerMsgStr( Buffer ); } } 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 - read completed successfully !ESUCCESS - read failed --*/ { ARC_STATUS status; PBMBUILD_REQUEST_PACKET pRequest = NULL; PBMBUILD_RESPONSE_PACKET pResponse = NULL; USHORT iRetry = 0; ULONG Length; ULONG RemoteHost = 0; USHORT RemotePort = 0; PUCHAR p; BOOLEAN fSuccess = FALSE; ULONG BuildServerIpAddress = 0; // // Set the max packet size. This is calculate 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 ); pRequest = BlAllocateHeap( RamdiskMaxPacketSize ); if ( pRequest == NULL ) { DBGPRINT(ERR, ("Failed to allocate request packet of size %d.\n", RamdiskMaxPacketSize)); return ENOMEM; } pResponse = BlAllocateHeap( RamdiskMaxPacketSize ); if ( pRequest == NULL ) { DBGPRINT(ERR, ("Failed to allocate request packet of size %d.\n", RamdiskMaxPacketSize)); return ENOMEM; } memset(pRequest, 0, RamdiskMaxPacketSize); memset(pResponse, 0, RamdiskMaxPacketSize); // // All packets sent from client will go from the client port // RomSetReceiveStatus( BMBUILD_CLIENT_PORT ); // // Discover build server // status = RamdiskDiscoverBuildServer( &BuildServerIpAddress ); if (status != ESUCCESS ) { goto Error; } // // Build request packet // status = RamdiskBuildRequest( pRequest ); if ( status != ESUCCESS ) { goto Error; } // // Start communication with boot server. Display the text progress // bar when booting of a ramdisk // BlOutputStartupMsg(RAMDISK_BUILD_REQUEST); BlUpdateProgressBar(0); RamdiskPrintBuildProgress( RAMDISK_BUILD_PROGRESS, BuildServerIpAddress ); DBGPRINT(INFO, ("Requesting appropriate image for this computer...\n")); while ( iRetry < RamdiskRetry ) { BlUpdateProgressBar( (iRetry * 100) / RamdiskRetry ); Length = pRequest->Length + BMBUILD_COMMON_PACKET_LENGTH; // allocate a new transaction ID for this session pRequest->XID = ++RamdiskXID; p = (PUCHAR) &NetServerIpAddress; DBGPRINT(INFO, ( "Sending request packet (%d bytes) XID = %d to %u.%u.%u.%u:%u.\n", Length, pRequest->XID, p[0], p[1], p[2], p[3], SWAP_WORD( BMBUILD_SERVER_PORT ) ) ); if ( RomSendUdpPacket( (PVOID)pRequest, Length, BuildServerIpAddress, BMBUILD_SERVER_PORT ) != Length ) { RamdiskWait( RamdiskTimeout ); iRetry++; RamdiskPrintBuildProgress( RAMDISK_BUILD_PROGRESS_ERROR, BuildServerIpAddress ); continue; } DBGPRINT(INFO, ( "Waiting for response (Timeout = %d secs).\n", RamdiskTimeout ) ); Length = RomReceiveUdpPacket( (PVOID)pResponse, RamdiskMaxPacketSize, RamdiskTimeout, &RemoteHost, &RemotePort); if ( Length == 0 ) { DBGPRINT(INFO, ( "Receive timed out.\n") ); iRetry++; RamdiskPrintBuildProgress( RAMDISK_BUILD_PROGRESS_TIMEOUT, BuildServerIpAddress ); continue; } if ( Length < BMBUILD_RESPONSE_FIXED_PACKET_LENGTH - BMBUILD_COMMON_PACKET_LENGTH || RemoteHost != BuildServerIpAddress || RemotePort != BMBUILD_SERVER_PORT || pResponse->OpCode != BMBUILD_OPCODE_RESPONSE || pResponse->Version != BMBUILD_PACKET_VERSION || pResponse->XID != pRequest->XID ) { p = (PUCHAR) &RemoteHost; DBGPRINT(INFO, ("Received invalid packet OpCode = %d Length = %d Version = %d XID = %d from %u.%u.%u.%u:%u\n", pResponse->OpCode, pResponse->Length, pResponse->Version, pResponse->XID, p[0], p[1], p[2], p[3], SWAP_WORD( RemotePort ) ) ); RamdiskWait( RamdiskTimeout ); iRetry++; RamdiskPrintBuildProgress( RAMDISK_BUILD_PROGRESS_ERROR, BuildServerIpAddress ); continue; } p = (PUCHAR) &RemoteHost; DBGPRINT(INFO, ("Received VALID packet OpCode = %d Length = %d Version = %d XID = %d from %u.%u.%u.%u:%u\n", pResponse->OpCode, pResponse->Length, pResponse->Version, pResponse->XID, p[0], p[1], p[2], p[3], SWAP_WORD( RemotePort )) ); if ( BMBUILD_IS_E( pResponse->Error ) ) { DBGPRINT(INFO, ("Request Failed. Error = %x\n", pResponse->Error) ); RamdiskWait( RamdiskTimeout ); iRetry++; RamdiskPrintBuildProgress( RAMDISK_BUILD_PROGRESS_PENDING, BuildServerIpAddress ); continue; } if ( pResponse->Error == BMBUILD_S_REQUEST_PENDING ) { DBGPRINT(INFO, ("Request is pending. Instructed to wait for %d secs.\n", pResponse->WaitTime ) ); RamdiskWait( pResponse->WaitTime ); RamdiskPrintBuildProgress( RAMDISK_BUILD_PROGRESS_ERROR, BuildServerIpAddress ); continue; } ASSERT( BMBUILD_S ( pResponse->Error ) ); fSuccess = TRUE; break; } if (fSuccess) { ASSERT ( RamdiskPath == NULL ); RamdiskPrintBuildProgress( RAMDISK_BUILD_PROGRESS, BuildServerIpAddress ); BlUpdateProgressBar( 100 ); // // Set the MTFTP options // RamdiskTFTPAddr = pResponse->TFTPAddr.Address; RamdiskMTFTPAddr = pResponse->MTFTPAddr.Address; RamdiskMTFTPCPort = pResponse->MTFTPCPort; RamdiskMTFTPSPort = pResponse->MTFTPSPort; RamdiskMTFTPTimeout = pResponse->MTFTPTimeout; RamdiskMTFTPDelay = pResponse->MTFTPDelay; RamdiskMTFTPFileSize = pResponse->MTFTPFileSize; RamdiskMTFTPChunkSize = pResponse->MTFTPChunkSize; // // Set the image offset and length // RamdiskImageOffset = pResponse->ImageFileOffset; RamdiskImageLength = pResponse->ImageFileLength; p = (PUCHAR)((ULONG_PTR)pResponse + pResponse->ImagePathOffset); RamdiskPath = BlAllocateHeap( strlen((PCHAR)p ) + sizeof ( "net(0)\\" ) ); if ( RamdiskPath == NULL ) { DBGPRINT(ERR, ("Failed to allocate memory for RamdiskPath size %d.\n", strlen((PCHAR)p ) + sizeof ( "net(0)\\" ))); return ENOMEM; } strcpy( RamdiskPath, "net(0)\\" ); strcat( RamdiskPath, (PCHAR)p ); if (DBGLVL(INFO)) { DbgPrint( "RDPATH = %s\n", RamdiskPath); p = (PUCHAR) &RamdiskTFTPAddr; DbgPrint( "RDTFTPADDR = %u.%u.%u.%u\n", p[0], p[1], p[2], p[3]); 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 ); } } else { DBGPRINT(ERR, ("RamdiskBuildAndInitialize: Failed.\n")); return EINVAL; } return ESUCCESS; Error: return status; } #endif #endif // defined(POST_XPSP) 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