/*++ Copyright (c) 1992 Microsoft Corporation Module Name: entry.c Abstract: x86-specific startup for setupldr Author: John Vert (jvert) 14-Oct-1993 Revision History: --*/ #include "bootx86.h" #include "stdio.h" #include "flop.h" #include #if 0 #define DBGOUT(x) BlPrint x #define DBGPAUSE while(!GET_KEY()); #else #define DBGOUT(x) #define DBGPAUSE #endif // // Prototypes for Internal Routines // VOID DoGlobalInitialization( PBOOT_CONTEXT ); #if defined(ELTORITO) BOOLEAN ElToritoCDBoot = FALSE; #endif extern CHAR NetBootPath[]; // // Global context pointers. These are passed to us by the SU module or // the bootstrap code. // PCONFIGURATION_COMPONENT_DATA FwConfigurationTree = NULL; PEXTERNAL_SERVICES_TABLE ExternalServicesTable; UCHAR BootPartitionName[80]; ULONG MachineType = 0; ULONG OsLoaderBase; ULONG OsLoaderExports; extern PUCHAR BlpResourceDirectory; extern PUCHAR BlpResourceFileOffset; ULONG PcrBasePage; ULONG TssBasePage; ULONG BootFlags = 0; ULONG NtDetectStart = 0; ULONG NtDetectEnd = 0; #ifdef FORCE_CD_BOOT BOOLEAN BlGetCdRomDrive( PUCHAR DriveId ) { BOOLEAN Result = FALSE; UCHAR Id = 0; do { if (BlIsElToritoCDBoot(Id)) { *DriveId = Id; Result = TRUE; break; } Id++; } while (Id != 0); return Result; } #endif VOID NtProcessStartup( IN PBOOT_CONTEXT BootContextRecord ) /*++ Routine Description: Main entry point for setup loader. Control is transferred here by the start-up (SU) module. Arguments: BootContextRecord - Supplies the boot context, particularly the ExternalServicesTable. Returns: Does not return. Control eventually passed to the kernel. --*/ { ARC_STATUS Status; #ifdef FORCE_CD_BOOT BOOLEAN CdFound; UCHAR CdId; #endif ULONG_PTR pFirmwareHeapAddress; ULONG TssSize,TssPages; // // Initialize the boot loader's video // DoGlobalInitialization(BootContextRecord); BlFillInSystemParameters(BootContextRecord); // // Set the global bootflags // BootFlags = BootContextRecord->BootFlags; #ifdef FORCE_CD_BOOT CdFound = BlGetCdRomDrive(&CdId); if (CdFound) { BlPrint("CD/DVD-Rom drive found with id:%u\n", CdId); BootContextRecord->FSContextPointer->BootDrive = CdId; } else { BlPrint("CD/DVD-Rom drive not found"); } #endif // for FORCE_CD_BOOT if (BootContextRecord->FSContextPointer->BootDrive == 0) { // // Boot was from A: // strcpy(BootPartitionName,"multi(0)disk(0)fdisk(0)"); // // To get around an apparent bug on the BIOS of some MCA machines // (specifically the NCR 386sx/MC20 w/ BIOS version 1.04.00 (3421), // Phoenix BIOS 1.02.07), whereby the first int13 to floppy results // in a garbage buffer, reset drive 0 here. // GET_SECTOR(0,0,0,0,0,0,NULL); } else if (BootContextRecord->FSContextPointer->BootDrive == 0x40) { // // Boot was from the net // strcpy(BootPartitionName,"net(0)"); BlBootingFromNet = TRUE; #if defined(REMOTE_BOOT) BlGetActivePartition(NetBootActivePartitionName); NetFindCSCPartitionName(); #endif } else if (BootContextRecord->FSContextPointer->BootDrive == 0x41) { // // Boot was from an SDI image // strcpy(BootPartitionName,"ramdisk(0)"); } else if (BlIsElToritoCDBoot(BootContextRecord->FSContextPointer->BootDrive)) { // // Boot was from El Torito CD // sprintf(BootPartitionName, "multi(0)disk(0)cdrom(%u)", BootContextRecord->FSContextPointer->BootDrive); ElToritoCDBoot = TRUE; } else { // // Find the partition we have been booted from. Note that this // is *NOT* necessarily the active partition. If the system has // Boot Mangler installed, it will be the active partition, and // we have to go figure out what partition we are actually on. // BlGetActivePartition(BootPartitionName); #if defined(REMOTE_BOOT) strcpy(NetBootActivePartitionName, BootPartitionName); NetFindCSCPartitionName(); #endif } // // We need to make sure that we've got a signature on disk 80. // If not, then write one. // { ULONG DriveId; ULONG NewSignature; UCHAR SectorBuffer[4096+256]; PUCHAR Sector; LARGE_INTEGER SeekValue; ULONG Count; Status = ArcOpen("multi(0)disk(0)rdisk(0)partition(0)", ArcOpenReadWrite, &DriveId); if (Status == ESUCCESS) { // // Get a reasonably unique seed to start with. // NewSignature = ArcGetRelativeTime(); NewSignature = (NewSignature & 0xFFFF) << 16; NewSignature += ArcGetRelativeTime(); // // Now we have a valid new signature to put on the disk. // Read the sector off disk, put the new signature in, // write the sector back, and recompute the checksum. // Sector = ALIGN_BUFFER(SectorBuffer); SeekValue.QuadPart = 0; Status = ArcSeek(DriveId, &SeekValue, SeekAbsolute); if (Status == ESUCCESS) { Status = ArcRead(DriveId,Sector,512,&Count); if( Status == ESUCCESS ) { if( ((PULONG)Sector)[PARTITION_TABLE_OFFSET/2-1] == 0 ) { // // He's 0. Write a real signature in there. // ((PULONG)Sector)[PARTITION_TABLE_OFFSET/2-1] = NewSignature; Status = ArcSeek(DriveId, &SeekValue, SeekAbsolute); if (Status == ESUCCESS) { Status = ArcWrite(DriveId,Sector,512,&Count); if( Status != ESUCCESS ) { #if DBG BlPrint( "Falied to write the new signature on the boot partition.\n" ); #endif } } else { #if DBG BlPrint( "Failed second ArcSeek on the boot partition to check for a signature.\n" ); #endif } } } else { #if DBG BlPrint( "Failed to ArcRead the boot partition to check for a signature.\n" ); #endif } } else { #if DBG BlPrint( "Failed to ArcSeek the boot partition to check for a signature.\n" ); #endif } ArcClose(DriveId); } else { #if DBG BlPrint( "Couldn't Open the boot partition to check for a signature.\n" ); #endif } } // // squirrel away some memory for the PCR and TSS so that we get the // preferred memory location (<16MB) for this data. // pFirmwareHeapAddress = (ULONG_PTR)FwAllocateHeapPermanent( 2 ); if (!pFirmwareHeapAddress) { BlPrint("Couldn't allocate memory for PCR\n"); goto BootFailed; } PcrBasePage = (ULONG)(pFirmwareHeapAddress>>PAGE_SHIFT); TssSize = (sizeof(KTSS) + PAGE_SIZE) & ~(PAGE_SIZE - 1); TssPages = TssSize / PAGE_SIZE; pFirmwareHeapAddress = (ULONG_PTR)FwAllocateHeapPermanent( TssPages ); if (!pFirmwareHeapAddress) { BlPrint("Couldn't allocate memory for TSS\n"); goto BootFailed; } TssBasePage = (ULONG)(pFirmwareHeapAddress>>PAGE_SHIFT); // // Initialize the memory descriptor list, the OS loader heap, and the // OS loader parameter block. // Status = BlMemoryInitialize(); if (Status != ESUCCESS) { BlPrint("Couldn't initialize memory\n"); goto BootFailed; } // // Initialize the OS loader I/O system. // AEInitializeStall(); Status = BlIoInitialize(); if (Status != ESUCCESS) { BlPrint("Couldn't initialize I/O\n"); goto BootFailed; } // // Call off to regular startup code // BlStartup(BootPartitionName); // // we should never get here! // BootFailed: if (BootFlags & BOOTFLAG_REBOOT_ON_FAILURE) { ULONG StartTime = ArcGetRelativeTime(); BlPrint(TEXT("\nRebooting in 5 seconds...\n")); while ( ArcGetRelativeTime() - StartTime < 5) {} ArcRestart(); } do { GET_KEY(); // BOOT FAILED! if (BlTerminalHandleLoaderFailure()) { ArcRestart(); } } while ( 1 ); } BOOLEAN BlDetectHardware( IN ULONG DriveId, IN PCHAR LoadOptions ) /*++ Routine Description: Loads and runs NTDETECT.COM to populate the ARC configuration tree. Arguments: DriveId - Supplies drive id where NTDETECT is located. LoadOptions - Supplies Load Options string to ntdetect. Return Value: TRUE - NTDETECT successfully run. FALSE - Error --*/ { // Current Loader stack size is 8K, so make sure you do not // blow that space. Make sure this is not smaller than 140. #define LOAD_OPTIONS_BUFFER_SIZE 512 ARC_STATUS Status; PCONFIGURATION_COMPONENT_DATA TempFwTree; ULONG TempFwHeapUsed; ULONG FileSize; ULONG DetectFileId; FILE_INFORMATION FileInformation; PUCHAR DetectionBuffer = (PUCHAR)DETECTION_LOADED_ADDRESS; PUCHAR Options = NULL; UCHAR Buffer[LOAD_OPTIONS_BUFFER_SIZE]; LARGE_INTEGER SeekPosition; ULONG Read; BOOLEAN SkipLegacyDetection; BOOLEAN Success = FALSE; ULONG HeapStart; ULONG HeapSize; ULONG RequiredLength = 0; // // Check if the ntdetect.com was bundled as a data section // in the loader executable. // if (NtDetectStart == 0) { // // Now check if we have ntdetect.com in the root directory, if yes, // we will load it to predefined location and transfer control to // it. // #if defined(ELTORITO) if (ElToritoCDBoot) { // we assume ntdetect.com is in the i386 directory Status = BlOpen( DriveId, "\\i386\\ntdetect.com", ArcOpenReadOnly, &DetectFileId ); } else { #endif if (BlBootingFromNet #if defined(REMOTE_BOOT) && NetworkBootRom #endif // defined(REMOTE_BOOT) ) { strcpy(Buffer, NetBootPath); #if defined(REMOTE_BOOT) // // This is the way it was done for remote BOOT, where we were // booting out of a client's machine directory. // strcat(Buffer, "BootDrive\\ntdetect.com"); #else // // This is how it is done for remote INSTALL, where we are // booting out of the templates directory under a setup directory. // strcat(Buffer, "ntdetect.com"); #endif // defined(REMOTE_BOOT) Status = BlOpen( DriveId, Buffer, ArcOpenReadOnly, &DetectFileId ); } else { Status = BlOpen( DriveId, "\\ntdetect.com", ArcOpenReadOnly, &DetectFileId ); } #if defined(ELTORITO) } #endif if (Status != ESUCCESS) { #if DBG BlPrint("Error opening NTDETECT.COM, status = %x\n", Status); BlPrint("Press any key to continue\n"); #endif goto Exit; } // // Determine the length of the ntdetect.com file // Status = BlGetFileInformation(DetectFileId, &FileInformation); if (Status != ESUCCESS) { BlClose(DetectFileId); #if DBG BlPrint("Error getting NTDETECT.COM file information, status = %x\n", Status); BlPrint("Press any key to continue\n"); #endif goto Exit; } FileSize = FileInformation.EndingAddress.LowPart; if (FileSize == 0) { BlClose(DetectFileId); #if DBG BlPrint("Error: size of NTDETECT.COM is zero.\n"); BlPrint("Press any key to continue\n"); #endif goto Exit; } SeekPosition.QuadPart = 0; Status = BlSeek(DetectFileId, &SeekPosition, SeekAbsolute); if (Status != ESUCCESS) { BlClose(DetectFileId); #if DBG BlPrint("Error seeking to start of NTDETECT.COM file\n"); BlPrint("Press any key to continue\n"); #endif goto Exit; } Status = BlRead( DetectFileId, DetectionBuffer, FileSize, &Read ); BlClose(DetectFileId); if (Status != ESUCCESS) { #if DBG BlPrint("Error reading from NTDETECT.COM\n"); BlPrint("Read %lx bytes\n",Read); BlPrint("Press any key to continue\n"); #endif goto Exit; } } else { // ntdetect.com was bundled in the loader image // as a data section. We will use it contents // instead of opening the file. RtlCopyMemory( DetectionBuffer, (PVOID)NtDetectStart, NtDetectEnd - NtDetectStart ); } // // Set the heap start and size used by ntdetect // HeapStart = (TEMPORARY_HEAP_START - 0x10) * PAGE_SIZE; HeapSize = 0x10000; // 64K // // We need to pass NTDETECT pointers < 1Mb, so // use local storage off the stack if possible. (which is // always < 1Mb.) If not possible (boot.ini is too big) // and we will add it to the heap used by ntdetect.com, thereby // reducing the heap space used by ntdetect.com // if ( LoadOptions ) { // count the characters in LoadOptions + null terminator + // room for " NOLEGACY" that might be appended later RequiredLength = strlen(LoadOptions) + strlen(" NOLEGACY") + 1; // check if the buffer on the stack is big enough if ( RequiredLength > LOAD_OPTIONS_BUFFER_SIZE ) { // // Buffer is too small. let move it to the // end of the ntdetect heap // Options = (PCHAR)( HeapStart + HeapSize - RequiredLength ); HeapSize -= RequiredLength; strcpy( Options, LoadOptions ); } else { // // Load options will fit on the stack. copy them there // strcpy( Buffer, LoadOptions ); Options = Buffer; } } else { // // No load options // Options = NULL; } // // Check whether we need to add the NOLEGACY option // if (BlDetectLegacyFreeBios()) { if (Options != NULL) { strcat(Options, " NOLEGACY"); } else { strcpy(Buffer, " NOLEGACY"); Options = Buffer; } } DETECT_HARDWARE((ULONG)HeapStart, (ULONG)HeapSize, (PVOID)&TempFwTree, (PULONG)&TempFwHeapUsed, (PCHAR)Options, (Options != NULL) ? strlen(Options) : 0 ); FwConfigurationTree = TempFwTree; Status = BlpMarkExtendedVideoRegionOffLimits(); Success = (BOOLEAN)(Status == ESUCCESS); Exit: // // Reinitialize the headless port - detect wipes it out. // BlInitializeHeadlessPort(); return(Success); } VOID DoGlobalInitialization( IN PBOOT_CONTEXT BootContextRecord ) /*++ Routine Description This routine calls all of the subsytem initialization routines. Arguments: None Returns: Nothing --*/ { ARC_STATUS Status; // // Set base address of OS Loader image for the debugger. // OsLoaderBase = BootContextRecord->OsLoaderBase; OsLoaderExports = BootContextRecord->OsLoaderExports; // // Initialize memory. // Status = InitializeMemorySubsystem(BootContextRecord); if (Status != ESUCCESS) { BlPrint("InitializeMemory failed %lx\n",Status); while (1) { } } ExternalServicesTable=BootContextRecord->ExternalServicesTable; MachineType = BootContextRecord->MachineType; // // Turn the cursor off // HW_CURSOR(0,127); BlpResourceDirectory = (PUCHAR)(BootContextRecord->ResourceDirectory); BlpResourceFileOffset = (PUCHAR)(BootContextRecord->ResourceOffset); NtDetectStart = BootContextRecord->NtDetectStart; NtDetectEnd = BootContextRecord->NtDetectEnd; // // If this is an SDI boot, copy the address of the SDI image out of the // boot context record. SdiAddress is declared in boot\inc\ramdisk.h and // initialized to 0 in boot\lib\ramdisk.c. // if (BootContextRecord->FSContextPointer->BootDrive == 0x41) { SdiAddress = BootContextRecord->SdiAddress; } InitializeMemoryDescriptors (); } VOID BlGetActivePartition( OUT PUCHAR BootPartitionName ) /*++ Routine Description: Determine the ARC name for the partition NTLDR was started from Arguments: BootPartitionName - Supplies a buffer where the ARC name of the partition will be returned. Return Value: Name of the partition is in BootPartitionName. Must always succeed. --*/ { UCHAR SectorBuffer[512]; ARC_STATUS Status; ULONG FileId; ULONG Count; int i; // // The boot sector used to boot us is still in memory at 0x7c00. // The hidden sectors field in the BPB is pretty much guaranteed // to be intact, since all boot codes use that field and thus // are unlikely to have overwritten it. // We open each partition and compare the in-memory hidden sector count // at 0x7c1c to the hidden sector value in the BPB. // i = 1; do { sprintf(BootPartitionName,"multi(0)disk(0)rdisk(0)partition(%u)",i); Status = ArcOpen(BootPartitionName,ArcOpenReadOnly,&FileId); if(Status == ESUCCESS) { // // Read the first part of the partition. // Status = ArcRead(FileId,SectorBuffer,512,&Count); ArcClose(FileId); if((Status == ESUCCESS) && !memcmp(SectorBuffer+0x1c,(PVOID)0x7c1c,4)) { // // Found it, BootPartitionName is already set for return. // return; } Status = ESUCCESS; } i++; } while (Status == ESUCCESS); // // Run out of partitions without finding match. Fall back on partition 1. // strcpy(BootPartitionName,"multi(0)disk(0)rdisk(0)partition(1)"); } BOOLEAN BlIsElToritoCDBoot( UCHAR DriveNum ) { // // Note, even though args are short, they are pushed on the stack with // 32bit alignment so the effect on the stack seen by the 16bit real // mode code is the same as if we were pushing longs here. // // GET_ELTORITO_STATUS is 0 if we are in emulation mode if (DriveNum > 0x81) { if (!GET_ELTORITO_STATUS(FwDiskCache,DriveNum)) { return(TRUE); } else { return(FALSE); } } else { return(FALSE); } } #if defined(REMOTE_BOOT) BOOLEAN NetFindCSCPartitionName( ) { UCHAR FileName[80]; UCHAR DiskName[80]; UCHAR PartitionName[80]; PUCHAR p; ULONG Part; ULONG FileId; ULONG DeviceId; if (NetBootSearchedForCSC) { return((BOOLEAN)strlen(NetBootCSCPartitionName)); } if (!strlen(NetBootActivePartitionName)) { BlGetActivePartition(NetBootActivePartitionName); } strcpy(DiskName, NetBootActivePartitionName); p = strstr(DiskName, "partition"); ASSERT( p != NULL ); *p = '\0'; Part = 1; while (TRUE) { sprintf(PartitionName, "%spartition(%u)", DiskName, Part); if (ArcOpen(PartitionName, ArcOpenReadOnly, &DeviceId) != ESUCCESS) { break; } ArcClose(DeviceId); sprintf(FileName, "%s%s", PartitionName, REMOTE_BOOT_IMIRROR_PATH_A REMOTE_BOOT_CSC_SUBDIR_A); if (ArcOpen(FileName, ArcOpenReadOnly, &FileId) == ESUCCESS) { ArcClose(FileId); NetBootSearchedForCSC = TRUE; strcpy(NetBootCSCPartitionName, PartitionName); return TRUE; } ArcClose(FileId); Part++; } strcpy(NetBootCSCPartitionName, NetBootActivePartitionName); return FALSE; } #endif // defined(REMOTE_BOOT)