/* * COPYRIGHT: See COPYING.ARM in the top level directory * PROJECT: ReactOS UEFI Boot Library * FILE: boot/environ/lib/io/device.c * PURPOSE: Boot Library Device Management Routines * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org) */ /* INCLUDES ******************************************************************/ #include "bl.h" /* DATA VARIABLES ************************************************************/ typedef struct _BL_DEVICE_IO_INFORMATION { ULONGLONG ReadCount; ULONGLONG WriteCount; } BL_DEVICE_IO_INFORMATION, *PBL_DEVICE_IO_INFORMATION; LIST_ENTRY DmRegisteredDevices; ULONG DmTableEntries; LIST_ENTRY DmRegisteredDevices; PVOID* DmDeviceTable; BL_DEVICE_IO_INFORMATION DmDeviceIoInformation; /* FUNCTIONS *****************************************************************/ typedef struct _BL_REGISTERED_DEVICE { LIST_ENTRY ListEntry; BL_DEVICE_CALLBACKS Callbacks; } BL_REGISTERED_DEVICE, *PBL_REGISTERED_DEVICE; PVOID* BlockIoDeviceTable; ULONG BlockIoDeviceTableEntries; ULONG BlockIoFirmwareRemovableDiskCount; ULONG BlockIoFirmwareRawDiskCount; ULONG BlockIoFirmwareCdromCount; PVOID BlockIopAlignedBuffer; ULONG BlockIopAlignedBufferSize; PVOID BlockIopPartialBlockBuffer; ULONG BlockIopPartialBlockBufferSize; PVOID BlockIopPrefetchBuffer; PVOID BlockIopReadBlockBuffer; ULONG BlockIopReadBlockBufferSize; ULONG HashTableId; BOOLEAN BlockIoInitialized; NTSTATUS BlockIoOpen ( _In_ PBL_DEVICE_DESCRIPTOR Device, _In_ PBL_DEVICE_ENTRY DeviceEntry ); NTSTATUS BlockIoGetInformation ( _In_ PBL_DEVICE_ENTRY DeviceEntry, _Out_ PBL_DEVICE_INFORMATION DeviceInformation ); NTSTATUS BlockIoSetInformation ( _In_ PBL_DEVICE_ENTRY DeviceEntry, _Out_ PBL_DEVICE_INFORMATION DeviceInformation ); NTSTATUS BlockIoRead ( _In_ PBL_DEVICE_ENTRY DeviceEntry, _In_ PVOID Buffer, _In_ ULONG Size, _Out_ PULONG BytesRead ); BL_DEVICE_CALLBACKS BlockIoDeviceFunctionTable = { NULL, BlockIoOpen, NULL, BlockIoRead, NULL, BlockIoGetInformation, BlockIoSetInformation }; NTSTATUS BlockIoFirmwareWrite ( _In_ PBL_BLOCK_DEVICE BlockDevice, _In_ PVOID Buffer, _In_ ULONGLONG Block, _In_ ULONGLONG BlockCount ) { return STATUS_NOT_IMPLEMENTED; } NTSTATUS BlockIoFirmwareRead ( _In_ PBL_BLOCK_DEVICE BlockDevice, _In_ PVOID Buffer, _In_ ULONGLONG Block, _In_ ULONGLONG BlockCount ) { NTSTATUS Status; EFI_BLOCK_IO *BlockProtocol; BL_ARCH_MODE OldMode; EFI_STATUS EfiStatus; ULONG FailureCount; for (FailureCount = 0, Status = STATUS_SUCCESS; FailureCount < 2 && NT_SUCCESS(Status); FailureCount++) { BlockProtocol = BlockDevice->Protocol; OldMode = CurrentExecutionContext->Mode; if (CurrentExecutionContext->Mode != 1) { Status = STATUS_NOT_IMPLEMENTED; break; } //EfiPrintf(L"EFI Reading BLOCK %d off media %lx (%d blocks)\r\n", //Block, BlockProtocol->Media->MediaId, BlockCount); EfiStatus = BlockProtocol->ReadBlocks(BlockProtocol, BlockProtocol->Media->MediaId, Block, BlockProtocol->Media->BlockSize * BlockCount, Buffer); if (EfiStatus == EFI_SUCCESS) { //EfiPrintf(L"EFI Read complete into buffer\r\n"); //EfiPrintf(L"Buffer data: %lx %lx %lx %lx\r\n", *(PULONG)Buffer, *((PULONG)Buffer + 1), *((PULONG)Buffer + 2), *((PULONG)Buffer + 3)); } if (OldMode != 1) { BlpArchSwitchContext(OldMode); } Status = EfiGetNtStatusCode(EfiStatus); if (Status != STATUS_MEDIA_CHANGED) { break; } EfiCloseProtocol(BlockDevice->Handle, &EfiBlockIoProtocol); Status = EfiOpenProtocol(BlockDevice->Handle, &EfiBlockIoProtocol, (PVOID*)BlockDevice->Protocol); } return Status; } NTSTATUS BlockIopFirmwareOperation ( PBL_DEVICE_ENTRY DeviceEntry, _In_ PVOID Buffer, _In_ ULONGLONG Block, _In_ ULONGLONG BlockCount, _In_ ULONG OperationType ) { ULONG FailureCount; PBL_BLOCK_DEVICE BlockDevice; NTSTATUS Status; BlockDevice = DeviceEntry->DeviceSpecificData; if (OperationType == 1) { for (FailureCount = 0; FailureCount < 3; FailureCount++) { Status = BlockIoFirmwareWrite(BlockDevice, Buffer, Block, BlockCount); if (Status >= 0) { break; } } } else { for (FailureCount = 0; FailureCount < 3; FailureCount++) { Status = BlockIoFirmwareRead(BlockDevice, Buffer, Block, BlockCount); if (Status >= 0) { break; } } } return Status; } NTSTATUS BlockIopFreeAlignedBuffer ( _Inout_ PVOID* Buffer, _Inout_ PULONG BufferSize ) { NTSTATUS Status; if (*BufferSize) { Status = MmPapFreePages(*Buffer, BL_MM_INCLUDE_MAPPED_ALLOCATED); *Buffer = NULL; *BufferSize = 0; } else { Status = STATUS_SUCCESS; } return Status; } NTSTATUS BlockIopAllocateAlignedBuffer ( _Inout_ PVOID* Buffer, _Inout_ PULONG BufferSize, _In_ ULONG Size, _In_ ULONG Alignment ) { NTSTATUS Status; if (!Alignment) { ++Alignment; } Status = STATUS_SUCCESS; if ((Size > *BufferSize) || ((Alignment - 1) & (ULONG_PTR)*Buffer)) { BlockIopFreeAlignedBuffer(Buffer, BufferSize); *BufferSize = ROUND_TO_PAGES(Size); Status = MmPapAllocatePagesInRange(Buffer, BlLoaderDeviceMemory, *BufferSize >> PAGE_SHIFT, 0, Alignment >> PAGE_SHIFT, NULL, 0); if (!NT_SUCCESS(Status)) { *BufferSize = 0; } } return Status; } NTSTATUS BlockIopReadUsingPrefetch ( _In_ PBL_DEVICE_ENTRY DeviceEntry, _In_ PVOID Buffer, _In_ ULONG BlockCount ) { EfiPrintf(L"No prefetch support\r\n"); return STATUS_NOT_IMPLEMENTED; } NTSTATUS BlockIopOperation ( _In_ PBL_DEVICE_ENTRY DeviceEntry, _In_ PVOID Buffer, _In_ ULONG BlockCount, _In_ ULONG OperationType ) { PBL_BLOCK_DEVICE BlockDevice; ULONG BufferSize, Alignment; ULONGLONG Offset; NTSTATUS Status; BlockDevice = DeviceEntry->DeviceSpecificData; BufferSize = BlockDevice->BlockSize * BlockCount; Offset = BlockDevice->Block + BlockDevice->StartOffset; if ((BlockDevice->LastBlock + 1) < (BlockDevice->Block + BlockCount)) { EfiPrintf(L"Read past end of device\r\n"); return STATUS_INVALID_PARAMETER; } Alignment = BlockDevice->Alignment; if (!(Alignment) || !((Alignment - 1) & (ULONG_PTR)Buffer)) { Status = BlockIopFirmwareOperation(DeviceEntry, Buffer, Offset, BlockCount, OperationType); if (!NT_SUCCESS(Status)) { EfiPrintf(L"EFI op failed: %lx\r\n", Status); return Status; } return STATUS_SUCCESS; } Status = BlockIopAllocateAlignedBuffer(&BlockIopAlignedBuffer, &BlockIopAlignedBufferSize, BufferSize, BlockDevice->Alignment); if (!NT_SUCCESS(Status)) { EfiPrintf(L"No memory for align\r\n"); return STATUS_NO_MEMORY; } if (OperationType == 1) { RtlCopyMemory(BlockIopAlignedBuffer, Buffer, BufferSize); } Status = BlockIopFirmwareOperation(DeviceEntry, BlockIopAlignedBuffer, Offset, BlockCount, OperationType); if (!NT_SUCCESS(Status)) { return Status; } if (!OperationType) { RtlCopyMemory(Buffer, BlockIopAlignedBuffer, BufferSize); } return STATUS_SUCCESS; } NTSTATUS BlockIopReadWriteVirtualDevice ( _In_ PBL_DEVICE_ENTRY DeviceEntry, _In_ PVOID Buffer, _In_ ULONG Size, _In_ ULONG Operation, _Out_ PULONG BytesRead ) { return STATUS_NOT_IMPLEMENTED; } NTSTATUS BlockIopReadPhysicalDevice ( _In_ PBL_DEVICE_ENTRY DeviceEntry, _In_ PVOID Buffer, _In_ ULONG Size, _Out_ PULONG BytesRead ) { PBL_BLOCK_DEVICE BlockDevice; PVOID ReadBuffer; ULONGLONG OffsetEnd, AlignedOffsetEnd, Offset; NTSTATUS Status; BlockDevice = DeviceEntry->DeviceSpecificData; ReadBuffer = Buffer; OffsetEnd = Size + BlockDevice->Offset; if (OffsetEnd < Size) { OffsetEnd = -1; return STATUS_INTEGER_OVERFLOW; } AlignedOffsetEnd = ~(BlockDevice->BlockSize - 1) & (OffsetEnd + BlockDevice->BlockSize - 1); if (AlignedOffsetEnd < OffsetEnd) { return STATUS_INTEGER_OVERFLOW; } if ((BlockDevice->Offset) || (Size != AlignedOffsetEnd)) { Status = BlockIopAllocateAlignedBuffer(&BlockIopReadBlockBuffer, &BlockIopReadBlockBufferSize, AlignedOffsetEnd, BlockDevice->Alignment); if (!NT_SUCCESS(Status)) { EfiPrintf(L"Failed to allocate buffer: %lx\r\n", Status); return Status; } ReadBuffer = BlockIopReadBlockBuffer; } Offset = AlignedOffsetEnd / BlockDevice->BlockSize; if (BlockDevice->Unknown & 2) { Status = BlockIopReadUsingPrefetch(DeviceEntry, ReadBuffer, AlignedOffsetEnd / BlockDevice->BlockSize); if (NT_SUCCESS(Status)) { goto ReadComplete; } } Status = BlockIopOperation(DeviceEntry, ReadBuffer, Offset, 0); if (!NT_SUCCESS(Status)) { EfiPrintf(L"Block I/O failed: %lx\r\n", Status); return Status; } BlockDevice->Block += Offset; ReadComplete: if (ReadBuffer != Buffer) { RtlCopyMemory(Buffer, (PVOID)((ULONG_PTR)ReadBuffer + (ULONG_PTR)BlockDevice->Offset), Size); } if (BytesRead) { *BytesRead = Size; } return STATUS_SUCCESS; } NTSTATUS BlockIopBlockInformationCheck ( _In_ PBL_BLOCK_DEVICE BlockDevice, _In_opt_ PULONG DesiredSize, _Out_opt_ PULONG Size, _Out_opt_ PULONG OutputAdjustedSize ) { ULONG RealSize; ULONGLONG Offset, LastOffset, RemainingOffset, MaxOffset; NTSTATUS Status; RealSize = 0; Offset = (BlockDevice->Offset * BlockDevice->BlockSize) + BlockDevice->Block; if (Offset > ((BlockDevice->LastBlock + 1) * BlockDevice->BlockSize)) { Status = STATUS_INVALID_PARAMETER; goto Quickie; } LastOffset = (BlockDevice->LastBlock * BlockDevice->BlockSize) + BlockDevice->BlockSize - 1; MaxOffset = BlockDevice->LastBlock; if (MaxOffset < BlockDevice->BlockSize) { MaxOffset = BlockDevice->BlockSize; } if (LastOffset < MaxOffset) { Status = STATUS_INVALID_PARAMETER; goto Quickie; } if (Offset > LastOffset) { Status = STATUS_INVALID_PARAMETER; goto Quickie; } RemainingOffset = LastOffset - Offset + 1; if (DesiredSize != FALSE) { RealSize = *DesiredSize; } else { RealSize = ULONG_MAX; } if (RemainingOffset < RealSize) { if (Size == FALSE) { RealSize = 0; Status = STATUS_INVALID_PARAMETER; goto Quickie; } RealSize = RemainingOffset; } Status = STATUS_SUCCESS; Quickie: if (Size) { *Size = RealSize; } return Status; } NTSTATUS BlockIoRead ( _In_ PBL_DEVICE_ENTRY DeviceEntry, _In_ PVOID Buffer, _In_ ULONG Size, _Out_ PULONG BytesRead ) { PBL_BLOCK_DEVICE BlockDevice; NTSTATUS Status; /* Get the device-specific data, which is our block device descriptor */ BlockDevice = DeviceEntry->DeviceSpecificData; /* Make sure that the buffer and size is valid */ Status = BlockIopBlockInformationCheck(BlockDevice, &Size, BytesRead, &Size); if (NT_SUCCESS(Status)) { /* Check if this is a virtual device or a physical device */ if (BlockDevice->DeviceFlags & BL_BLOCK_DEVICE_VIRTUAL_FLAG) { /* Do a virtual read or write */ Status = BlockIopReadWriteVirtualDevice(DeviceEntry, Buffer, Size, 0, BytesRead); } else { /* Do a physical read or write */ Status = BlockIopReadPhysicalDevice(DeviceEntry, Buffer, Size, BytesRead); } } else if (BytesRead) { /* We failed, if the caller wanted bytes read, return 0 */ *BytesRead = 0; } /* Return back to the caller */ return Status; } NTSTATUS BlockIoSetInformation ( _In_ PBL_DEVICE_ENTRY DeviceEntry, _Out_ PBL_DEVICE_INFORMATION DeviceInformation ) { PBL_BLOCK_DEVICE BlockDevice; ULONGLONG Offset; BlockDevice = DeviceEntry->DeviceSpecificData; /* Take the current block number and block-offset and conver to full offset */ Offset = DeviceInformation->BlockDeviceInfo.Block * BlockDevice->BlockSize + DeviceInformation->BlockDeviceInfo.Offset; /* Make sure that the full offset is still within the bounds of the device */ if (Offset > ((BlockDevice->LastBlock + 1) * BlockDevice->BlockSize - 1)) { EfiPrintf(L"Offset out of bounds\r\n"); return STATUS_INVALID_PARAMETER; } /* Convery the full raw offset into a block number and block-offset */ BlockDevice->Block = Offset / BlockDevice->BlockSize; BlockDevice->Offset = Offset % BlockDevice->BlockSize; /* Return the unknown */ BlockDevice->Unknown = DeviceInformation->BlockDeviceInfo.Unknown; /* All done */ return STATUS_SUCCESS; } NTSTATUS BlockIoGetInformation ( _In_ PBL_DEVICE_ENTRY DeviceEntry, _Out_ PBL_DEVICE_INFORMATION DeviceInformation ) { /* Copy the device specific data into the block device information */ RtlCopyMemory(&DeviceInformation->BlockDeviceInfo, DeviceEntry->DeviceSpecificData, sizeof(DeviceInformation->BlockDeviceInfo)); /* Hardcode the device type */ DeviceInformation->DeviceType = DiskDevice; return STATUS_SUCCESS; } BOOLEAN BlDeviceIsVirtualPartitionDevice ( _In_ PBL_DEVICE_DESCRIPTOR InputDevice, _Outptr_ PBL_DEVICE_DESCRIPTOR* VirtualDevice ) { BOOLEAN IsVirtual; PBL_LOCAL_DEVICE ParentDisk; /* Assume it isn't */ IsVirtual = FALSE; /* Check if this is a partition device */ if ((InputDevice->DeviceType == LegacyPartitionDevice) || (InputDevice->DeviceType == PartitionDevice)) { /* Check if the parent disk is a VHD */ ParentDisk = &InputDevice->Partition.Disk; if (ParentDisk->Type == VirtualDiskDevice) { /* This is a virtual partition device -- does the caller want it? */ IsVirtual = TRUE; if (VirtualDevice) { *VirtualDevice = (PBL_DEVICE_DESCRIPTOR)(&ParentDisk->VirtualHardDisk + 1); } } } /* Return */ return IsVirtual; } NTSTATUS BlDeviceSetInformation ( _In_ ULONG DeviceId, _Out_ PBL_DEVICE_INFORMATION DeviceInformation ) { PBL_DEVICE_ENTRY DeviceEntry; /* This parameter is not optional */ if (!DeviceInformation) { return STATUS_INVALID_PARAMETER; } /* Make sure the device ID is valid */ if (DmTableEntries <= DeviceId) { return STATUS_INVALID_PARAMETER; } /* Get the device entry */ DeviceEntry = DmDeviceTable[DeviceId]; if (!DeviceEntry) { return STATUS_INVALID_PARAMETER; } /* Make sure the device is open */ if (!(DeviceEntry->Flags & BL_DEVICE_ENTRY_OPENED)) { return STATUS_INVALID_PARAMETER; } /* Set the device information */ return DeviceEntry->Callbacks.SetInformation(DeviceEntry, DeviceInformation); } NTSTATUS BlDeviceGetInformation ( _In_ ULONG DeviceId, _Out_ PBL_DEVICE_INFORMATION DeviceInformation ) { PBL_DEVICE_ENTRY DeviceEntry; /* This parameter is not optional */ if (!DeviceInformation) { return STATUS_INVALID_PARAMETER; } /* Make sure the device ID is valid */ if (DmTableEntries <= DeviceId) { return STATUS_INVALID_PARAMETER; } /* Get the device entry */ DeviceEntry = DmDeviceTable[DeviceId]; if (!DeviceEntry) { return STATUS_INVALID_PARAMETER; } /* Make sure the device is open */ if (!(DeviceEntry->Flags & BL_DEVICE_ENTRY_OPENED)) { return STATUS_INVALID_PARAMETER; } /* Return the device information */ DeviceInformation->DeviceType = DeviceEntry->DeviceDescriptor->DeviceType; return DeviceEntry->Callbacks.GetInformation(DeviceEntry, DeviceInformation); } NTSTATUS BlDeviceRead ( _In_ ULONG DeviceId, _In_ PVOID Buffer, _In_ ULONG Size, _Out_opt_ PULONG BytesRead ) { PBL_DEVICE_ENTRY DeviceEntry; NTSTATUS Status; ULONG BytesTransferred; /* Make sure we have a buffer, and the device ID is valid */ if (!(Buffer) || (DmTableEntries <= DeviceId)) { return STATUS_INVALID_PARAMETER; } /* Get the device entry for it */ DeviceEntry = DmDeviceTable[DeviceId]; if (!DeviceEntry) { return STATUS_INVALID_PARAMETER; } /* Make sure this is a device opened for read access */ if (!(DeviceEntry->Flags & BL_DEVICE_ENTRY_OPENED) || !(DeviceEntry->Flags & BL_DEVICE_ENTRY_READ_ACCESS)) { return STATUS_INVALID_PARAMETER; } /* Issue the read */ Status = DeviceEntry->Callbacks.Read(DeviceEntry, Buffer, Size, &BytesTransferred); if (!DeviceEntry->Unknown) { /* Update performance counters */ DmDeviceIoInformation.ReadCount += BytesTransferred; } /* Return back how many bytes were read, if caller wants to know */ if (BytesRead) { *BytesRead = BytesTransferred; } /* Return read result */ return Status; } NTSTATUS BlDeviceReadAtOffset ( _In_ ULONG DeviceId, _In_ ULONG Size, _In_ ULONGLONG Offset, _In_ PVOID Buffer, _Out_ PULONG BytesRead ) { NTSTATUS Status; BL_DEVICE_INFORMATION DeviceInfo; /* Get the current block and offset */ Status = BlDeviceGetInformation(DeviceId, &DeviceInfo); if (!NT_SUCCESS(Status)) { return Status; } /* Get the block and block-offset based on the new raw offset */ DeviceInfo.BlockDeviceInfo.Block = Offset / DeviceInfo.BlockDeviceInfo.BlockSize; DeviceInfo.BlockDeviceInfo.Offset = Offset % DeviceInfo.BlockDeviceInfo.BlockSize; /* Update the block and offset */ Status = BlDeviceSetInformation(DeviceId, &DeviceInfo); if (NT_SUCCESS(Status)) { /* Now issue a read, with this block and offset configured */ Status = BlDeviceRead(DeviceId, Buffer, Size, BytesRead); } /* All good, return the caller */ return Status; } BOOLEAN BlpDeviceCompare ( _In_ PBL_DEVICE_DESCRIPTOR Device1, _In_ PBL_DEVICE_DESCRIPTOR Device2 ) { BOOLEAN DeviceMatch; ULONG DeviceSize; /* Assume failure */ DeviceMatch = FALSE; /* Check if the two devices exist and are identical in type */ if ((Device1) && (Device2) && (Device1->DeviceType == Device2->DeviceType)) { /* Take the bigger of the two sizes */ DeviceSize = max(Device1->Size, Device2->Size); if (DeviceSize >= (ULONG)FIELD_OFFSET(BL_DEVICE_DESCRIPTOR, Local)) { /* Compare the two devices up to their size */ if (RtlEqualMemory(&Device1->Local, &Device2->Local, DeviceSize - FIELD_OFFSET(BL_DEVICE_DESCRIPTOR, Local))) { /* They match! */ DeviceMatch = TRUE; } } } /* Return matching state */ return DeviceMatch; } NTSTATUS BlockIopFreeAllocations ( _In_ PBL_BLOCK_DEVICE BlockDevice ) { /* If a block device was passed in, free it */ if (BlockDevice) { BlMmFreeHeap(BlockDevice); } /* Nothing else to do */ return STATUS_SUCCESS; } NTSTATUS BlockIoEfiGetBlockIoInformation ( _In_ PBL_BLOCK_DEVICE BlockDevice ) { NTSTATUS Status; EFI_BLOCK_IO_MEDIA *Media; /* Open the Block I/O protocol on this device */ Status = EfiOpenProtocol(BlockDevice->Handle, &EfiBlockIoProtocol, (PVOID*)&BlockDevice->Protocol); if (!NT_SUCCESS(Status)) { return Status; } /* Get information on the block media */ Media = BlockDevice->Protocol->Media; /* Set the appropriate device flags */ BlockDevice->DeviceFlags = 0; if (Media->RemovableMedia) { BlockDevice->DeviceFlags = BL_BLOCK_DEVICE_REMOVABLE_FLAG; } if (Media->MediaPresent) { BlockDevice->DeviceFlags |= BL_BLOCK_DEVICE_PRESENT_FLAG; } /* No clue */ BlockDevice->Unknown = 0; /* Set the block size */ BlockDevice->BlockSize = Media->BlockSize; /* Make sure there's a last block value */ if (!Media->LastBlock) { return STATUS_INVALID_PARAMETER; } /* Don't let it be too high */ if (Media->LastBlock > 0xFFFFFFFFFFE) { BlockDevice->LastBlock = 0xFFFFFFFFFFE; } else { BlockDevice->LastBlock = Media->LastBlock; } /* Make the alignment the smaller of the I/O alignment or the block size */ if (Media->IoAlign >= Media->BlockSize) { BlockDevice->Alignment = Media->IoAlign; } else { BlockDevice->Alignment = Media->BlockSize; } /* All good */ return STATUS_SUCCESS; } NTSTATUS BlockIoEfiGetChildHandle ( _In_ PBL_PROTOCOL_HANDLE ProtocolInterface, _In_ PBL_PROTOCOL_HANDLE ChildProtocolInterface) { NTSTATUS Status; ULONG i, DeviceCount; EFI_DEVICE_PATH *DevicePath, *ParentDevicePath; EFI_HANDLE *DeviceHandles; EFI_HANDLE Handle; /* Find all the Block I/O device handles on the system */ DeviceCount = 0; DeviceHandles = 0; Status = EfiLocateHandleBuffer(ByProtocol, &EfiBlockIoProtocol, &DeviceCount, &DeviceHandles); if (!NT_SUCCESS(Status)) { /* Failed to enumerate, bail out */ return Status; } /* Loop all the handles */ for (i = 0; i < DeviceCount; i++) { /* Check if this is the device itself */ Handle = DeviceHandles[i]; if (Handle == ProtocolInterface->Handle) { /* Skip it */ continue; } /* Get the device path of this device */ Status = EfiOpenProtocol(Handle, &EfiDevicePathProtocol, (PVOID*)&DevicePath); if (!NT_SUCCESS(Status)) { /* We failed, skip it */ continue; } /* See if we are its parent */ ParentDevicePath = EfiIsDevicePathParent(ProtocolInterface->Interface, DevicePath); if (ParentDevicePath == ProtocolInterface->Interface) { /* Yup, return back to caller */ ChildProtocolInterface->Handle = Handle; ChildProtocolInterface->Interface = DevicePath; Status = STATUS_SUCCESS; goto Quickie; } /* Close the device path */ EfiCloseProtocol(Handle, &EfiDevicePathProtocol); } /* If we got here, nothing was found */ Status = STATUS_NO_SUCH_DEVICE; Quickie: /* Free the handle array buffer */ BlMmFreeHeap(DeviceHandles); return Status; } NTSTATUS BlockIoGetGPTDiskSignature ( _In_ PBL_DEVICE_ENTRY DeviceEntry, _Out_ PGUID DiskSignature ) { EfiPrintf(L"GPT not supported\r\n"); return STATUS_NOT_IMPLEMENTED; } NTSTATUS BlockIoEfiGetDeviceInformation ( _In_ PBL_DEVICE_ENTRY DeviceEntry ) { NTSTATUS Status; PBL_DEVICE_DESCRIPTOR Device; PBL_BLOCK_DEVICE BlockDevice; EFI_DEVICE_PATH *LeafNode; BL_PROTOCOL_HANDLE Protocol[2]; ACPI_HID_DEVICE_PATH *AcpiPath; HARDDRIVE_DEVICE_PATH *DiskPath; BOOLEAN Found; ULONG i; /* Extract the identifier, and the block device object */ Device = DeviceEntry->DeviceDescriptor; BlockDevice = (PBL_BLOCK_DEVICE)DeviceEntry->DeviceSpecificData; /* Initialize protocol handles */ Protocol[0].Handle = BlockDevice->Handle; Protocol[1].Handle = 0; /* Open this device */ Status = EfiOpenProtocol(Protocol[0].Handle, &EfiDevicePathProtocol, &Protocol[0].Interface); if (!NT_SUCCESS(Status)) { /* Fail */ return Status; } /* Iterate twice -- once for the top level, once for the bottom */ for (i = 0, Found = FALSE; Found == FALSE && Protocol[i].Handle; i++) { /* Check what kind of leaf node device this is */ LeafNode = EfiGetLeafNode(Protocol[i].Interface); EfiPrintf(L"Pass %d, Leaf node: %p Type: %d\r\n", i, LeafNode, LeafNode->Type); if (LeafNode->Type == ACPI_DEVICE_PATH) { /* We only support floppy drives */ AcpiPath = (ACPI_HID_DEVICE_PATH*)LeafNode; if ((AcpiPath->HID == EISA_PNP_ID(0x604)) || (AcpiPath->HID == EISA_PNP_ID(0x700))) { /* Set the boot library specific device types */ Device->DeviceType = LocalDevice; Device->Local.Type = FloppyDevice; /* The ACPI UID is the drive number */ Device->Local.FloppyDisk.DriveNumber = AcpiPath->UID; /* We found a match */ Found = TRUE; } } else if ((LeafNode->Type == MEDIA_DEVICE_PATH) && (i == 1)) { /* Extract the disk path and check if it's a physical disk */ DiskPath = (HARDDRIVE_DEVICE_PATH*)LeafNode; EfiPrintf(L"Disk path: %p Type: %lx\r\n", DiskPath, LeafNode->SubType); if (LeafNode->SubType == MEDIA_HARDDRIVE_DP) { /* Set this as a local device */ Device->Local.Type = LocalDevice; /* Check if this is an MBR partition */ if (DiskPath->SignatureType == SIGNATURE_TYPE_MBR) { /* Set that this is a local partition */ Device->DeviceType = LegacyPartitionDevice; Device->Partition.Disk.Type = LocalDevice; /* Write the MBR partition signature */ BlockDevice->PartitionType = MbrPartition; BlockDevice->Disk.Mbr.Signature = *(PULONG)&DiskPath->Signature[0]; Found = TRUE; } else if (DiskPath->SignatureType == SIGNATURE_TYPE_GUID) { /* Set this as a GPT partition */ BlockDevice->PartitionType = GptPartition; Device->Local.HardDisk.PartitionType = GptPartition; /* Get the GPT signature */ Status = BlockIoGetGPTDiskSignature(DeviceEntry, &Device->Local.HardDisk.Gpt.PartitionSignature); if (NT_SUCCESS(Status)) { /* Copy it */ RtlCopyMemory(&BlockDevice->Disk.Gpt.Signature, &Device->Local.HardDisk.Gpt.PartitionSignature, sizeof(BlockDevice->Disk.Gpt.Signature)); Found = TRUE; } } /* Otherwise, this is a raw disk */ BlockDevice->PartitionType = RawPartition; Device->Local.HardDisk.PartitionType = RawPartition; Device->Local.HardDisk.Raw.DiskNumber = BlockIoFirmwareRawDiskCount++; } else if (LeafNode->SubType == MEDIA_CDROM_DP) { /* Set block device information */ EfiPrintf(L"Found CD-ROM\r\n"); BlockDevice->PartitionType = RawPartition; BlockDevice->Type = CdRomDevice; /* Set CDROM data */ Device->Local.Type = CdRomDevice; Device->Local.FloppyDisk.DriveNumber = 0; Found = TRUE; } } else if ((LeafNode->Type != MEDIA_DEVICE_PATH) && (LeafNode->Type != ACPI_DEVICE_PATH) && (i == 0)) { /* This is probably a messaging device node. Are we under it? */ Status = BlockIoEfiGetChildHandle(Protocol, &Protocol[1]); EfiPrintf(L"Pass 0, non DP/ACPI path. Child handle obtained: %lx\r\n", Protocol[1].Handle); if (!NT_SUCCESS(Status)) { /* We're not. So this must be a raw device */ Device->DeviceType = LocalDevice; Found = TRUE; /* Is it a removable raw device? */ if (BlockDevice->DeviceFlags & BL_BLOCK_DEVICE_REMOVABLE_FLAG) { /* This is a removable (CD or Floppy or USB) device */ BlockDevice->Type = FloppyDevice; Device->Local.Type = FloppyDevice; Device->Local.FloppyDisk.DriveNumber = BlockIoFirmwareRemovableDiskCount++; EfiPrintf(L"Found Floppy\r\n"); } else { /* It's a fixed device */ BlockDevice->Type = DiskDevice; Device->Local.Type = DiskDevice; /* Set it as a raw partition */ Device->Local.HardDisk.PartitionType = RawPartition; Device->Local.HardDisk.Mbr.PartitionSignature = BlockIoFirmwareRawDiskCount++; EfiPrintf(L"Found raw disk\r\n"); } } } } /* Close any protocols that we opened for each handle */ while (i) { EfiCloseProtocol(Protocol[--i].Handle, &EfiDevicePathProtocol); } /* Return appropriate status */ return Found ? STATUS_SUCCESS : STATUS_NOT_SUPPORTED; } NTSTATUS BlockIoEfiReset ( VOID ) { EfiPrintf(L"not implemented\r\n"); return STATUS_NOT_IMPLEMENTED; } NTSTATUS BlockIoEfiFlush ( VOID ) { EfiPrintf(L"not implemented\r\n"); return STATUS_NOT_IMPLEMENTED; } NTSTATUS BlockIoEfiCreateDeviceEntry ( _In_ PBL_DEVICE_ENTRY *DeviceEntry, _Out_ PVOID Handle ) { PBL_DEVICE_ENTRY IoDeviceEntry; PBL_BLOCK_DEVICE BlockDevice; NTSTATUS Status; PBL_DEVICE_DESCRIPTOR Device; /* Allocate the entry for this device and zero it out */ IoDeviceEntry = BlMmAllocateHeap(sizeof(*IoDeviceEntry)); if (!IoDeviceEntry) { return STATUS_NO_MEMORY; } RtlZeroMemory(IoDeviceEntry, sizeof(*IoDeviceEntry)); /* Allocate the device descriptor for this device and zero it out */ Device = BlMmAllocateHeap(sizeof(*Device)); if (!Device) { return STATUS_NO_MEMORY; } RtlZeroMemory(Device, sizeof(*Device)); /* Allocate the block device specific data, and zero it out */ BlockDevice = BlMmAllocateHeap(sizeof(*BlockDevice)); if (!BlockDevice) { return STATUS_NO_MEMORY; } RtlZeroMemory(BlockDevice, sizeof(*BlockDevice)); /* Save the descriptor and block device specific data */ IoDeviceEntry->DeviceSpecificData = BlockDevice; IoDeviceEntry->DeviceDescriptor = Device; /* Set the size of the descriptor */ Device->Size = sizeof(*Device); /* Copy the standard I/O callbacks */ RtlCopyMemory(&IoDeviceEntry->Callbacks, &BlockIoDeviceFunctionTable, sizeof(IoDeviceEntry->Callbacks)); /* Add the two that are firmware specific */ IoDeviceEntry->Callbacks.Reset = BlockIoEfiReset; IoDeviceEntry->Callbacks.Flush = BlockIoEfiFlush; /* Save the EFI handle */ BlockDevice->Handle = Handle; /* Get information on this device from EFI, caching it in the device */ Status = BlockIoEfiGetBlockIoInformation(BlockDevice); if (NT_SUCCESS(Status)) { /* Build the descriptor structure for this device */ Status = BlockIoEfiGetDeviceInformation(IoDeviceEntry); if (NT_SUCCESS(Status)) { /* We have a fully constructed device, return it */ *DeviceEntry = IoDeviceEntry; return STATUS_SUCCESS; } } /* Failure path, free the descriptor if we allocated one */ if (IoDeviceEntry->DeviceDescriptor) { BlMmFreeHeap(IoDeviceEntry->DeviceDescriptor); } /* Free any other specific allocations */ BlockIopFreeAllocations(IoDeviceEntry->DeviceSpecificData); /* Free the device entry itself and return the failure code */ BlMmFreeHeap(IoDeviceEntry); EfiPrintf(L"Failed: %lx\r\n", Status); return Status; } NTSTATUS BlockIoEfiCompareDevice ( _In_ PBL_DEVICE_DESCRIPTOR Device, _In_ EFI_HANDLE Handle ) { PBL_LOCAL_DEVICE LocalDeviceInfo, EfiLocalDeviceInfo; PBL_DEVICE_ENTRY DeviceEntry; PBL_DEVICE_DESCRIPTOR EfiDevice; NTSTATUS Status; DeviceEntry = NULL; /* Check if no device was given */ if (!Device) { /* Fail the comparison */ Status = STATUS_INVALID_PARAMETER; goto Quickie; } /* Check if this is a local disk device */ if (Device->DeviceType != DiskDevice) { /* Nope -- is it a partition device? */ if ((Device->DeviceType != LegacyPartitionDevice) && (Device->DeviceType != PartitionDevice)) { /* Nope, so we can't compare */ Status = STATUS_INVALID_PARAMETER; goto Quickie; } /* If so, return the device information for the parent disk */ LocalDeviceInfo = &Device->Partition.Disk; } else { /* Just return the disk information itself */ LocalDeviceInfo = &Device->Local; } /* Create an EFI device entry for the EFI device handle */ Status = BlockIoEfiCreateDeviceEntry(&DeviceEntry, Handle); if (!NT_SUCCESS(Status)) { goto Quickie; } /* Read the descriptor and assume failure for now */ EfiDevice = DeviceEntry->DeviceDescriptor; Status = STATUS_UNSUCCESSFUL; /* Check if the EFI device is a disk */ if (EfiDevice->DeviceType != DiskDevice) { /* Nope, is it a partition? */ if ((EfiDevice->DeviceType != LegacyPartitionDevice) && (EfiDevice->DeviceType != PartitionDevice)) { /* Neither, invalid handle so bail out */ Status = STATUS_INVALID_PARAMETER; goto Quickie; } /* Yes, so get the information of the parent disk */ EfiLocalDeviceInfo = &EfiDevice->Partition.Disk; } else { /* It's a disk, so get the disk information itself */ EfiLocalDeviceInfo = &EfiDevice->Local; } /* Are the two devices the same type? */ if (EfiLocalDeviceInfo->Type != LocalDeviceInfo->Type) { /* Nope, that was easy */ goto Quickie; } /* Yes, what kind of device is the EFI side? */ switch (EfiLocalDeviceInfo->Type) { case LocalDevice: /* Local hard drive, compare the signature */ if (RtlCompareMemory(&EfiLocalDeviceInfo->HardDisk, &LocalDeviceInfo->HardDisk, sizeof(LocalDeviceInfo->HardDisk)) == sizeof(LocalDeviceInfo->HardDisk)) { Status = STATUS_SUCCESS; } break; case FloppyDevice: case CdRomDevice: /* Removable floppy or CD, compare the disk number */ if (RtlCompareMemory(&EfiLocalDeviceInfo->FloppyDisk, &LocalDeviceInfo->FloppyDisk, sizeof(LocalDeviceInfo->FloppyDisk)) == sizeof(LocalDeviceInfo->FloppyDisk)) { Status = STATUS_SUCCESS; } break; case RamDiskDevice: /* RAM disk, compare the size and base information */ if (RtlCompareMemory(&EfiLocalDeviceInfo->RamDisk, &LocalDeviceInfo->RamDisk, sizeof(LocalDeviceInfo->RamDisk)) == sizeof(LocalDeviceInfo->RamDisk)) { Status = STATUS_SUCCESS; } break; case FileDevice: /* File, compare the file identifier */ if (RtlCompareMemory(&EfiLocalDeviceInfo->File, &LocalDeviceInfo->File, sizeof(LocalDeviceInfo->File)) == sizeof(LocalDeviceInfo->File)) { Status = STATUS_SUCCESS; } break; /* Something else we don't support */ default: break; } Quickie: /* All done, did we have an EFI device entry? */ if (DeviceEntry) { /* Free it, since we only needed it locally for comparison */ BlMmFreeHeap(DeviceEntry->DeviceDescriptor); BlockIopFreeAllocations(DeviceEntry->DeviceSpecificData); BlMmFreeHeap(DeviceEntry); } /* Return back to the caller */ return Status; } NTSTATUS BlockIoFirmwareOpen ( _In_ PBL_DEVICE_DESCRIPTOR Device, _In_ PBL_BLOCK_DEVICE BlockIoDevice ) { NTSTATUS Status; BOOLEAN DeviceMatch; BL_HASH_ENTRY HashEntry; ULONG i, Id, DeviceCount; PBL_DEVICE_ENTRY DeviceEntry; EFI_HANDLE* DeviceHandles; /* Initialize everything */ DeviceEntry = NULL; DeviceCount = 0; DeviceHandles = 0; DeviceEntry = NULL; /* Ask EFI for handles to all block devices */ Status = EfiLocateHandleBuffer(ByProtocol, &EfiBlockIoProtocol, &DeviceCount, &DeviceHandles); if (!NT_SUCCESS(Status)) { return STATUS_NO_SUCH_DEVICE; } /* Build a hash entry, with the value inline */ HashEntry.Flags = BL_HT_VALUE_IS_INLINE; HashEntry.Size = sizeof(EFI_HANDLE); /* Loop each device we got */ DeviceMatch = FALSE; Status = STATUS_NO_SUCH_DEVICE; for (i = 0; i < DeviceCount; i++) { /* Check if we have a match in the device hash table */ HashEntry.Value = DeviceHandles[i]; Status = BlHtLookup(HashTableId, &HashEntry, 0); if (NT_SUCCESS(Status)) { /* We already know about this device */ EfiPrintf(L"Device is known\r\n"); continue; } /* New device, store it in the hash table */ Status = BlHtStore(HashTableId, &HashEntry, DeviceHandles[i], sizeof(DeviceHandles[i])); if (!NT_SUCCESS(Status)) { /* Free the array and fail */ BlMmFreeHeap(DeviceHandles); break; } /* Create an entry for this device*/ Status = BlockIoEfiCreateDeviceEntry(&DeviceEntry, DeviceHandles[i]); if (!NT_SUCCESS(Status)) { EfiPrintf(L"EFI create failed: %lx\r\n", Status); continue; } /* Add the device entry to the device table */ Status = BlTblSetEntry(&BlockIoDeviceTable, &BlockIoDeviceTableEntries, DeviceEntry, &Id, TblDoNotPurgeEntry); if (!NT_SUCCESS(Status)) { /* Remove it from teh hash table */ BlHtDelete(HashTableId, &HashEntry); /* Free the block I/O device data */ BlockIopFreeAllocations(DeviceEntry->DeviceSpecificData); /* Free the descriptor */ BlMmFreeHeap(DeviceEntry->DeviceDescriptor); /* Free the entry */ BlMmFreeHeap(DeviceEntry); break; } /* Does this device match what we're looking for? */ DeviceMatch = BlpDeviceCompare(DeviceEntry->DeviceDescriptor, Device); if (DeviceMatch) { /* Yep, return the data back */ RtlCopyMemory(BlockIoDevice, DeviceEntry->DeviceSpecificData, sizeof(*BlockIoDevice)); Status = STATUS_SUCCESS; break; } } /* Free the device handle buffer array */ BlMmFreeHeap(DeviceHandles); /* Return status */ return Status; } NTSTATUS PartitionOpen ( _In_ PBL_DEVICE_DESCRIPTOR Device, _In_ PBL_DEVICE_ENTRY DeviceEntry ) { EfiPrintf(L"Not implemented!\r\n"); return STATUS_NOT_IMPLEMENTED; } NTSTATUS VhdFileDeviceOpen ( _In_ PBL_DEVICE_DESCRIPTOR Device, _In_ PBL_DEVICE_ENTRY DeviceEntry ) { EfiPrintf(L"Not implemented!\r\n"); return STATUS_NOT_IMPLEMENTED; } NTSTATUS DiskClose ( _In_ PBL_DEVICE_ENTRY DeviceEntry ) { NTSTATUS Status, LocalStatus; PBL_BLOCK_DEVICE BlockDevice; /* Assume success */ Status = STATUS_SUCCESS; BlockDevice = DeviceEntry->DeviceSpecificData; /* Close the protocol */ LocalStatus = EfiCloseProtocol(BlockDevice->Handle, &EfiBlockIoProtocol); if (!NT_SUCCESS(LocalStatus)) { /* Only inherit failures */ Status = LocalStatus; } /* Free the block device allocations */ LocalStatus = BlockIopFreeAllocations(BlockDevice); if (!NT_SUCCESS(LocalStatus)) { /* Only inherit failures */ Status = LocalStatus; } /* Return back to caller */ return Status; } NTSTATUS DiskOpen ( _In_ PBL_DEVICE_DESCRIPTOR Device, _In_ PBL_DEVICE_ENTRY DeviceEntry ) { NTSTATUS Status; /* Use firmware-specific functions to open the disk */ Status = BlockIoFirmwareOpen(Device, DeviceEntry->DeviceSpecificData); if (NT_SUCCESS(Status)) { /* Overwrite with our own close routine */ DeviceEntry->Callbacks.Close = DiskClose; } /* Return back to caller */ return Status; } NTSTATUS RdDeviceOpen ( _In_ PBL_DEVICE_DESCRIPTOR Device, _In_ PBL_DEVICE_ENTRY DeviceEntry ) { EfiPrintf(L"Not implemented!\r\n"); return STATUS_NOT_IMPLEMENTED; } NTSTATUS FileDeviceOpen ( _In_ PBL_DEVICE_DESCRIPTOR Device, _In_ PBL_DEVICE_ENTRY DeviceEntry ) { EfiPrintf(L"Not implemented!\r\n"); return STATUS_NOT_IMPLEMENTED; } NTSTATUS SpOpen ( _In_ PBL_DEVICE_DESCRIPTOR Device, _In_ PBL_DEVICE_ENTRY DeviceEntry ) { EfiPrintf(L"Not implemented!\r\n"); return STATUS_NOT_IMPLEMENTED; } NTSTATUS UdpOpen ( _In_ PBL_DEVICE_DESCRIPTOR Device, _In_ PBL_DEVICE_ENTRY DeviceEntry ) { EfiPrintf(L"Not implemented!\r\n"); return STATUS_NOT_IMPLEMENTED; } BL_DEVICE_CALLBACKS FileDeviceFunctionTable = { NULL, FileDeviceOpen, NULL, }; BL_DEVICE_CALLBACKS PartitionDeviceFunctionTable = { NULL, PartitionOpen, NULL, }; BL_DEVICE_CALLBACKS RamDiskDeviceFunctionTable = { NULL, RdDeviceOpen, NULL, }; BL_DEVICE_CALLBACKS DiskDeviceFunctionTable = { NULL, DiskOpen, NULL, }; BL_DEVICE_CALLBACKS VirtualDiskDeviceFunctionTable = { NULL, VhdFileDeviceOpen, NULL, }; BL_DEVICE_CALLBACKS UdpFunctionTable = { NULL, UdpOpen, NULL, }; BL_DEVICE_CALLBACKS SerialPortFunctionTable = { NULL, SpOpen, NULL, }; BOOLEAN DeviceTableCompare ( _In_ PVOID Entry, _In_ PVOID Argument1, _In_ PVOID Argument2, _Inout_ PVOID Argument3, _Inout_ PVOID Argument4 ) { BOOLEAN Found; PBL_DEVICE_DESCRIPTOR Device = (PBL_DEVICE_DESCRIPTOR)Argument1; PBL_DEVICE_ENTRY DeviceEntry = (PBL_DEVICE_ENTRY)Entry; ULONG Flags = *(PULONG)Argument2; ULONG Unknown = *(PULONG)Argument3; /* Assume failure */ Found = FALSE; /* Compare the device descriptor */ if (BlpDeviceCompare(DeviceEntry->DeviceDescriptor, Device)) { /* Compare something */ if (DeviceEntry->Unknown == Unknown) { /* Compare flags */ if ((!(Flags & BL_DEVICE_READ_ACCESS) || (DeviceEntry->Flags & BL_DEVICE_ENTRY_READ_ACCESS)) && (!(Flags & BL_DEVICE_WRITE_ACCESS) || (DeviceEntry->Flags & BL_DEVICE_ENTRY_WRITE_ACCESS))) { /* And more flags */ if (((Flags & 8) || !(DeviceEntry->Flags & 8)) && (!(Flags & 8) || (DeviceEntry->Flags & 8))) { /* Found a match! */ Found = TRUE; } } } } /* Return matching state */ return Found; } NTSTATUS DeviceTableDestroyEntry ( _In_ PVOID Entry, _In_ ULONG DeviceId ) { PBL_DEVICE_ENTRY DeviceEntry = (PBL_DEVICE_ENTRY)Entry; NTSTATUS Status; /* Call the close routine for this entry */ Status = DeviceEntry->Callbacks.Close(DmDeviceTable[DeviceId]); /* Free the descriptor, and the device itself */ BlMmFreeHeap(DeviceEntry->DeviceDescriptor); BlMmFreeHeap(DeviceEntry); /* Clear out the netry, and return */ DmDeviceTable[DeviceId] = NULL; return Status; } NTSTATUS DeviceTablePurge ( _In_ PVOID Entry ) { PBL_DEVICE_ENTRY DeviceEntry = (PBL_DEVICE_ENTRY)Entry; NTSTATUS Status; /* Check if the device is opened */ if (DeviceEntry->Flags & BL_DEVICE_ENTRY_OPENED) { /* It is, so can't purge it */ Status = STATUS_UNSUCCESSFUL; } else { /* It isn't, so destroy the entry */ Status = DeviceTableDestroyEntry(DeviceEntry, DeviceEntry->DeviceId); } /* Return back to caller */ return Status; } NTSTATUS BlockIoDeviceTableDestroyEntry ( _In_ PVOID Entry, _In_ ULONG DeviceId ) { PBL_DEVICE_ENTRY DeviceEntry = (PBL_DEVICE_ENTRY)Entry; NTSTATUS Status; /* Call the close routine for this entry */ Status = DeviceEntry->Callbacks.Close(DeviceEntry); /* Free the descriptor, and the device itself */ BlMmFreeHeap(DeviceEntry->DeviceDescriptor); BlMmFreeHeap(DeviceEntry); /* Clear out the netry, and return */ BlockIoDeviceTable[DeviceId] = NULL; return Status; } NTSTATUS BlockIoDeviceTableDestroy ( VOID ) { NTSTATUS Status; /* Call the entry destructor on each entry in the table */ Status = BlTblMap(BlockIoDeviceTable, BlockIoDeviceTableEntries, BlockIoDeviceTableDestroyEntry); /* Free the table and return */ BlMmFreeHeap(BlockIoDeviceTable); return Status; } NTSTATUS BlockIopDestroy ( VOID ) { /* Free the prefetch buffer */ BlMmFreeHeap(BlockIopPrefetchBuffer); /* Set state to non initialized */ BlockIoInitialized = FALSE; /* Return back */ return STATUS_SUCCESS; } ULONG BlockIoEfiHashFunction ( _In_ PBL_HASH_ENTRY Entry, _In_ ULONG TableSize ) { /* Get rid of the alignment bits to have a more unique number */ return ((ULONG_PTR)Entry->Value >> 3) % TableSize; } NTSTATUS BlockIopInitialize ( VOID ) { NTSTATUS Status; /* Allocate the block device table and zero it out */ BlockIoDeviceTableEntries = 8; BlockIoDeviceTable = BlMmAllocateHeap(sizeof(PVOID) * BlockIoDeviceTableEntries); if (!BlockIoDeviceTableEntries) { return STATUS_NO_MEMORY; } RtlZeroMemory(BlockIoDeviceTable, sizeof(PVOID) * BlockIoDeviceTableEntries); /* Register our destructor */ Status = BlpIoRegisterDestroyRoutine(BlockIoDeviceTableDestroy); if (!NT_SUCCESS(Status)) { return Status; } /* Initialize all counters */ BlockIoFirmwareRemovableDiskCount = 0; BlockIoFirmwareRawDiskCount = 0; BlockIoFirmwareCdromCount = 0; /* Initialize the buffers and their sizes */ BlockIopAlignedBuffer = NULL; BlockIopAlignedBufferSize = 0; BlockIopPartialBlockBuffer = NULL; BlockIopPartialBlockBufferSize = 0; BlockIopPrefetchBuffer = NULL; BlockIopReadBlockBuffer = NULL; BlockIopReadBlockBufferSize = 0; /* Allocate the prefetch buffer */ Status = MmPapAllocatePagesInRange(&BlockIopPrefetchBuffer, BlLoaderDeviceMemory, 0x100, 0, 0, NULL, 0); if (NT_SUCCESS(Status)) { /* Initialize the block cache */ Status = BcInitialize(); if (NT_SUCCESS(Status)) { /* Initialize the block device hash table */ Status = BlHtCreate(29, BlockIoEfiHashFunction, NULL, &HashTableId); if (NT_SUCCESS(Status)) { /* Register our destructor */ Status = BlpIoRegisterDestroyRoutine(BlockIopDestroy); if (NT_SUCCESS(Status)) { /* We're good */ BlockIoInitialized = TRUE; } } } } /* Check if this is the failure path */ if (!NT_SUCCESS(Status)) { /* Free the prefetch buffer is one was allocated */ if (BlockIopPrefetchBuffer) { MmPapFreePages(BlockIopPrefetchBuffer, BL_MM_INCLUDE_MAPPED_ALLOCATED); } } /* Return back to the caller */ return Status; } BOOLEAN BlockIoDeviceTableCompare ( _In_ PVOID Entry, _In_ PVOID Argument1, _In_ PVOID Argument2, _In_ PVOID Argument3, _In_ PVOID Argument4 ) { PBL_DEVICE_ENTRY DeviceEntry = (PBL_DEVICE_ENTRY)Entry; PBL_DEVICE_DESCRIPTOR Device = (PBL_DEVICE_DESCRIPTOR)Argument1; /* Compare the two devices */ return BlpDeviceCompare(DeviceEntry->DeviceDescriptor, Device); } NTSTATUS BlockIoOpen ( _In_ PBL_DEVICE_DESCRIPTOR Device, _In_ PBL_DEVICE_ENTRY DeviceEntry ) { NTSTATUS Status; PBL_BLOCK_DEVICE BlockDevice; PBL_DEVICE_ENTRY FoundDeviceEntry; ULONG Dummy; /* Check if the block I/O manager is initialized */ if (!BlockIoInitialized) { /* First call, initialize it now */ Status = BlockIopInitialize(); if (!NT_SUCCESS(Status)) { /* Failed to initialize block I/O */ return Status; } } /* Copy a function table for block I/O devices */ RtlCopyMemory(&DeviceEntry->Callbacks, &BlockIoDeviceFunctionTable, sizeof(DeviceEntry->Callbacks)); /* Allocate a block I/O device */ BlockDevice = BlMmAllocateHeap(sizeof(*BlockDevice)); if (!BlockDevice) { return STATUS_NO_MEMORY; } /* Set this as the device-specific data for this device entry */ Status = STATUS_SUCCESS; DeviceEntry->DeviceSpecificData = BlockDevice; /* Check if we already have this device in our device table */ FoundDeviceEntry = BlTblFindEntry(BlockIoDeviceTable, BlockIoDeviceTableEntries, &Dummy, BlockIoDeviceTableCompare, Device, NULL, NULL, NULL); if (FoundDeviceEntry) { /* We already found a device, so copy its device data and callbacks */ //EfiPrintf(L"Block I/O Device entry found: %p\r\n", FoundDeviceEntry); RtlCopyMemory(BlockDevice, FoundDeviceEntry->DeviceSpecificData, sizeof(*BlockDevice)); RtlCopyMemory(&DeviceEntry->Callbacks, &FoundDeviceEntry->Callbacks, sizeof(DeviceEntry->Callbacks)); return Status; } /* Zero out the device for now */ RtlZeroMemory(BlockDevice, sizeof(*BlockDevice)); /* Is this a disk? */ if (Device->DeviceType == DiskDevice) { /* What type of disk is it? */ switch (Device->Local.Type) { /* Is it a raw physical disk? */ case LocalDevice: case FloppyDevice: case CdRomDevice: /* Open a disk device */ Status = DiskDeviceFunctionTable.Open(Device, DeviceEntry); break; /* Is it a RAM disk? */ case RamDiskDevice: /* Open a RAM disk */ Status = RamDiskDeviceFunctionTable.Open(Device, DeviceEntry); break; /* Is it a file? */ case FileDevice: /* Open a file */ Status = FileDeviceFunctionTable.Open(Device, DeviceEntry); break; /* Is it a VHD? */ case VirtualDiskDevice: /* Open a virtual disk */ Status = VirtualDiskDeviceFunctionTable.Open(Device, DeviceEntry); break; /* Is it something else? */ default: /* Not supported */ Status = STATUS_INVALID_PARAMETER; break; } } else if ((Device->DeviceType == LegacyPartitionDevice) || (Device->DeviceType == PartitionDevice)) { /* This is a partition on a disk, open it as such */ Status = PartitionDeviceFunctionTable.Open(Device, DeviceEntry); } else { /* Other devices are not supported */ Status = STATUS_INVALID_PARAMETER; } /* Check for failure */ if (!NT_SUCCESS(Status)) { /* Free any allocations for this device */ BlockIopFreeAllocations(BlockDevice); } /* Return back to the caller */ return Status; } NTSTATUS BlpDeviceResolveLocate ( _In_ PBL_DEVICE_DESCRIPTOR InputDevice, _Out_ PBL_DEVICE_DESCRIPTOR* LocateDevice ) { EfiPrintf(L"Not implemented!\r\n"); return STATUS_NOT_IMPLEMENTED; } NTSTATUS BlDeviceClose ( _In_ ULONG DeviceId ) { PBL_DEVICE_ENTRY DeviceEntry; /* Validate the device ID */ if (DmTableEntries <= DeviceId) { return STATUS_INVALID_PARAMETER; } /* Make sure there's a device there */ DeviceEntry = DmDeviceTable[DeviceId]; if (DeviceEntry == NULL) { return STATUS_INVALID_PARAMETER; } /* Make sure the device is active */ if (!(DeviceEntry->Flags & BL_DEVICE_ENTRY_OPENED)) { return STATUS_INVALID_PARAMETER; } /* Drop a reference and check if it's the last one */ DeviceEntry->ReferenceCount--; if (!DeviceEntry->ReferenceCount) { /* Mark the device as inactive */ DeviceEntry->Flags = ~BL_DEVICE_ENTRY_OPENED; } /* We're good */ return STATUS_SUCCESS; } NTSTATUS BlpDeviceOpen ( _In_ PBL_DEVICE_DESCRIPTOR Device, _In_ ULONG Flags, _In_ ULONG Unknown, _Out_ PULONG DeviceId ) { NTSTATUS Status; PBL_DEVICE_ENTRY DeviceEntry; PBL_DEVICE_DESCRIPTOR LocateDeviceDescriptor; PBL_REGISTERED_DEVICE RegisteredDevice; PLIST_ENTRY NextEntry, ListHead; DeviceEntry = NULL; /* Check for missing parameters */ if (!(Device) || !(DeviceId) || !(Device->Size)) { /* Bail out */ Status = STATUS_INVALID_PARAMETER; goto Quickie; } /* Make sure both read and write access are set */ if (!(Flags & (BL_DEVICE_READ_ACCESS | BL_DEVICE_WRITE_ACCESS))) { /* Bail out */ Status = STATUS_INVALID_PARAMETER; goto Quickie; } /* Check if the boot device is being opened */ if (Device->DeviceType == BootDevice) { /* Select it */ Device = BlpBootDevice; } /* Check if the 'locate' device is being opened */ if (Device->DeviceType == LocateDevice) { /* Go find it */ Status = BlpDeviceResolveLocate(Device, &LocateDeviceDescriptor); if (!NT_SUCCESS(Status)) { /* Not found, bail out */ goto Quickie; } /* Select it */ Device = LocateDeviceDescriptor; } /* Check if the device isn't ready yet */ if (Device->Flags & 1) { /* Return a failure */ Status = STATUS_DEVICE_NOT_READY; goto Quickie; } /* Check if we already have an entry for the device */ DeviceEntry = BlTblFindEntry(DmDeviceTable, DmTableEntries, DeviceId, DeviceTableCompare, Device, &Flags, &Unknown, NULL); if (DeviceEntry) { /* Return it, taking a reference on it */ *DeviceId = DeviceEntry->DeviceId; ++DeviceEntry->ReferenceCount; DeviceEntry->Flags |= BL_DEVICE_ENTRY_OPENED; return STATUS_SUCCESS; } /* We don't, allocate one */ DeviceEntry = BlMmAllocateHeap(sizeof(*DeviceEntry)); if (!DeviceEntry) { Status = STATUS_NO_MEMORY; goto Quickie; } /* Fill it out */ RtlZeroMemory(DeviceEntry, sizeof(*DeviceEntry)); DeviceEntry->ReferenceCount = 1; DeviceEntry->Flags |= (BL_DEVICE_ENTRY_OPENED | BL_DEVICE_ENTRY_READ_ACCESS | BL_DEVICE_ENTRY_WRITE_ACCESS); DeviceEntry->Unknown = Unknown; /* Save flag 8 if needed */ if (Flags & 8) { DeviceEntry->Flags |= 8; } /* Allocate a device descriptor for the device */ DeviceEntry->DeviceDescriptor = BlMmAllocateHeap(Device->Size); if (!DeviceEntry->DeviceDescriptor) { Status = STATUS_NO_MEMORY; goto Quickie; } /* Copy the descriptor that was passed in */ RtlCopyMemory(DeviceEntry->DeviceDescriptor, Device, Device->Size); /* Now loop the list of dynamically registered devices */ ListHead = &DmRegisteredDevices; NextEntry = ListHead->Flink; while (NextEntry != ListHead) { /* Get the device */ RegisteredDevice = CONTAINING_RECORD(NextEntry, BL_REGISTERED_DEVICE, ListEntry); /* Open the device */ Status = RegisteredDevice->Callbacks.Open(Device, DeviceEntry); if (NT_SUCCESS(Status)) { /* The device was opened, so we have the right one */ goto DeviceOpened; } /* Nope, keep trying */ NextEntry = NextEntry->Flink; } /* Well, it wasn't a dynamic device. Is it a block device? */ if ((Device->DeviceType == PartitionDevice) || (Device->DeviceType == DiskDevice) || (Device->DeviceType == LegacyPartitionDevice)) { /* Call the Block I/O handler */ Status = BlockIoDeviceFunctionTable.Open(Device, DeviceEntry); } else if (Device->DeviceType == SerialDevice) { /* It's a serial device, call the serial device handler */ Status = SerialPortFunctionTable.Open(Device, DeviceEntry); } else if (Device->DeviceType == UdpDevice) { /* It's a network device, call the UDP device handler */ Status = UdpFunctionTable.Open(Device, DeviceEntry); } else { /* Unsupported type of device */ Status = STATUS_NOT_IMPLEMENTED; } /* Check if the device was opened successfully */ if (NT_SUCCESS(Status)) { DeviceOpened: /* Save the entry in the device table */ Status = BlTblSetEntry(&DmDeviceTable, &DmTableEntries, DeviceEntry, DeviceId, DeviceTablePurge); if (NT_SUCCESS(Status)) { /* It worked -- return the ID in the table to the caller */ EfiPrintf(L"Device ID: %lx\r\n", *DeviceId); DeviceEntry->DeviceId = *DeviceId; return STATUS_SUCCESS; } } Quickie: /* Failure path -- did we allocate a device entry? */ EfiPrintf(L"Block failure: %lx\r\n", Status); if (DeviceEntry) { /* Yep -- did it have a descriptor? */ if (DeviceEntry->DeviceDescriptor) { /* Free it */ BlMmFreeHeap(DeviceEntry->DeviceDescriptor); } /* Free the entry */ BlMmFreeHeap(DeviceEntry); } /* Return the failure */ return Status; } NTSTATUS BlpDeviceInitialize ( VOID ) { NTSTATUS Status; /* Initialize the table count and list of devices */ DmTableEntries = 8; InitializeListHead(&DmRegisteredDevices); /* Initialize device information */ DmDeviceIoInformation.ReadCount = 0; DmDeviceIoInformation.WriteCount = 0; /* Allocate the device table */ DmDeviceTable = BlMmAllocateHeap(DmTableEntries * sizeof(PVOID)); if (DmDeviceTable) { /* Clear it */ RtlZeroMemory(DmDeviceTable, DmTableEntries * sizeof(PVOID)); #if BL_BITLOCKER_SUPPORT /* Initialize BitLocker support */ Status = FvebInitialize(); #else Status = STATUS_SUCCESS; #endif } else { /* No memory, we'll fail */ Status = STATUS_NO_MEMORY; } /* Return initialization state */ return Status; }