From 2356f4da54a320988e3820bf23dda07beea4a070 Mon Sep 17 00:00:00 2001 From: Rafal Kupiec Date: Wed, 6 Dec 2023 23:09:43 +0100 Subject: [PATCH] Import volumes suppport --- xtldr2/CMakeLists.txt | 1 + xtldr2/globals.c | 3 + xtldr2/includes/bootman.h | 55 +++ xtldr2/includes/globals.h | 3 + xtldr2/volume.c | 900 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 962 insertions(+) create mode 100644 xtldr2/volume.c diff --git a/xtldr2/CMakeLists.txt b/xtldr2/CMakeLists.txt index 2004d41..cda9b16 100644 --- a/xtldr2/CMakeLists.txt +++ b/xtldr2/CMakeLists.txt @@ -17,6 +17,7 @@ list(APPEND XTLDR_SOURCE ${XTLDR_SOURCE_DIR}/memory.c ${XTLDR_SOURCE_DIR}/protocol.c ${XTLDR_SOURCE_DIR}/string.c + # ${XTLDR_SOURCE_DIR}/volume.c ${XTLDR_SOURCE_DIR}/xtldr.c) # Link static XTLDR library diff --git a/xtldr2/globals.c b/xtldr2/globals.c index 6017862..ae67696 100644 --- a/xtldr2/globals.c +++ b/xtldr2/globals.c @@ -21,6 +21,9 @@ CPPORT BlpSerialPort; /* XT Boot Loader status data */ XTBL_STATUS BlpStatus = {0}; +/* List of available block devices */ +LIST_ENTRY EfiBlockDevices; + /* EFI Image Handle */ EFI_HANDLE EfiImageHandle; diff --git a/xtldr2/includes/bootman.h b/xtldr2/includes/bootman.h index e0ad29c..2a1647a 100644 --- a/xtldr2/includes/bootman.h +++ b/xtldr2/includes/bootman.h @@ -16,10 +16,31 @@ typedef VOID (BMPRINTCHAR)(IN USHORT Character); /* XTLDR routines forward references */ +XTCDECL +EFI_STATUS +BlCloseVolume(IN PEFI_HANDLE VolumeHandle); + +XTCDECL +EFI_STATUS +BlEnumerateBlockDevices(); + XTCDECL EFI_STATUS BlExitBootServices(IN UINT_PTR MapKey); +XTCDECL +EFI_STATUS +BlFindVolumeDevicePath(IN PEFI_DEVICE_PATH_PROTOCOL FsHandle, + IN CONST PWCHAR FileSystemPath, + OUT PEFI_DEVICE_PATH_PROTOCOL* DevicePath); + +XTCDECL +EFI_STATUS +BlGetVolumeDevicePath(IN PCHAR SystemPath, + OUT PEFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT PCHAR *ArcName, + OUT PCHAR *Path); + XTCDECL EFI_STATUS BlMemoryAllocatePages(IN UINT64 Pages, @@ -30,6 +51,12 @@ EFI_STATUS BlMemoryAllocatePool(IN UINT_PTR Size, OUT PVOID *Memory); +XTCDECL +EFI_STATUS +BlOpenVolume(IN PEFI_DEVICE_PATH_PROTOCOL DevicePath, + OUT PEFI_HANDLE DiskHandle, + OUT PEFI_FILE_HANDLE *FsHandle); + XTCDECL VOID BlConsoleClearScreen(); @@ -79,6 +106,34 @@ XTCDECL EFI_STATUS BlpActivateSerialIOController(); +XTCDECL +EFI_STATUS +BlpDiscoverEfiBlockDevices(OUT PLIST_ENTRY BlockDevices); + +XTCDECL +EFI_STATUS +BlpDissectVolumeArcPath(IN PCHAR SystemPath, + OUT PCHAR *ArcName, + OUT PCHAR *Path, + OUT PUSHORT DriveType, + OUT PULONG DriveNumber, + OUT PULONG PartNumber); + +XTCDECL +PEFI_DEVICE_PATH_PROTOCOL +BlpDuplicateDevicePath(IN PEFI_DEVICE_PATH_PROTOCOL DevicePath); + +XTCDECL +EFI_STATUS +BlpFindLastBlockDeviceNode(IN PEFI_DEVICE_PATH_PROTOCOL DevicePath, + OUT PEFI_DEVICE_PATH_PROTOCOL *LastNode); + +XTCDECL +BOOLEAN +BlpFindParentBlockDevice(IN PLIST_ENTRY BlockDevices, + IN PEFI_BLOCK_DEVICE_DATA ChildNode, + OUT PEFI_BLOCK_DEVICE_DATA ParentNode); + XTCDECL VOID BlpParseCommandLineOptions(VOID); diff --git a/xtldr2/includes/globals.h b/xtldr2/includes/globals.h index 9d09f76..c43b052 100644 --- a/xtldr2/includes/globals.h +++ b/xtldr2/includes/globals.h @@ -24,6 +24,9 @@ EXTERN CPPORT BlpSerialPort; /* XT Boot Loader status data */ EXTERN XTBL_STATUS BlpStatus; +/* List of available block devices */ +EXTERN LIST_ENTRY EfiBlockDevices; + /* EFI Image Handle */ EXTERN EFI_HANDLE EfiImageHandle; diff --git a/xtldr2/volume.c b/xtldr2/volume.c new file mode 100644 index 0000000..8176190 --- /dev/null +++ b/xtldr2/volume.c @@ -0,0 +1,900 @@ +/** + * 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: %lx)\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: {%g}, PartSize: %luB)\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 = BlMemoryAllocatePool(sizeof(EFI_BLOCK_DEVICE), (PVOID *)&BlockDevice); + if(Status != STATUS_EFI_SUCCESS) + { + BlDebugPrint(L"ERROR: Unable to allocate memory pool for block device (status code: %lx)\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 = BlMemoryAllocatePool(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; +} + +/** + * 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 PCHAR SystemPath, + OUT PEFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT PCHAR *ArcName, + OUT PCHAR *Path) +{ + PEFI_BLOCK_DEVICE Device; + USHORT DriveType; + ULONG DriveNumber; + ULONG PartNumber; + PCHAR 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"EFI/GPT GUID in system path is not supported yet\n"); + return STATUS_EFI_UNSUPPORTED; + } + else if(PathLength == PARTUUID_STRING_LENGTH) + { + /* This is MBR UUID */ + BlDebugPrint(L"MBR partition UUID in system path is not supported yet\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"Failed to parse system path: '%s' with status code: %lx\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"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; +} + +/** + * 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 = EfiSystemTable->BootServices->LocateHandleBuffer(ByProtocol, &IoGuid, NULL, &HandlesCount, &Handles); + if(Status != STATUS_EFI_SUCCESS) + { + /* Failed to locate handles */ + BlDebugPrint(L"ERROR: Failed to locate block devices handles (status code: %lx)\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 = EfiSystemTable->BootServices->OpenProtocol(Handles[Index], &IoGuid, (PVOID *)&Io, EfiImageHandle, + NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); + 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: %lx)\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: 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: %lx)\n", Status); + EfiSystemTable->BootServices->CloseProtocol(Handles[Index], &IoGuid, EfiImageHandle, NULL); + continue; + } + + /* Allocate memory for block device */ + Status = BlMemoryAllocatePool(sizeof(*BlockDevice), (PVOID *)&BlockDevice); + if(Status != STATUS_EFI_SUCCESS) + { + /* Memory allocation failure */ + BlDebugPrint(L"ERROR: Unable to allocate memory pool for block device (status code: %lx)\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 */ + BlMemoryFreePool(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 PCHAR SystemPath, + OUT PCHAR *ArcName, + OUT PCHAR *Path, + OUT PUSHORT DriveType, + OUT PULONG DriveNumber, + OUT PULONG PartNumber) +{ + PCHAR ArcPath, LocalArcName; + ULONG ArcLength = 0; + + /* Set default values */ + *DriveType = XTBL_BOOT_DEVICE_UNKNOWN; + *DriveNumber = 0; + *PartNumber = 0; + + /* Look for the ARC path */ + if(RtlCompareStringInsensitive(SystemPath, "ramdisk(0)", 0) == 0) + { + /* This is RAM disk */ + ArcLength = 10; + *DriveType = XTBL_BOOT_DEVICE_RAMDISK; + } + else if(RtlCompareStringInsensitive(SystemPath, "multi(0)disk(0)", 0) == 0) + { + /* This is a multi-disk port */ + ArcLength = 15; + ArcPath = SystemPath + ArcLength; + + /* Check for disk type */ + if(RtlCompareStringInsensitive(ArcPath, "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(RtlCompareStringInsensitive(ArcPath, "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(RtlCompareStringInsensitive(ArcPath, "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(RtlCompareStringInsensitive(ArcPath, "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) + { + BlMemoryAllocatePool(ArcLength, (PVOID *)&LocalArcName); + RtlCopyMemory(LocalArcName, SystemPath, ArcLength); + 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 += *(PUINT16)DevicePath->Length; + if(DevicePathNode->Type == EFI_END_DEVICE_PATH) + { + break; + } + DevicePathNode = (PEFI_DEVICE_PATH_PROTOCOL)((PUCHAR)DevicePathNode + *(PUINT16)DevicePath->Length); + } + + /* Check length */ + if(Length == 0) + { + /* Nothing to duplicate */ + return NULL; + } + + /* Allocate memory for the new device path */ + Status = BlMemoryAllocatePool(Length, (PVOID *)&DevicePathClone); + if(Status != STATUS_EFI_SUCCESS) + { + /* Failed to allocate memory */ + BlDebugPrint(L"ERROR: Unable to allocate memory pool for device path duplicate\n"); + 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 = *(PUINT16)ChildDevicePath->Length; + ParentLength = *(PUINT16)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; +}