/*++ Copyright (c) 1992 Microsoft Corporation Module Name: arcdisk.c Abstract: Provides the routines for collecting the disk information for all the ARC disks visible in the ARC environment. Author: John Vert (jvert) 3-Nov-1993 Revision History: Vijay Jayaseelan (vijayj) 2-April-2000 - Added EFI partition table support --*/ #include "bootlib.h" #ifdef i386 #include "bldrx86.h" #endif #if defined(_IA64_) #include "bldria64.h" #endif #ifdef EFI_PARTITION_SUPPORT // // EFI partition entries // UNALIGNED EFI_PARTITION_ENTRY EfiPartitionBuffer[128] = {0}; #endif BOOLEAN BlpEnumerateDisks( IN PCONFIGURATION_COMPONENT_DATA ConfigData ); #if defined(_X86_) && !defined(ARCI386) static VOID BlpEnumerateXInt13( VOID ) /*++ Routine Description: This routine will go through all the enumerated disks and record their ability to support xInt13. Arguments: None. Return Value: None. --*/ { CHAR Partition[100]; ULONG DiskId; ARC_STATUS Status; PARC_DISK_SIGNATURE DiskSignature; PARC_DISK_INFORMATION DiskInfo; PLIST_ENTRY Entry; DiskInfo = BlLoaderBlock->ArcDiskInformation; Entry = DiskInfo->DiskSignatures.Flink; while (Entry != &DiskInfo->DiskSignatures) { DiskSignature = CONTAINING_RECORD(Entry,ARC_DISK_SIGNATURE,ListEntry); // // Open partition0 on the disk and get it's device ID. // strcpy(Partition, DiskSignature->ArcName); strcat(Partition, "partition(0)"); Status = ArcOpen(Partition, ArcOpenReadOnly, &DiskId); if( Status == ESUCCESS ) { // // Now we've got the DiskId. Fortunately, someone // has been keeping track of all the DiskIds on the // machine and whether or not they've got xint13 support. // All we need to do now is go lookup our diskid in // that database and get the xint13 BOOLEAN. // DiskSignature->xInt13 = BlFileTable[DiskId].u.DriveContext.xInt13; // // We don't need you anymore. // ArcClose(DiskId); } else { DiskSignature->xInt13 = FALSE; } Entry = Entry->Flink; } } #endif // for defined(_X86_) && !defined(ARCI386) ARC_STATUS BlGetArcDiskInformation( BOOLEAN XInt13Support ) /*++ Routine Description: Enumerates the ARC disks present in the system and collects the identifying disk information from each one. Arguments: XInt13Support : Indicates whether to find XInt13 support or not Return Value: None. --*/ { PARC_DISK_INFORMATION DiskInfo; #if !(defined(_X86_) && !defined(ARCI386)) UNREFERENCED_PARAMETER(XInt13Support); #endif DiskInfo = BlAllocateHeap(sizeof(ARC_DISK_INFORMATION)); if (DiskInfo==NULL) { return(ENOMEM); } InitializeListHead(&DiskInfo->DiskSignatures); BlLoaderBlock->ArcDiskInformation = DiskInfo; BlSearchConfigTree(BlLoaderBlock->ConfigurationRoot, PeripheralClass, DiskPeripheral, (ULONG)-1, BlpEnumerateDisks); #if defined(_X86_) && !defined(ARCI386) // // Enumerate XInt13 support on X86 only if asked for // if (XInt13Support) { BlpEnumerateXInt13(); } #endif return(ESUCCESS); } BOOLEAN BlpEnumerateDisks( IN PCONFIGURATION_COMPONENT_DATA ConfigData ) /*++ Routine Description: Callback routine for enumerating the disks in the ARC firmware tree. It reads all the necessary information from the disk to uniquely identify it. Arguments: ConfigData - Supplies a pointer to the disk's ARC component data. Return Value: TRUE - continue searching FALSE - stop searching tree. --*/ { CHAR DiskName[100]; BlGetPathnameFromComponent(ConfigData, DiskName); return(BlReadSignature(DiskName,FALSE)); } BOOLEAN BlReadSignature( IN PCHAR DiskName, IN BOOLEAN IsCdRom ) /*++ Routine Description: Given an ARC disk name, reads the MBR and adds its signature to the list of disks. Arguments: Diskname - Supplies the name of the disk. IsCdRom - Indicates whether the disk is a CD-ROM. Return Value: TRUE - Success FALSE - Failure --*/ { PARC_DISK_SIGNATURE Signature; BOOLEAN Status = FALSE; Signature = BlAllocateHeap(sizeof(ARC_DISK_SIGNATURE)); if (Signature==NULL) { return(FALSE); } Signature->ArcName = BlAllocateHeap((ULONG)strlen(DiskName)+2); if (Signature->ArcName==NULL) { return(FALSE); } #if defined(i386) Status = BlFindDiskSignature(DiskName, Signature); #endif if(!Status) { Status = BlGetDiskSignature(DiskName, IsCdRom, Signature); } if (Status) { InsertHeadList(&BlLoaderBlock->ArcDiskInformation->DiskSignatures, &Signature->ListEntry); } return(TRUE); } BOOLEAN ArcDiskGPTDiskReadCallback( ULONGLONG StartingLBA, ULONG BytesToRead, PVOID pContext, UNALIGNED PVOID OutputBuffer ) /*++ Routine Description: This routine is a callback for reading data for a routine that validates the GPT partition table. NOTE: This routine changes the seek position on disk, and you must seek back to your original seek position if you plan on reading from the disk after making this call. Arguments: StartingLBA - starting logical block address to read from. BytesToRead - Indicates how many bytes are to be read. pContext - context pointer for hte function (in this case, a pointer to the disk id.) OutputBuffer - a buffer that receives the data. It's assumed that it is at least BytesToRead big enough. Return Value: TRUE - success, data has been read FALSE - failed, data has not been read. --*/ { ARC_STATUS Status; LARGE_INTEGER SeekPosition; PUSHORT DataPointer; ULONG DiskId; ULONG ReadCount = 0; DiskId = *((PULONG)pContext); // // read the second LBA on the disk // SeekPosition.QuadPart = StartingLBA * SECTOR_SIZE; Status = ArcSeek(DiskId, &SeekPosition, SeekAbsolute ); if (Status != ESUCCESS) { return FALSE; } DataPointer = OutputBuffer; Status = ArcRead( DiskId, DataPointer, BytesToRead, &ReadCount); if ((Status == ESUCCESS) && (ReadCount == BytesToRead)) { return(TRUE); } return(FALSE); } BOOLEAN BlGetDiskSignature( IN PCHAR DiskName, IN BOOLEAN IsCdRom, PARC_DISK_SIGNATURE Signature ) /*++ Routine Description: This routine gets the NTFT disk signature for a specified partition or path. Arguments: DiskName - Supplies the arcname of the partition or drive. IsCdRom - Indicates whether the disk is a CD-ROM. Signature - Returns a full ARC_DISK_SIGNATURE. Return Value: TRUE - success, Signature will be filled in. FALSE - failed, Signature will not be filled in. --*/ { UCHAR SectorBuffer[2048+256] = {0}; CHAR Partition[100]; ULONG DiskId; ULONG Status; LARGE_INTEGER SeekValue; PUCHAR Sector; ULONG i; ULONG Sum; ULONG Count; ULONG SectorSize; EFI_PARTITION_TABLE *EfiHdr; if (IsCdRom) { SectorSize = 2048; } else { SectorSize = 512; } #if defined(_i386_) // // NTDETECT creates an "eisa(0)..." arcname for detected // BIOS disks on an EISA machine. Change this to "multi(0)..." // in order to be consistent with the rest of the system // (particularly the arcname in boot.ini) // if (_strnicmp(DiskName,"eisa",4)==0) { strcpy(Signature->ArcName,"multi"); strcpy(Partition,"multi"); strcat(Signature->ArcName,DiskName+4); strcat(Partition,DiskName+4); } else { strcpy(Signature->ArcName, DiskName); strcpy(Partition, DiskName); } #else strcpy(Signature->ArcName, DiskName); strcpy(Partition, DiskName); #endif strcat(Partition, "partition(0)"); Status = ArcOpen(Partition, ArcOpenReadOnly, &DiskId); if (Status != ESUCCESS) { return(FALSE); } // // Read in the first sector // Sector = ALIGN_BUFFER(SectorBuffer); if (IsCdRom) { // // For a CD-ROM, the interesting data starts at 0x8000. // SeekValue.QuadPart = 0x8000; } else { SeekValue.QuadPart = 0; } Status = ArcSeek(DiskId, &SeekValue, SeekAbsolute); if (Status == ESUCCESS) { Status = ArcRead(DiskId, Sector, SectorSize, &Count); } if (Status != ESUCCESS) { ArcClose(DiskId); return(FALSE); } // // Check to see whether this disk has a valid partition table signature or not. // if (((PUSHORT)Sector)[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE) { Signature->ValidPartitionTable = FALSE; } else { Signature->ValidPartitionTable = TRUE; } Signature->Signature = ((PULONG)Sector)[PARTITION_TABLE_OFFSET/2-1]; // // compute the checksum // Sum = 0; for (i=0; i<(SectorSize/4); i++) { Sum += ((PULONG)Sector)[i]; } Signature->CheckSum = ~Sum + 1; // // Check for GPT disk. // Signature->IsGpt = FALSE; if (!IsCdRom) { SeekValue.QuadPart = 1 * SectorSize; Status = ArcSeek(DiskId, &SeekValue, SeekAbsolute); if (Status == ESUCCESS) { Status = ArcRead(DiskId, Sector, SectorSize, &Count); if (Status == ESUCCESS) { ULONG tmpDiskId = DiskId; // // verify EFI partition table // EfiHdr = (EFI_PARTITION_TABLE *)Sector; if (BlIsValidGUIDPartitionTable( EfiHdr, 1, &tmpDiskId, ArcDiskGPTDiskReadCallback)) { Signature->IsGpt = TRUE; memcpy( Signature->GptSignature, EfiHdr->DiskGuid, sizeof(EfiHdr->DiskGuid) ); } } } } ArcClose(DiskId); return(TRUE); } #ifdef EFI_PARTITION_SUPPORT /* void DbgOut(PWSTR Str); //#define DBG_PRINT(x) DbgOut(x); ULONG BlGetKey(); #if defined(_IA64_) #define STR_PREFIX L #define DBG_PRINT(x) DbgOut(x) #else #define STR_PREFIX #define DBG_PRINT(x) \ {\ BlPrint(x); \ while (!BlGetKey()); \ } #endif // _IA64_ */ #define DBG_PRINT(x) #define STR_PREFIX UNALIGNED EFI_PARTITION_ENTRY * BlLocateGPTPartition( IN UCHAR PartitionNumber, IN UCHAR MaxPartitions, IN PUCHAR ValidPartCount ) { UNALIGNED EFI_PARTITION_ENTRY *PartEntry = NULL; UCHAR NullGuid[16] = {0}; UCHAR PartIdx = 0; UCHAR PartCount = 0; #if 0 BlPrint("BlLocateGPTPartition(%d,%d,%d)\r\n", PartitionNumber, MaxPartitions, ValidPartCount ? *ValidPartCount : 0); while (!BlGetKey()); #endif if (ARGUMENT_PRESENT(ValidPartCount)) { PartCount = *ValidPartCount; } PartitionNumber++; // convert to one based index // // Locate the requested valid partition // while ((PartIdx < MaxPartitions) && (PartCount < PartitionNumber)) { DBG_PRINT(STR_PREFIX"Verifying GPT Partition Entry\r\n"); PartEntry = (UNALIGNED EFI_PARTITION_ENTRY *)(EfiPartitionBuffer + PartIdx); if ((memcmp(PartEntry->Type, NullGuid, 16)) && (memcmp(PartEntry->Id, NullGuid, 16)) && (PartEntry->StartingLBA != 0) && (PartEntry->EndingLBA != 0)) { DBG_PRINT(STR_PREFIX"Found Valid GPT Partition Entry\r\n"); PartCount++; if (ARGUMENT_PRESENT(ValidPartCount)) { (*ValidPartCount)++; } // // Get hold of the partition entry // if (PartCount == PartitionNumber) { break; } else { PartEntry = NULL; } } else { PartEntry = NULL; } PartIdx++; } return PartEntry; } BOOLEAN BlDiskOpenGPTDiskReadCallback( ULONGLONG StartingLBA, ULONG BytesToRead, PVOID pContext, UNALIGNED PVOID OutputBuffer ) /*++ Routine Description: This routine is a callback for reading data for a routine that validates the GPT partition table. Arguments: StartingLBA - starting logical block address to read from. BytesToRead - Indicates how many bytes are to be read. pContext - context pointer for hte function (in this case, a pointer to the disk id.) OutputBuffer - a buffer that receives the data. It's assumed that it is at least BytesToRead big enough. Return Value: TRUE - success, data has been read FALSE - failed, data has not been read. --*/ { ARC_STATUS Status; LARGE_INTEGER SeekPosition; PUSHORT DataPointer; ULONG DiskId; ULONG ReadCount = 0; DiskId = *((PULONG)pContext); // // read the second LBA on the disk // SeekPosition.QuadPart = StartingLBA * SECTOR_SIZE; Status = (BlFileTable[DiskId].DeviceEntryTable->Seek)(DiskId, &SeekPosition, SeekAbsolute ); if (Status != ESUCCESS) { return FALSE; } DataPointer = OutputBuffer; Status = (BlFileTable[DiskId].DeviceEntryTable->Read)(DiskId, DataPointer, BytesToRead, &ReadCount); if ((Status == ESUCCESS) && (ReadCount == BytesToRead)) { return(TRUE); } return(FALSE); } ARC_STATUS BlOpenGPTDiskPartition( IN ULONG FileId, IN ULONG DiskId, IN UCHAR PartitionNumber ) { ARC_STATUS Status; LARGE_INTEGER SeekPosition; UCHAR DataBuffer[SECTOR_SIZE * 2] = {0}; ULONG ReadCount = 0; UCHAR NullGuid[16] = {0}; UNALIGNED EFI_PARTITION_TABLE *EfiHdr; UNALIGNED EFI_PARTITION_ENTRY *PartEntry; ULONG tmpDiskId = DiskId; if (PartitionNumber >= 128) return EINVAL; DBG_PRINT(STR_PREFIX"Seeking GPT PT\r\n"); // // read the second LBA on the disk // SeekPosition.QuadPart = 1 * SECTOR_SIZE; Status = (BlFileTable[DiskId].DeviceEntryTable->Seek)(DiskId, &SeekPosition, SeekAbsolute ); if (Status != ESUCCESS) return Status; DBG_PRINT(STR_PREFIX"Reading GPT PT\r\n"); Status = (BlFileTable[DiskId].DeviceEntryTable->Read)(DiskId, DataBuffer, SECTOR_SIZE, &ReadCount); if (Status != ESUCCESS) return Status; if (ReadCount != SECTOR_SIZE) { Status = EIO; return Status; } EfiHdr = (UNALIGNED EFI_PARTITION_TABLE *)DataBuffer; DBG_PRINT(STR_PREFIX"Verifying GPT PT\r\n"); // // verify EFI partition table // if (!BlIsValidGUIDPartitionTable( EfiHdr, 1, &tmpDiskId, BlDiskOpenGPTDiskReadCallback)) { Status = EBADF; return Status; } // // Locate and read the partition entry // which is requested // SeekPosition.QuadPart = EfiHdr->PartitionEntryLBA * SECTOR_SIZE; DBG_PRINT(STR_PREFIX"Seeking GPT Partition Entries\r\n"); Status = (BlFileTable[DiskId].DeviceEntryTable->Seek)(DiskId, &SeekPosition, SeekAbsolute ); if (Status != ESUCCESS) return Status; RtlZeroMemory(EfiPartitionBuffer, sizeof(EfiPartitionBuffer)); DBG_PRINT(STR_PREFIX"Reading GPT Partition Entries\r\n"); Status = (BlFileTable[DiskId].DeviceEntryTable->Read)(DiskId, EfiPartitionBuffer, sizeof(EfiPartitionBuffer), &ReadCount); if (Status != ESUCCESS) return Status; if (ReadCount != sizeof(EfiPartitionBuffer)) { Status = EIO; return Status; } DBG_PRINT(STR_PREFIX"Locating the correct GPT partition entry\r\n"); PartEntry = (UNALIGNED EFI_PARTITION_ENTRY *)BlLocateGPTPartition(PartitionNumber, 128, NULL); if (PartEntry) { DBG_PRINT(STR_PREFIX"Verifying GPT Partition Entry\r\n"); if ((memcmp(PartEntry->Type, NullGuid, 16)) && (memcmp(PartEntry->Id, NullGuid, 16)) && (PartEntry->StartingLBA != 0) && (PartEntry->EndingLBA != 0)) { PPARTITION_CONTEXT PartContext = &(BlFileTable[FileId].u.PartitionContext); ULONG SectorCount = (ULONG)(PartEntry->EndingLBA - PartEntry->StartingLBA); DBG_PRINT(STR_PREFIX"Initializing GPT Partition Entry Context\r\n"); // // Fill the partition context structure // PartContext->PartitionLength.QuadPart = SectorCount * SECTOR_SIZE; PartContext->StartingSector = (ULONG)(PartEntry->StartingLBA); PartContext->EndingSector = (ULONG)(PartEntry->EndingLBA); PartContext->DiskId = (UCHAR)DiskId; BlFileTable[FileId].Position.QuadPart = 0; #if 0 BlPrint("GPT Partition opened:L:%ld,%ld:%ld,SS:%ld,ES:%ld\n", PartitionNumber, (ULONG)PartContext->PartitionLength.QuadPart, (ULONG)PartContext->StartingSector, (ULONG)PartContext->EndingSector); while (!GET_KEY()); #endif Status = ESUCCESS; } else { Status = EBADF; } } else { Status = EBADF; } DBG_PRINT(STR_PREFIX"Returning from BlOpenGPTDiskPartition(...)\r\n"); return Status; } #endif // for EFI_PARTITION_SUPPORT