/** * PROJECT: ExectOS * COPYRIGHT: See COPYING.md in the top level directory * FILE: xtldr/volume.c * DESCRIPTION: XTLDR volume support * DEVELOPERS: Rafal Kupiec */ #include /** * This routine closes an EFI volume handle. * * @param VolumeHandle * Specifies a handle of opened volume. * * @return This routine returns status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlCloseVolume(IN PEFI_HANDLE VolumeHandle) { EFI_GUID LIPGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID; /* Make sure a handle specified */ if(VolumeHandle != NULL) { /* Close a handle */ return EfiSystemTable->BootServices->CloseProtocol(VolumeHandle, &LIPGuid, EfiImageHandle, NULL); } /* Return success */ return STATUS_EFI_SUCCESS; } /** * Discovers and enumerates a block devices available to EFI system. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlEnumerateBlockDevices() { PEFI_DEVICE_PATH_PROTOCOL LastNode = NULL; PEFI_BLOCK_DEVICE_DATA ParentNode = NULL; PEFI_BLOCK_DEVICE_DATA BlockDeviceData; PEFI_BLOCK_DEVICE BlockDevice; LIST_ENTRY BlockDevices; PLIST_ENTRY ListEntry; EFI_STATUS Status; PEFI_ACPI_HID_DEVICE_PATH AcpiDevice; PEFI_HARDDRIVE_DEVICE_PATH HDPath; PEFI_BLOCK_IO_MEDIA Media; PEFI_GUID PartitionGuid; ULONG DriveNumber, PartitionNumber; USHORT DriveType; ULONG CDCount = 0, FDCount = 0, HDCount = 0, RDCount = 0; /* Initialize list entries */ RtlInitializeListHead(&BlockDevices); RtlInitializeListHead(&EfiBlockDevices); /* Discover EFI block devices and store them in linked list */ Status = BlpDiscoverEfiBlockDevices(&BlockDevices); if(Status != STATUS_EFI_SUCCESS) { BlDebugPrint(L"ERROR: Failed to discover EFI block devices (Status Code: 0x%zX)\n", Status); return Status; } /* Identify all discovered devices */ ListEntry = BlockDevices.Flink; while(ListEntry != &BlockDevices) { /* Take block device from the list */ BlockDeviceData = CONTAIN_RECORD(ListEntry, EFI_BLOCK_DEVICE_DATA, ListEntry); /* Find last node */ Status = BlpFindLastBlockDeviceNode(BlockDeviceData->DevicePath, &LastNode); if(Status != STATUS_EFI_SUCCESS) { BlDebugPrint(L"WARNING: Block device last node not found\n"); ListEntry = ListEntry->Flink; continue; } /* Set drive type to 'unknown' by default */ DriveType = XTBL_BOOT_DEVICE_UNKNOWN; /* Check last node type */ if(LastNode->Type == EFI_ACPI_DEVICE_PATH && LastNode->SubType == EFI_ACPI_DP) { /* Check for Floppy EISA identifiers */ AcpiDevice = (PEFI_ACPI_HID_DEVICE_PATH)LastNode; if(AcpiDevice->HID == 0x60441D0 || AcpiDevice->HID == 0x70041D0 || AcpiDevice->HID == 0x70141D1) { /* Floppy drive found */ Media = BlockDeviceData->BlockIo->Media; DriveType = XTBL_BOOT_DEVICE_FLOPPY; DriveNumber = FDCount++; PartitionNumber = 0; /* Print debug message */ BlDebugPrint(L"Found Floppy Disk (DiskNumber: %lu, MediaPresent: %u, RO: %u)\n", DriveNumber, Media->MediaPresent, Media->ReadOnly); } } else if(LastNode->Type == EFI_MEDIA_DEVICE_PATH) { /* Media device path found */ if(LastNode->SubType == EFI_MEDIA_CDROM_DP) { /* Optical drive found */ Media = BlockDeviceData->BlockIo->Media; DriveType = XTBL_BOOT_DEVICE_CDROM; DriveNumber = CDCount++; PartitionNumber = 0; /* Print debug message */ BlDebugPrint(L"Found CD-ROM drive (DriveNumber: %lu, MediaPresent: %u, RemovableMedia: %u, RO: %u)\n", DriveNumber, Media->MediaPresent, Media->RemovableMedia, Media->ReadOnly); } else if(LastNode->SubType == EFI_MEDIA_HARDDRIVE_DP) { /* Hard disk partition found */ Media = BlockDeviceData->BlockIo->Media; HDPath = (PEFI_HARDDRIVE_DEVICE_PATH)LastNode; DriveType = XTBL_BOOT_DEVICE_HARDDISK; DriveNumber = (HDPath->PartitionNumber == 1) ? HDCount++ : HDCount - 1; PartitionNumber = HDPath->PartitionNumber; PartitionGuid = (PEFI_GUID)HDPath->Signature; /* Print debug message */ BlDebugPrint(L"Found Hard Disk partition (DiskNumber: %lu, PartNumber: %lu, " L"MBRType: %u, GUID: {%V}, PartSize: %uB)\n", DriveNumber, PartitionNumber, HDPath->MBRType, PartitionGuid, HDPath->PartitionSize * Media->BlockSize); } else if(LastNode->SubType == EFI_MEDIA_RAMDISK_DP) { /* RAM disk found */ Media = BlockDeviceData->BlockIo->Media; DriveType = XTBL_BOOT_DEVICE_RAMDISK; DriveNumber = RDCount++; PartitionNumber = 0; /* Print debug message */ BlDebugPrint(L"Found RAM Disk (DiskNumber: %lu, MediaPresent: %u)\n", DriveNumber, Media->MediaPresent); } if(!BlpFindParentBlockDevice(&BlockDevices, BlockDeviceData, ParentNode)) { BlDebugPrint(L"WARNING: No parent device found, skipping orphaned media device path\n"); continue; } } /* Make sure the device found has valid type set */ if(DriveType != XTBL_BOOT_DEVICE_UNKNOWN) { /* Allocate memory for block device */ Status = BlAllocateMemoryPool(sizeof(EFI_BLOCK_DEVICE), (PVOID *)&BlockDevice); if(Status != STATUS_EFI_SUCCESS) { BlDebugPrint(L"ERROR: Failed to allocate memory pool for block device (Status Code: 0x%zX)\n", Status); return STATUS_EFI_OUT_OF_RESOURCES; } /* Initialize block device */ BlockDevice->DevicePath = BlpDuplicateDevicePath(BlockDeviceData->DevicePath); BlockDevice->DriveType = DriveType; BlockDevice->DriveNumber = DriveNumber; BlockDevice->PartitionNumber = PartitionNumber; BlockDevice->PartitionGuid = PartitionGuid; /* Add block device to global list */ RtlInsertTailList(&EfiBlockDevices, &BlockDevice->ListEntry); } /* Get next entry from linked list */ ListEntry = ListEntry->Flink; } /* Return success */ return STATUS_EFI_SUCCESS; } /** * Finds an EFI device path for a specified path on a given file system. * * @param FsHandle * The handle of the corresponding file system. * * @param FileSystemPath * Specifies a path on the corresponding file system. * * @param DevicePath * Specifies a pointer to the memory area, where found device path will be stored. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlFindVolumeDevicePath(IN PEFI_DEVICE_PATH_PROTOCOL FsHandle, IN CONST PWCHAR FileSystemPath, OUT PEFI_DEVICE_PATH_PROTOCOL* DevicePath) { EFI_STATUS Status; SIZE_T FsPathLength, DevicePathLength = 0; PEFI_FILEPATH_DEVICE_PATH FilePath = NULL; PEFI_DEVICE_PATH_PROTOCOL EndDevicePath; PEFI_DEVICE_PATH_PROTOCOL DevicePathHandle; /* Set local device path handle */ DevicePathHandle = FsHandle; /* Find the end device path node */ while(TRUE) { /* Make sure there is a next node */ if(*(PUSHORT)DevicePathHandle->Length == 0) { /* End device path not found */ return STATUS_EFI_NOT_FOUND; } /* Check if end device path node found */ if(DevicePathHandle->Type == EFI_END_DEVICE_PATH) { /* End device path node found */ break; } /* Get next node */ DevicePathLength += *(PUSHORT)DevicePathHandle->Length; DevicePathHandle = (PEFI_DEVICE_PATH_PROTOCOL)((PUCHAR)DevicePathHandle + *(PUSHORT)DevicePathHandle->Length); } /* Check real path length */ FsPathLength = RtlWideStringLength(FileSystemPath, 0) * sizeof(WCHAR); /* Allocate memory pool for device path */ Status = BlAllocateMemoryPool(FsPathLength + DevicePathLength + sizeof(EFI_DEVICE_PATH_PROTOCOL), (PVOID *)DevicePath); if(Status != STATUS_EFI_SUCCESS) { /* Memory allocation failure */ return Status; } /* Set file path */ RtlCopyMemory(*DevicePath, FsHandle, DevicePathLength); FilePath = (PEFI_FILEPATH_DEVICE_PATH)((PUCHAR)*DevicePath + DevicePathLength); FilePath->Header.Type = EFI_MEDIA_DEVICE_PATH; FilePath->Header.SubType = EFI_MEDIA_FILEPATH_DP; FilePath->Header.Length[0] = (UCHAR)FsPathLength + FIELD_OFFSET(EFI_FILEPATH_DEVICE_PATH, PathName) + sizeof(WCHAR); FilePath->Header.Length[1] = FilePath->Header.Length[0] >> 8; /* Set device path end node */ RtlCopyMemory(FilePath->PathName, FileSystemPath, FsPathLength + sizeof(WCHAR)); EndDevicePath = (PEFI_DEVICE_PATH_PROTOCOL)&FilePath->PathName[(FsPathLength / sizeof(WCHAR)) + 1]; EndDevicePath->Type = EFI_END_DEVICE_PATH; EndDevicePath->SubType = EFI_END_ENTIRE_DP; EndDevicePath->Length[0] = sizeof(EFI_DEVICE_PATH_PROTOCOL); EndDevicePath->Length[1] = 0; /* Return success */ return STATUS_EFI_SUCCESS; } /** * Creates a copy of the system path with EFI standard directory separators. * * @param SystemPath * Supplies a pointer to the system path. * * @param EfiPath * Supplies a pointer to the memory area, where EFI path will be stored. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlGetEfiPath(IN PWCHAR SystemPath, OUT PWCHAR *EfiPath) { SIZE_T Index, PathLength; EFI_STATUS Status; /* Get system path length */ PathLength = RtlWideStringLength(SystemPath, 0); /* Allocate memory for storing EFI path */ Status = BlAllocateMemoryPool(sizeof(WCHAR) * (PathLength + 1), (PVOID *)EfiPath); if(Status != STATUS_EFI_SUCCESS) { /* Failed to allocate memory, print error message and return status code */ BlDebugPrint(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\n", Status); return STATUS_EFI_OUT_OF_RESOURCES; } /* Make a copy of SystemPath string */ RtlCopyMemory(*EfiPath, SystemPath, sizeof(WCHAR) * (PathLength + 1)); /* Replace directory separator if needed to comply with EFI standard */ for(Index = 0; Index < PathLength; Index++) { if((*EfiPath)[Index] == L'/') { /* Replace '/' with '\' */ (*EfiPath)[Index] = L'\\'; } } /* Return success */ return STATUS_EFI_SUCCESS; } /** * Finds a volume device path based on the specified ARC name or UUID. * * @param SystemPath * An input string containing ARC/UUID path. * * @param DevicePath * Supplies a pointer to memory region where device path will be stored. * * @param Path * Supplies a pointer to the memory area, where path on device will be saved. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlGetVolumeDevicePath(IN PWCHAR SystemPath, OUT PEFI_DEVICE_PATH_PROTOCOL *DevicePath, OUT PWCHAR *ArcName, OUT PWCHAR *Path) { PEFI_BLOCK_DEVICE Device; USHORT DriveType; ULONG DriveNumber; ULONG PartNumber; PWCHAR Volume; ULONG PathLength; PLIST_ENTRY ListEntry; EFI_STATUS Status; /* Make sure this is not set */ *DevicePath = NULL; /* Find volume path and its length */ Volume = SystemPath; while(*Volume != '/' && *Volume != '\\' && *Volume != '\0') { Volume++; } PathLength = Volume - SystemPath; /* Check if valume path specified */ if(PathLength == 0) { /* No volume path available */ *Path = SystemPath; return STATUS_EFI_NOT_FOUND; } /* Check system path format */ if(SystemPath[0] == '{') { if(PathLength == GUID_STRING_LENGTH) { /* This is EFI GUID */ BlDebugPrint(L"WARNING: EFI/GPT GUID in system path is not supported\n"); return STATUS_EFI_UNSUPPORTED; } else if(PathLength == PARTUUID_STRING_LENGTH) { /* This is MBR UUID */ BlDebugPrint(L"WARNING: MBR partition UUID in system path is not supported\n"); return STATUS_EFI_UNSUPPORTED; } else { /* Invalid UUID format */ return STATUS_EFI_INVALID_PARAMETER; } } else { /* Defaults to ARC path, dissect it */ Status = BlpDissectVolumeArcPath(SystemPath, ArcName, Path, &DriveType, &DriveNumber, &PartNumber); } /* Check if volume path parsed successfully */ if(Status != STATUS_EFI_SUCCESS) { /* Failed to parse system path */ BlDebugPrint(L"ERROR: Failed to parse system path: '%s' (Status Code: 0x%zX)\n", SystemPath, Status); return Status; } /* Look for block device corresponding to dissected ARC path */ ListEntry = EfiBlockDevices.Flink; while(ListEntry != &EfiBlockDevices) { /* Check if this is the volume we are looking for */ Device = CONTAIN_RECORD(ListEntry, EFI_BLOCK_DEVICE, ListEntry); if((Device->DriveType == DriveType && Device->DriveNumber == DriveNumber && Device->PartitionNumber == PartNumber)) { /* Found volume */ *DevicePath = Device->DevicePath; break; } ListEntry = ListEntry->Flink; } /* Check if volume was found */ if(*DevicePath == NULL) { /* Failed to find volume */ BlDebugPrint(L"ERROR: Volume (DriveType: %u, DriveNumber: %lu, PartNumber: %lu) not found\n", DriveType, DriveNumber, PartNumber); return STATUS_EFI_NOT_FOUND; } /* return success */ return STATUS_EFI_SUCCESS; } /** * This routine opens an EFI volume and corresponding filesystem. * * @param DevicePath * Specifies a device path of the volume to open. If not specifies, uses image protocol by default. * * @param DiskHandle * The handle of the opened disk volume. * * @param FsHandle * The handle of the opened file system. * * @return This routine returns status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlOpenVolume(IN PEFI_DEVICE_PATH_PROTOCOL DevicePath, OUT PEFI_HANDLE DiskHandle, OUT PEFI_FILE_HANDLE *FsHandle) { EFI_GUID SFSGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; EFI_GUID LIPGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID; PEFI_SIMPLE_FILE_SYSTEM_PROTOCOL FileSystemProtocol; PEFI_LOADED_IMAGE_PROTOCOL ImageProtocol; EFI_STATUS Status; /* Check if device path has been passed or not */ if(DevicePath != NULL) { /* Locate the device path */ Status = EfiSystemTable->BootServices->LocateDevicePath(&SFSGuid, &DevicePath, DiskHandle); if(Status != STATUS_EFI_SUCCESS) { /* Failed to locate device path */ return Status; } } else { /* Open the image protocol if no device path specified */ Status = EfiSystemTable->BootServices->OpenProtocol(EfiImageHandle, &LIPGuid, (PVOID *)&ImageProtocol, EfiImageHandle, NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); if(Status != STATUS_EFI_SUCCESS) { /* Failed to open image protocol */ return Status; } /* Store disk handle */ *DiskHandle = ImageProtocol->DeviceHandle; } /* Open the filesystem protocol */ Status = EfiSystemTable->BootServices->OpenProtocol(*DiskHandle, &SFSGuid, (PVOID *)&FileSystemProtocol, EfiImageHandle, NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); /* Check if filesystem protocol opened successfully */ if(Status != STATUS_EFI_SUCCESS) { /* Failed to open the filesystem protocol, close volume */ BlCloseVolume(*DiskHandle); return Status; } /* Open the corresponding filesystem */ Status = FileSystemProtocol->OpenVolume(FileSystemProtocol, FsHandle); if(Status != STATUS_EFI_SUCCESS) { /* Failed to open the filesystem, close volume */ BlCloseVolume(*DiskHandle); return Status; } /* Return success */ return STATUS_EFI_SUCCESS; } /** * Reads data from the file. * * @param DirHandle * Supplies a handle of the opened filesystem directory. * * @param FileName * Supplies the name of the file to read. * * @param FileData * Provides a buffer to store the data read from the file. * * @param FileSize * Provides a pointer to the variable to store a size of the buffer. * * @return This routine returns status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlReadFile(IN PEFI_FILE_HANDLE DirHandle, IN CONST PWCHAR FileName, OUT PVOID *FileData, OUT PSIZE_T FileSize) { EFI_GUID FileInfoGuid = EFI_FILE_INFO_PROTOCOL_GUID; EFI_PHYSICAL_ADDRESS Address; PEFI_FILE_HANDLE FileHandle; PEFI_FILE_INFO FileInfo; EFI_STATUS Status; UINT_PTR ReadSize; SIZE_T Pages; Status = DirHandle->Open(DirHandle, &FileHandle, FileName, EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM); if(Status != STATUS_EFI_SUCCESS) { /* Failed to open file */ return Status; } /* Set required size for getting file information */ ReadSize = sizeof(EFI_FILE_INFO) + 32; /* Allocate necessary amount of memory */ Status = BlAllocateMemoryPool(ReadSize, (PVOID *)&FileInfo); if(Status != STATUS_EFI_SUCCESS) { /* Memory allocation failure */ FileHandle->Close(FileHandle); return Status; } /* First attempt to get file information */ FileHandle->GetInfo(FileHandle, &FileInfoGuid, &ReadSize, FileInfo); if(Status == STATUS_EFI_BUFFER_TOO_SMALL) { /* Buffer is too small, but EFI tells the required size, so reallocate */ BlFreeMemoryPool(&FileInfo); Status = BlAllocateMemoryPool(ReadSize, (PVOID *)&FileInfo); if(Status != STATUS_EFI_SUCCESS) { /* Memory allocation failure */ FileHandle->Close(FileHandle); return Status; } /* Second attempt to get file information */ Status = FileHandle->GetInfo(FileHandle, &FileInfoGuid, &ReadSize, FileInfo); } /* Check if file information got successfully */ if(Status != STATUS_EFI_SUCCESS) { /* Unable to get file information */ FileHandle->Close(FileHandle); BlFreeMemoryPool(&FileInfo); return Status; } /* Store file size and calculate number of pages */ *FileSize = FileInfo->FileSize; Pages = EFI_SIZE_TO_PAGES(FileInfo->FileSize); /* Allocate pages */ Status = BlAllocateMemoryPages(Pages, &Address); if(Status != STATUS_EFI_SUCCESS) { /* Pages allocation failure */ FileHandle->Close(FileHandle); BlFreeMemoryPool(&FileInfo); return Status; } /* Calculate number of bytes to read and zero memory */ ReadSize = Pages * EFI_PAGE_SIZE; *FileData = (PCHAR)(UINT_PTR)Address; RtlZeroMemory(*FileData, ReadSize); /* Read data from the file */ Status = FileHandle->Read(FileHandle, &ReadSize, *FileData); if(Status != STATUS_EFI_SUCCESS) { /* Failed to read data */ FileHandle->Close(FileHandle); BlFreeMemoryPool(&FileInfo); BlFreeMemoryPages(Pages, (EFI_PHYSICAL_ADDRESS)(UINT_PTR)*FileData); return Status; } /* Close handle and free memory */ FileHandle->Close(FileHandle); BlFreeMemoryPool(FileInfo); /* Return success */ return STATUS_EFI_SUCCESS; } /** * Gets a list of block devices from an EFI enabled BIOS. * * @param BlockDevices * Supplies a pointer to a variable to receive a list of EFI block devices. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlpDiscoverEfiBlockDevices(OUT PLIST_ENTRY BlockDevices) { EFI_GUID DevicePathGuid = EFI_DEVICE_PATH_PROTOCOL_GUID; EFI_GUID IoGuid = EFI_BLOCK_IO_PROTOCOL_GUID; PEFI_DEVICE_PATH_PROTOCOL DevicePath; PEFI_BLOCK_DEVICE_DATA BlockDevice; UINT_PTR HandlesCount, Index; PEFI_HANDLE Handles = NULL; PEFI_BLOCK_IO_PROTOCOL Io; EFI_STATUS Status; /* Locate handles which support the disk I/O interface */ Status = BlLocateProtocolHandles(&Handles, &HandlesCount, &IoGuid); if(Status != STATUS_EFI_SUCCESS) { /* Failed to locate handles */ BlDebugPrint(L"ERROR: Failed to locate block devices handles (Status Code: 0x%zX)\n", Status); return Status; } /* Iterate through all handles */ for(Index = 0; Index < HandlesCount; Index++) { /* Print debug message */ BlDebugPrint(L"Opening %lu block device from %lu discovered\n", Index + 1, HandlesCount); /* Open I/O protocol for given handle */ Io = NULL; Status = BlOpenProtocolHandle(Handles[Index], (PVOID *)&Io, &IoGuid); if(Status != STATUS_EFI_SUCCESS || Io == NULL) { /* Failed to open I/O protocol, skip it */ BlDebugPrint(L"WARNING: Failed to open EFI Block I/O protocol (Status Code: 0x%zX)\n", Status); continue; } /* Check if this is iPXE stub */ if(Io->Media && Io->Media->BlockSize == 1 && Io->Media->MediaId == 0x69505845U) { /* Skip stub as it is non-functional */ BlDebugPrint(L"WARNING: Skipping iPXE stub block I/O protocol"); continue; } /* Check if DevicePath protocol is supported by this handle */ DevicePath = NULL; Status = EfiSystemTable->BootServices->HandleProtocol(Handles[Index], &DevicePathGuid, (PVOID *)&DevicePath); if(Status != STATUS_EFI_SUCCESS || DevicePath == NULL) { /* Device failed to handle DP protocol */ BlDebugPrint(L"WARNING: Unable to open DevicePath protocol (Status Code: 0x%zX)\n", Status); EfiSystemTable->BootServices->CloseProtocol(Handles[Index], &IoGuid, EfiImageHandle, NULL); continue; } /* Allocate memory for block device */ Status = BlAllocateMemoryPool(sizeof(*BlockDevice), (PVOID *)&BlockDevice); if(Status != STATUS_EFI_SUCCESS) { /* Memory allocation failure */ BlDebugPrint(L"ERROR: Failed to allocate memory pool for block device (Status Code: 0x%zX)\n", Status); EfiSystemTable->BootServices->CloseProtocol(Handles[Index], &DevicePathGuid, EfiImageHandle, NULL); EfiSystemTable->BootServices->CloseProtocol(Handles[Index], &IoGuid, EfiImageHandle, NULL); return Status; } /* Store new block device into a linked list */ BlockDevice->BlockIo = Io; BlockDevice->DevicePath = DevicePath; RtlInsertTailList(BlockDevices, &BlockDevice->ListEntry); } /* Free handles buffer */ BlFreeMemoryPool(Handles); /* Return success */ return STATUS_EFI_SUCCESS; } /** * Dissects a specified ARC name and provides detailed information about corresponding device and on disk path. * * @param SystemPath * Supplies an input ARC path. * * @param Path * Specifies a pointer to variable, where on disk path will be saved. * * @param DriveType * Supplies a pointer to the variable that receives a drive type. * * @param DriveNumber * Supplies a pointer to the variable that receives a drive number. * * @param PartNumber * Supplies a pointer to the variable that receives a parition number if applicable, otherwise stores 0 (zero). * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlpDissectVolumeArcPath(IN PWCHAR SystemPath, OUT PWCHAR *ArcName, OUT PWCHAR *Path, OUT PUSHORT DriveType, OUT PULONG DriveNumber, OUT PULONG PartNumber) { PWCHAR ArcPath, LocalArcName; ULONG ArcLength = 0; /* Set default values */ *DriveType = XTBL_BOOT_DEVICE_UNKNOWN; *DriveNumber = 0; *PartNumber = 0; /* Look for the ARC path */ if(RtlCompareWideStringInsensitive(SystemPath, L"ramdisk(0)", 0) == 0) { /* This is RAM disk */ ArcLength = 10; *DriveType = XTBL_BOOT_DEVICE_RAMDISK; } else if(RtlCompareWideStringInsensitive(SystemPath, L"multi(0)disk(0)", 0) == 0) { /* This is a multi-disk port */ ArcLength = 15; ArcPath = SystemPath + ArcLength; /* Check for disk type */ if(RtlCompareWideStringInsensitive(ArcPath, L"cdrom(", 0) == 0) { /* This is an optical drive */ ArcLength += 6; /* Find drive number */ while(SystemPath[ArcLength] != ')' && SystemPath[ArcLength] != '\0') { if(SystemPath[ArcLength] >= '0' && SystemPath[ArcLength] <= '9') { /* Calculate drive number */ *DriveNumber *= 10; *DriveNumber += SystemPath[ArcLength] - '0'; } ArcLength++; } /* Set proper drive type */ *DriveType = XTBL_BOOT_DEVICE_CDROM; ArcLength++; } else if(RtlCompareWideStringInsensitive(ArcPath, L"fdisk(", 0) == 0) { /* This is a floppy drive */ ArcLength += 6; /* Find drive number */ while(SystemPath[ArcLength] != ')' && SystemPath[ArcLength] != '\0') { if(SystemPath[ArcLength] >= '0' && SystemPath[ArcLength] <= '9') { /* Calculate drive number */ *DriveNumber *= 10; *DriveNumber += SystemPath[ArcLength] - '0'; } ArcLength++; } /* Set proper drive type */ *DriveType = XTBL_BOOT_DEVICE_FLOPPY; ArcLength++; } else if(RtlCompareWideStringInsensitive(ArcPath, L"rdisk(", 0) == 0) { /* This is a hard disk */ ArcLength += 6; /* Find drive number */ while(SystemPath[ArcLength] != ')' && SystemPath[ArcLength] != '\0') { if(SystemPath[ArcLength] >= '0' && SystemPath[ArcLength] <= '9') { /* Calculate drive number */ *DriveNumber *= 10; *DriveNumber += SystemPath[ArcLength] - '0'; } ArcLength++; } /* Set proper drive type */ *DriveType = XTBL_BOOT_DEVICE_HARDDISK; ArcLength++; ArcPath = SystemPath + ArcLength; /* Look for a partition */ if(RtlCompareWideStringInsensitive(ArcPath, L"partition(", 0) == 0) { /* Partition information found */ ArcLength += 10; /* Find partition number */ while(SystemPath[ArcLength] != ')' && SystemPath[ArcLength] != '\0') { if(SystemPath[ArcLength] >= '0' && SystemPath[ArcLength] <= '9') { /* Calculate partition number */ *PartNumber *= 10; *PartNumber += SystemPath[ArcLength] - '0'; } ArcLength++; } ArcLength++; } } else { /* Unsupported disk type */ return STATUS_EFI_UNSUPPORTED; } } else { /* Unsupported ARC path */ return STATUS_EFI_UNSUPPORTED; } /* Store the path if possible */ if(Path) { *Path = SystemPath + ArcLength; } /* Store ARC name if possible */ if(ArcName) { BlAllocateMemoryPool(ArcLength * sizeof(WCHAR), (PVOID *)&LocalArcName); RtlCopyMemory(LocalArcName, SystemPath, ArcLength * sizeof(WCHAR)); LocalArcName[ArcLength] = '\0'; *ArcName = LocalArcName; } /* Return success */ return STATUS_EFI_SUCCESS; } /** * This routine duplicates a device path object. * * @param DevicePath * An input device path that is going to be clonned. * * @return Returns a duplicate of input device path. * * @since XT 1.0 */ XTCDECL PEFI_DEVICE_PATH_PROTOCOL BlpDuplicateDevicePath(IN PEFI_DEVICE_PATH_PROTOCOL DevicePath) { PEFI_DEVICE_PATH_PROTOCOL DevicePathNode; PEFI_DEVICE_PATH_PROTOCOL DevicePathClone; EFI_STATUS Status; UINT Length = 0; DevicePathNode = DevicePath; /* Get the device path length */ while(TRUE) { Length += *(PUSHORT)DevicePath->Length; if(DevicePathNode->Type == EFI_END_DEVICE_PATH) { break; } DevicePathNode = (PEFI_DEVICE_PATH_PROTOCOL)((PUCHAR)DevicePathNode + *(PUSHORT)DevicePath->Length); } /* Check length */ if(Length == 0) { /* Nothing to duplicate */ return NULL; } /* Allocate memory for the new device path */ Status = BlAllocateMemoryPool(Length, (PVOID *)&DevicePathClone); if(Status != STATUS_EFI_SUCCESS) { /* Failed to allocate memory */ BlDebugPrint(L"ERROR: Failed to allocate memory pool for device path duplicate (Status Code: 0x%zX)\n", Status); return NULL; } /* Copy the device path */ RtlCopyMemory(DevicePathClone, DevicePath, Length); /* Return the cloned object */ return DevicePathClone; } /** * Attempts to find a last node of the EFI block device. * * @param DevicePath * An input device path. * * @param LastNode * A pointer to the buffer where last node will be stored. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlpFindLastBlockDeviceNode(IN PEFI_DEVICE_PATH_PROTOCOL DevicePath, OUT PEFI_DEVICE_PATH_PROTOCOL *LastNode) { PEFI_DEVICE_PATH_PROTOCOL EndNode, NextNode; /* Make sure end is not reached yet */ if(DevicePath->Type == EFI_END_DEVICE_PATH) { /* End reached, nothing to do */ LastNode = NULL; return STATUS_EFI_INVALID_PARAMETER; } /* Fast forward to the last node */ EndNode = DevicePath; while(EndNode->Type != EFI_END_DEVICE_PATH) { NextNode = EndNode; EndNode = (PEFI_DEVICE_PATH_PROTOCOL)((PUCHAR)EndNode + *(PUSHORT)EndNode->Length); } /* Store last node found */ *LastNode = NextNode; /* Return success */ return STATUS_EFI_SUCCESS; } /** * This routine attempts to find a parent device of the provided block device. * * @param BlockDevice * A linked list of discovered block devices. * * @param ChildNode * Block device that is looking for a parent device. * * @param ParentNode * A pointer to memory region where pointer to the parent node will be provided. * * @return This routine returns TRUE if parent node has been found, or FALSE otherwise. * * @since XT 1.0 */ XTCDECL BOOLEAN BlpFindParentBlockDevice(IN PLIST_ENTRY BlockDevices, IN PEFI_BLOCK_DEVICE_DATA ChildNode, OUT PEFI_BLOCK_DEVICE_DATA ParentNode) { PEFI_DEVICE_PATH_PROTOCOL ChildDevicePath, ParentDevicePath; PEFI_BLOCK_DEVICE_DATA BlockDeviceData; UINT ChildLength, ParentLength; PLIST_ENTRY ListEntry; ListEntry = BlockDevices->Flink; while(ListEntry != BlockDevices) { /* Take block device from the list */ BlockDeviceData = CONTAIN_RECORD(ListEntry, EFI_BLOCK_DEVICE_DATA, ListEntry); ChildDevicePath = ChildNode->DevicePath; ParentDevicePath = BlockDeviceData->DevicePath; /* Iterate nodes */ while(TRUE) { /* Check if the parent device is a match */ if(ParentDevicePath->Type == EFI_END_DEVICE_PATH) { /* Parent device is a match */ ParentNode = BlockDeviceData; return TRUE; } /* Get child and parent node lengths */ ChildLength = *(PUSHORT)ChildDevicePath->Length; ParentLength = *(PUSHORT)ParentDevicePath->Length; /* Check if lengths match */ if(ChildLength != ParentLength) { /* Lengths do not match, this is not a valid parent */ break; } /* Move to the next child and parent nodes */ ChildDevicePath = (PEFI_DEVICE_PATH_PROTOCOL)((PUCHAR)ChildDevicePath + ChildLength); ParentDevicePath = (PEFI_DEVICE_PATH_PROTOCOL)((PUCHAR)ParentDevicePath + ParentLength); } /* Get next entry from linked list */ ListEntry = ListEntry->Flink; } /* Apparently not found a parent node */ return FALSE; }