/*++ Copyright (c) 1998 Microsoft Corporation Module Name: mbr.c Abstract: This module implements the FIXMBR command. Author: Wesley Witt (wesw) 21-Oct-1998 Revision History: --*/ #include "cmdcons.h" #pragma hdrstop #include // // For NEC98 boot memu code. // #include VOID RcDetermineDisk0( VOID ); BOOL RcDetermineDisk0Enum( IN PPARTITIONED_DISK Disk, IN PDISK_REGION Region, IN ULONG_PTR Context ); NTSTATUS RcOpenPartition( IN PWSTR DiskDevicePath, IN ULONG PartitionNumber, OUT HANDLE *Handle, IN BOOLEAN NeedWriteAccess ); NTSTATUS RcReadDiskSectors( IN HANDLE Handle, IN ULONG SectorNumber, IN ULONG SectorCount, IN ULONG BytesPerSector, IN OUT PVOID AlignedBuffer ); NTSTATUS RcWriteDiskSectors( IN HANDLE Handle, IN ULONG SectorNumber, IN ULONG SectorCount, IN ULONG BytesPerSector, IN OUT PVOID AlignedBuffer ); #define MBRSIZE_NEC98 0x2000 #define IPL_SIGNATURE_NEC98 "IPL1" ULONG RcCmdFixMBR( IN PTOKENIZED_LINE TokenizedLine ) /*++ Routine Description: Top-level routine supporting the FIXMBR command in the setup diagnostic command interpreter. FIXMBR writes a new master boot record. It will ask before writing the boot record if it cannot detect a valid mbr signature. Arguments: TokenizedLine - supplies structure built by the line parser describing each string on the line as typed by the user. Return Value: None. --*/ { WCHAR DeviceName[256]; ULONG i; ULONG SectorCount; ULONG BytesPerSector; PUCHAR Buffer = NULL; UCHAR InfoBuffer[2048]; ULONG SectorId = 0; HANDLE handle = 0; NTSTATUS rc; PON_DISK_MBR mbr; IO_STATUS_BLOCK StatusBlock; Int13HookerType Int13Hooker = NoHooker; ULONG NextSector; WCHAR Text[2]; PWSTR YesNo = NULL; BOOL Confirm = TRUE; BOOL SignatureInvalid = FALSE; BOOL Int13Detected = FALSE; PREAL_DISK_MBR_NEC98 MbrNec98; // // command is only supported on X86 platforms. // Alpha or other RISC platforms don't use // mbr code // #ifndef _X86_ RcMessageOut( MSG_ONLY_ON_X86 ); return 1; #else if (RcCmdParseHelp( TokenizedLine, MSG_FIXMBR_HELP )) { return 1; } if (TokenizedLine->TokenCount == 2) { wcscpy( DeviceName, TokenizedLine->Tokens->Next->String ); } else { RtlZeroMemory(DeviceName,sizeof(DeviceName)); SpEnumerateDiskRegions( (PSPENUMERATEDISKREGIONS)RcDetermineDisk0Enum, (ULONG_PTR)DeviceName ); } // // NEC98 does not support to fix removable media. // if (IsNEC_98 && RcIsFileOnRemovableMedia((PWSTR)DeviceName) == STATUS_SUCCESS) { return 1; } rc = RcOpenPartition( DeviceName, 0, &handle, TRUE ); if (!NT_SUCCESS(rc)) { DEBUG_PRINTF(( "failed to open partition zero!!!!!!" )); return 1; } // // get disk geometry // rc = ZwDeviceIoControlFile( handle, NULL, NULL, NULL, &StatusBlock, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, InfoBuffer, sizeof( InfoBuffer ) ); if( !NT_SUCCESS( rc ) ) { RcMessageOut( MSG_FIXMBR_READ_ERROR ); goto cleanup; } // // retrieve the sector size! // BytesPerSector = ((DISK_GEOMETRY*)InfoBuffer)->BytesPerSector; // // compute the sector count // SectorCount = max( 1, (!IsNEC_98 ? (sizeof( ON_DISK_MBR )/BytesPerSector) : (MBRSIZE_NEC98/BytesPerSector) )); // // allocate a buffer twice as big as necessary // Buffer = SpMemAlloc( 2 * SectorCount * BytesPerSector ); // // align the buffer // if(!IsNEC_98) { mbr = ALIGN( Buffer, BytesPerSector ); } else { MbrNec98 = ALIGN( Buffer, BytesPerSector ); } // // take in the sectors // rc = RcReadDiskSectors( handle, SectorId, SectorCount, BytesPerSector, (!IsNEC_98 ? (PVOID)mbr : (PVOID)MbrNec98) ); if (!NT_SUCCESS(rc)) { RcMessageOut( MSG_FIXMBR_READ_ERROR ); goto cleanup; } if ((!IsNEC_98 && U_USHORT(mbr->AA55Signature) != MBR_SIGNATURE) || (IsNEC_98 && ((U_USHORT(MbrNec98->AA55Signature) != MBR_SIGNATURE) || _strnicmp(MbrNec98->IPLSignature,IPL_SIGNATURE_NEC98,sizeof(IPL_SIGNATURE_NEC98)-1))) ) { SignatureInvalid = TRUE; RcMessageOut( MSG_FIXMBR_NO_VALID_SIGNATURE ); } // // check for weird int13 hookers // // No NEC98 supports EZ Drive. // // if (!IsNEC_98) { // // // EZDrive support: if the first entry in the partition table is // type 0x55, then the actual partition table is on sector 1. // // Only for x86 because on non-x86, the firmware can't see EZDrive // partitions. // // if (mbr->PartitionTable[0].SystemId == 0x55) { Int13Hooker = HookerEZDrive; SectorId = 1; } // // Also check for on-track. // if( mbr->PartitionTable[0].SystemId == 0x54 ) { Int13Hooker = HookerOnTrackDiskManager; SectorId = 1; } // // there's a define for HookerMax but we don't appear // to check for it in setup so I don't check for it here // // // If we have an int13 hooker // if (Int13Hooker != NoHooker) { Int13Detected = TRUE; RcMessageOut( MSG_FIXMBR_INT13_HOOKER ); } // // we have a valid signature AND int 13 hooker is detected // if (Int13Detected) { // // take sector 1 in, since sector 0 is the int hooker boot code // rc = RcReadDiskSectors( handle, SectorId, SectorCount, BytesPerSector, mbr ); // // sector 1 should look like a valid MBR too // if (U_USHORT(mbr->AA55Signature) != MBR_SIGNATURE) { SignatureInvalid = TRUE; RcMessageOut( MSG_FIXMBR_NO_VALID_SIGNATURE ); } } } RcMessageOut( MSG_FIXMBR_WARNING_BEFORE_PROCEED ); if (!InBatchMode) { YesNo = SpRetreiveMessageText(ImageBase,MSG_YESNO,NULL,0); if(!YesNo) { Confirm = FALSE; } while(Confirm) { RcMessageOut( MSG_FIXMBR_ARE_YOU_SURE ); if(RcLineIn(Text,2)) { if((Text[0] == YesNo[0]) || (Text[0] == YesNo[1])) { // // Wants to do it. // Confirm = FALSE; } else { if((Text[0] == YesNo[2]) || (Text[0] == YesNo[3])) { // // Doesn't want to do it. // goto cleanup; } } } } } // // now we need to slap in new boot code! // make sure the boot code starts at the start of the sector. // if(!IsNEC_98) { ASSERT(&((PON_DISK_MBR)0)->BootCode == 0); } else { ASSERT(&((PREAL_DISK_MBR_NEC98)0)->BootCode == 0); } RcMessageOut( MSG_FIXMBR_DOING_IT, DeviceName ); // // clobber the existing boot code // if(!IsNEC_98) { RtlMoveMemory(mbr,x86BootCode,sizeof(mbr->BootCode)); // // put a new signature in // U_USHORT(mbr->AA55Signature) = MBR_SIGNATURE; } else { // // Write MBR in 1st sector. // RtlMoveMemory(MbrNec98,x86PC98BootCode,0x200); // // Write continous MBR after 3rd sector. // RtlMoveMemory((PUCHAR)MbrNec98+0x400,x86PC98BootMenu,MBRSIZE_NEC98-0x400); } // // write out the sector // rc = RcWriteDiskSectors( handle, SectorId, SectorCount, BytesPerSector, (!IsNEC_98 ? (PVOID)mbr : (PVOID)MbrNec98) ); if (!NT_SUCCESS( rc )) { DEBUG_PRINTF(( "failed writing out new MBR." )); RcMessageOut( MSG_FIXMBR_FAILED ); goto cleanup; } RcMessageOut( MSG_FIXMBR_DONE ); cleanup: if (handle) { NtClose(handle); } if (Buffer) { SpMemFree(Buffer); } if (YesNo) { SpMemFree(YesNo); } return 1; #endif } BOOL RcDetermineDisk0Enum( IN PPARTITIONED_DISK Disk, IN PDISK_REGION Region, IN ULONG_PTR Context ) /*++ Routine Description: Callback routine passed to SpEnumDiskRegions. Arguments: Region - a pointer to a disk region returned by SpEnumDiskRegions Ignore - ignored parameter Return Value: TRUE - to continue enumeration FALSE - to end enumeration --*/ { WCHAR ArcName[256]; PWSTR DeviceName = (PWSTR)Context; SpArcNameFromRegion( Region, ArcName, sizeof(ArcName), PartitionOrdinalCurrent, PrimaryArcPath ); // // look for the one with arc path L"multi(0)disk(0)rdisk(0)" // if( wcsstr( ArcName, L"multi(0)disk(0)rdisk(0)" ) ) { *DeviceName = UNICODE_NULL; SpNtNameFromRegion( Region, DeviceName, MAX_PATH * sizeof(WCHAR), PartitionOrdinalCurrent ); if (*DeviceName != UNICODE_NULL) { PWSTR PartitionKey = wcsstr(DeviceName, L"Partition"); if (!PartitionKey) { PartitionKey = wcsstr(DeviceName, L"partition"); } // // partition 0 represents the start of disk // if (PartitionKey) { *PartitionKey = UNICODE_NULL; wcscat(DeviceName, L"Partition0"); } else { DeviceName[wcslen(DeviceName) - 1] = L'0'; } } return FALSE; } return TRUE; } NTSTATUS RcReadDiskSectors( IN HANDLE Handle, IN ULONG SectorNumber, IN ULONG SectorCount, IN ULONG BytesPerSector, IN OUT PVOID AlignedBuffer ) /*++ Routine Description: Reads one or more disk sectors. Arguments: Handle - supplies handle to open partition object from which sectors are to be read or written. The handle must be opened for synchronous I/O. Return Value: NTSTATUS value indicating outcome of I/O operation. --*/ { LARGE_INTEGER IoOffset; ULONG IoSize; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS Status; // // Calculate the large integer byte offset of the first sector // and the size of the I/O. // IoOffset.QuadPart = SectorNumber * BytesPerSector; IoSize = SectorCount * BytesPerSector; // // Perform the I/O. // Status = (NTSTATUS) ZwReadFile( Handle, NULL, NULL, NULL, &IoStatusBlock, AlignedBuffer, IoSize, &IoOffset, NULL ); if (!NT_SUCCESS(Status)) { KdPrint(("SETUP: Unable to read %u sectors starting at sector %u\n",SectorCount,SectorNumber)); } return(Status); } NTSTATUS RcWriteDiskSectors( IN HANDLE Handle, IN ULONG SectorNumber, IN ULONG SectorCount, IN ULONG BytesPerSector, IN OUT PVOID AlignedBuffer ) /*++ Routine Description: Writes one or more disk sectors. Arguments: Handle - supplies handle to open partition object from which sectors are to be read or written. The handle must be opened for synchronous I/O. Return Value: NTSTATUS value indicating outcome of I/O operation. --*/ { LARGE_INTEGER IoOffset; ULONG IoSize; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS Status; // // Calculate the large integer byte offset of the first sector // and the size of the I/O. // IoOffset.QuadPart = SectorNumber * BytesPerSector; IoSize = SectorCount * BytesPerSector; // // Perform the I/O. // Status = (NTSTATUS) ZwWriteFile( Handle, NULL, NULL, NULL, &IoStatusBlock, AlignedBuffer, IoSize, &IoOffset, NULL ); if (!NT_SUCCESS(Status)) { KdPrint(("SETUP: Unable to write %u sectors starting at sector %u\n",SectorCount,SectorNumber)); } return(Status); } NTSTATUS RcOpenPartition( IN PWSTR DiskDevicePath, IN ULONG PartitionNumber, OUT HANDLE *Handle, IN BOOLEAN NeedWriteAccess ) /*++ Routine Description: Opens and returns a handle to the specified partition. Arguments: DiskDevicePath - the path to the device. PartitionNumber - if the path doesn't already specify the Partition then the function will open the partition specified by this number Handle - where the open handle will be returned. The handle is opened for synchronous I/O. NeedWriteAccess - true to open in R/W Return Value: NTSTATUS value indicating outcome of I/O operation. --*/ { PWSTR PartitionPath; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES Obja; NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; // // Form the pathname of partition. // PartitionPath = SpMemAlloc((wcslen(DiskDevicePath) * sizeof(WCHAR)) + sizeof(L"\\partition000")); if(PartitionPath == NULL) { return STATUS_NO_MEMORY; } // // if partition is already specified in the string, then don't bother appending // it // if (wcsstr( DiskDevicePath, L"Partition" ) == 0) { swprintf(PartitionPath,L"%ws\\partition%u",DiskDevicePath,PartitionNumber); } else { swprintf(PartitionPath,L"%ws",DiskDevicePath); } // // Attempt to open partition0. // INIT_OBJA(&Obja,&UnicodeString,PartitionPath); Status = ZwCreateFile( Handle, FILE_GENERIC_READ | (NeedWriteAccess ? FILE_GENERIC_WRITE : 0), &Obja, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | (NeedWriteAccess ? FILE_SHARE_WRITE : 0), FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); if (!NT_SUCCESS(Status)) { KdPrint(("CMDCONS: Unable to open %ws (%lx)\n",PartitionPath,Status)); } SpMemFree(PartitionPath); return Status; }