From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../Universal/Disk/PartitionDxe/Partition.c | 1369 ++++++++++++++++++++ 1 file changed, 1369 insertions(+) create mode 100644 roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c (limited to 'roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c') diff --git a/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c new file mode 100644 index 000000000..f10ce7c65 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c @@ -0,0 +1,1369 @@ +/** @file + Partition driver that produces logical BlockIo devices from a physical + BlockIo device. The logical BlockIo devices are based on the format + of the raw block devices media. Currently "El Torito CD-ROM", UDF, Legacy + MBR, and GPT partition schemes are supported. + +Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc. +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "Partition.h" + +// +// Partition Driver Global Variables. +// +EFI_DRIVER_BINDING_PROTOCOL gPartitionDriverBinding = { + PartitionDriverBindingSupported, + PartitionDriverBindingStart, + PartitionDriverBindingStop, + // + // Grub4Dos copies the BPB of the first partition to the MBR. If the + // DriverBindingStart() of the Fat driver gets run before that of Partition + // driver only the first partition can be recognized. + // Let the driver binding version of Partition driver be higher than that of + // Fat driver to make sure the DriverBindingStart() of the Partition driver + // gets run before that of Fat driver so that all the partitions can be recognized. + // + 0xb, + NULL, + NULL +}; + +// +// Prioritized function list to detect partition table. +// Refer to UEFI Spec 13.3.2 Partition Discovery, the block device +// should be scanned in below order: +// 1. GPT +// 2. ISO 9660 (El Torito) (or UDF) +// 3. MBR +// 4. no partiton found +// Note: UDF is using a same method as booting from CD-ROM, so put it along +// with CD-ROM check. +// +PARTITION_DETECT_ROUTINE mPartitionDetectRoutineTable[] = { + PartitionInstallGptChildHandles, + PartitionInstallUdfChildHandles, + PartitionInstallMbrChildHandles, + NULL +}; + +/** + Test to see if this driver supports ControllerHandle. Any ControllerHandle + than contains a BlockIo and DiskIo protocol or a BlockIo2 protocol can be + supported. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device + @retval EFI_ALREADY_STARTED This driver is already running on this device + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +PartitionDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_DEV_PATH *Node; + + // + // Check RemainingDevicePath validation + // + if (RemainingDevicePath != NULL) { + // + // Check if RemainingDevicePath is the End of Device Path Node, + // if yes, go on checking other conditions + // + if (!IsDevicePathEnd (RemainingDevicePath)) { + // + // If RemainingDevicePath isn't the End of Device Path Node, + // check its validation + // + Node = (EFI_DEV_PATH *) RemainingDevicePath; + if (Node->DevPath.Type != MEDIA_DEVICE_PATH || + Node->DevPath.SubType != MEDIA_HARDDRIVE_DP || + DevicePathNodeLength (&Node->DevPath) != sizeof (HARDDRIVE_DEVICE_PATH)) { + return EFI_UNSUPPORTED; + } + } + } + + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + if (EFI_ERROR (Status)) { + return Status; + } + // + // Close the I/O Abstraction(s) used to perform the supported test + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + // + // Open the EFI Device Path protocol needed to perform the supported test + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Close protocol, don't use device path protocol in the Support() function + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + // + // Open the IO Abstraction(s) needed to perform the supported test + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + +/** + Start this driver on ControllerHandle by opening a Block IO or a Block IO2 + or both, and Disk IO protocol, reading Device Path, and creating a child + handle with a Disk IO and device path protocol. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +PartitionDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_STATUS OpenStatus; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_BLOCK_IO2_PROTOCOL *BlockIo2; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_DISK_IO2_PROTOCOL *DiskIo2; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + PARTITION_DETECT_ROUTINE *Routine; + BOOLEAN MediaPresent; + EFI_TPL OldTpl; + + BlockIo2 = NULL; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + // + // Check RemainingDevicePath validation + // + if (RemainingDevicePath != NULL) { + // + // Check if RemainingDevicePath is the End of Device Path Node, + // if yes, return EFI_SUCCESS + // + if (IsDevicePathEnd (RemainingDevicePath)) { + Status = EFI_SUCCESS; + goto Exit; + } + } + + // + // Try to open BlockIO and BlockIO2. If BlockIO would be opened, continue, + // otherwise, return error. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIoProtocolGuid, + (VOID **) &BlockIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiBlockIo2ProtocolGuid, + (VOID **) &BlockIo2, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + BlockIo2 = NULL; + } + + // + // Get the Device Path Protocol on ControllerHandle's handle. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + goto Exit; + } + + // + // Get the DiskIo and DiskIo2. + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + goto Exit; + } + + OpenStatus = Status; + + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIo2ProtocolGuid, + (VOID **) &DiskIo2, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + DiskIo2 = NULL; + } + + // + // Try to read blocks when there's media or it is removable physical partition. + // + Status = EFI_UNSUPPORTED; + MediaPresent = BlockIo->Media->MediaPresent; + if (BlockIo->Media->MediaPresent || + (BlockIo->Media->RemovableMedia && !BlockIo->Media->LogicalPartition)) { + // + // Try for GPT, then legacy MBR partition types, and then UDF and El Torito. + // If the media supports a given partition type install child handles to + // represent the partitions described by the media. + // + Routine = &mPartitionDetectRoutineTable[0]; + while (*Routine != NULL) { + Status = (*Routine) ( + This, + ControllerHandle, + DiskIo, + DiskIo2, + BlockIo, + BlockIo2, + ParentDevicePath + ); + if (!EFI_ERROR (Status) || Status == EFI_MEDIA_CHANGED || Status == EFI_NO_MEDIA) { + break; + } + Routine++; + } + } + // + // In the case that the driver is already started (OpenStatus == EFI_ALREADY_STARTED), + // the DevicePathProtocol and the DiskIoProtocol are not actually opened by the + // driver. So don't try to close them. Otherwise, we will break the dependency + // between the controller and the driver set up before. + // + // In the case that when the media changes on a device it will Reinstall the + // BlockIo interaface. This will cause a call to our Stop(), and a subsequent + // reentrant call to our Start() successfully. We should leave the device open + // when this happen. The "media change" case includes either the status is + // EFI_MEDIA_CHANGED or it is a "media" to "no media" change. + // + if (EFI_ERROR (Status) && + !EFI_ERROR (OpenStatus) && + Status != EFI_MEDIA_CHANGED && + !(MediaPresent && Status == EFI_NO_MEDIA)) { + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + // + // Close Parent DiskIo2 if has. + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIo2ProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + } + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Stop this driver on ControllerHandle. Support stopping any child handles + created by this driver. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +PartitionDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + UINTN Index; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_BLOCK_IO2_PROTOCOL *BlockIo2; + BOOLEAN AllChildrenStopped; + PARTITION_PRIVATE_DATA *Private; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_GUID *TypeGuid; + + BlockIo = NULL; + BlockIo2 = NULL; + Private = NULL; + + if (NumberOfChildren == 0) { + // + // In the case of re-entry of the PartitionDriverBindingStop, the + // NumberOfChildren may not reflect the actual number of children on the + // bus driver. Hence, additional check is needed here. + // + if (HasChildren (ControllerHandle)) { + DEBUG((EFI_D_ERROR, "PartitionDriverBindingStop: Still has child.\n")); + return EFI_DEVICE_ERROR; + } + + // + // Close the bus driver + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + // + // Close Parent BlockIO2 if has. + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIo2ProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); + return EFI_SUCCESS; + } + + AllChildrenStopped = TRUE; + for (Index = 0; Index < NumberOfChildren; Index++) { + gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiBlockIoProtocolGuid, + (VOID **) &BlockIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + // + // Try to locate BlockIo2. + // + gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiBlockIo2ProtocolGuid, + (VOID **) &BlockIo2, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + + Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (BlockIo); + if (Private->InStop) { + // + // If the child handle is going to be stopped again during the re-entry + // of DriverBindingStop, just do nothing. + // + break; + } + Private->InStop = TRUE; + + BlockIo->FlushBlocks (BlockIo); + + if (BlockIo2 != NULL) { + Status = BlockIo2->FlushBlocksEx (BlockIo2, NULL); + DEBUG((EFI_D_ERROR, "PartitionDriverBindingStop: FlushBlocksEx returned with %r\n", Status)); + } else { + Status = EFI_SUCCESS; + } + + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ChildHandleBuffer[Index] + ); + + if (IsZeroGuid (&Private->TypeGuid)) { + TypeGuid = NULL; + } else { + TypeGuid = &Private->TypeGuid; + } + + // + // All Software protocols have be freed from the handle so remove it. + // Remove the BlockIo Protocol if has. + // Remove the BlockIo2 Protocol if has. + // + if (BlockIo2 != NULL) { + // + // Some device drivers might re-install the BlockIO(2) protocols for a + // media change condition. Therefore, if the FlushBlocksEx returned with + // EFI_MEDIA_CHANGED, just let the BindingStop fail to avoid potential + // reference of already stopped child handle. + // + if (Status != EFI_MEDIA_CHANGED) { + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + Private->DevicePath, + &gEfiBlockIoProtocolGuid, + &Private->BlockIo, + &gEfiBlockIo2ProtocolGuid, + &Private->BlockIo2, + &gEfiPartitionInfoProtocolGuid, + &Private->PartitionInfo, + TypeGuid, + NULL, + NULL + ); + } + } else { + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + Private->DevicePath, + &gEfiBlockIoProtocolGuid, + &Private->BlockIo, + &gEfiPartitionInfoProtocolGuid, + &Private->PartitionInfo, + TypeGuid, + NULL, + NULL + ); + } + + if (EFI_ERROR (Status)) { + Private->InStop = FALSE; + gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &DiskIo, + This->DriverBindingHandle, + ChildHandleBuffer[Index], + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + FreePool (Private->DevicePath); + FreePool (Private); + } + + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + if (Status == EFI_MEDIA_CHANGED) { + break; + } + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Reset the Block Device. + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +PartitionReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + PARTITION_PRIVATE_DATA *Private; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (This); + + return Private->ParentBlockIo->Reset ( + Private->ParentBlockIo, + ExtendedVerification + ); +} + +/** + Probe the media status and return EFI_NO_MEDIA or EFI_MEDIA_CHANGED + for no media or media change case. Otherwise DefaultStatus is returned. + + @param DiskIo Pointer to the DiskIo instance. + @param MediaId Id of the media, changes every time the media is replaced. + @param DefaultStatus The default status to return when it's not the no media + or media change case. + + @retval EFI_NO_MEDIA There is no media. + @retval EFI_MEDIA_CHANGED The media was changed. + @retval others The default status to return. +**/ +EFI_STATUS +ProbeMediaStatus ( + IN EFI_DISK_IO_PROTOCOL *DiskIo, + IN UINT32 MediaId, + IN EFI_STATUS DefaultStatus + ) +{ + EFI_STATUS Status; + UINT8 Buffer[1]; + + // + // Read 1 byte from offset 0 to check if the MediaId is still valid. + // The reading operation is synchronious thus it is not worth it to + // allocate a buffer from the pool. The destination buffer for the + // data is in the stack. + // + Status = DiskIo->ReadDisk (DiskIo, MediaId, 0, 1, (VOID*)Buffer); + if ((Status == EFI_NO_MEDIA) || (Status == EFI_MEDIA_CHANGED)) { + return Status; + } + return DefaultStatus; +} + +/** + Read by using the Disk IO protocol on the parent device. Lba addresses + must be converted to byte offsets. + + @param This Protocol instance pointer. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer Buffer containing read data + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not + valid for the device. + +**/ +EFI_STATUS +EFIAPI +PartitionReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + PARTITION_PRIVATE_DATA *Private; + UINT64 Offset; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (This); + + if (BufferSize % Private->BlockSize != 0) { + return ProbeMediaStatus (Private->DiskIo, MediaId, EFI_BAD_BUFFER_SIZE); + } + + Offset = MultU64x32 (Lba, Private->BlockSize) + Private->Start; + if (Offset + BufferSize > Private->End) { + return ProbeMediaStatus (Private->DiskIo, MediaId, EFI_INVALID_PARAMETER); + } + // + // Because some kinds of partition have different block size from their parent + // device, we call the Disk IO protocol on the parent device, not the Block IO + // protocol + // + return Private->DiskIo->ReadDisk (Private->DiskIo, MediaId, Offset, BufferSize, Buffer); +} + +/** + Write by using the Disk IO protocol on the parent device. Lba addresses + must be converted to byte offsets. + + @param[in] This Protocol instance pointer. + @param[in] MediaId Id of the media, changes every time the media is replaced. + @param[in] Lba The starting Logical Block Address to read from + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[in] Buffer Buffer containing data to be written to device. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains a LBA that is not + valid for the device. + +**/ +EFI_STATUS +EFIAPI +PartitionWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + PARTITION_PRIVATE_DATA *Private; + UINT64 Offset; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (This); + + if (BufferSize % Private->BlockSize != 0) { + return ProbeMediaStatus (Private->DiskIo, MediaId, EFI_BAD_BUFFER_SIZE); + } + + Offset = MultU64x32 (Lba, Private->BlockSize) + Private->Start; + if (Offset + BufferSize > Private->End) { + return ProbeMediaStatus (Private->DiskIo, MediaId, EFI_INVALID_PARAMETER); + } + // + // Because some kinds of partition have different block size from their parent + // device, we call the Disk IO protocol on the parent device, not the Block IO + // protocol + // + return Private->DiskIo->WriteDisk (Private->DiskIo, MediaId, Offset, BufferSize, Buffer); +} + + +/** + Flush the parent Block Device. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS All outstanding data was written to the device + @retval EFI_DEVICE_ERROR The device reported an error while writting back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +PartitionFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ) +{ + PARTITION_PRIVATE_DATA *Private; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (This); + + return Private->ParentBlockIo->FlushBlocks (Private->ParentBlockIo); +} + +/** + Probe the media status and return EFI_NO_MEDIA or EFI_MEDIA_CHANGED + for no media or media change case. Otherwise DefaultStatus is returned. + + @param DiskIo2 Pointer to the DiskIo2 instance. + @param MediaId Id of the media, changes every time the media is replaced. + @param DefaultStatus The default status to return when it's not the no media + or media change case. + + @retval EFI_NO_MEDIA There is no media. + @retval EFI_MEDIA_CHANGED The media was changed. + @retval others The default status to return. +**/ +EFI_STATUS +ProbeMediaStatusEx ( + IN EFI_DISK_IO2_PROTOCOL *DiskIo2, + IN UINT32 MediaId, + IN EFI_STATUS DefaultStatus + ) +{ + EFI_STATUS Status; + UINT8 Buffer[1]; + + // + // Read 1 byte from offset 0 to check if the MediaId is still valid. + // The reading operation is synchronious thus it is not worth it to + // allocate a buffer from the pool. The destination buffer for the + // data is in the stack. + // + Status = DiskIo2->ReadDiskEx (DiskIo2, MediaId, 0, NULL, 1, (VOID*)Buffer); + if ((Status == EFI_NO_MEDIA) || (Status == EFI_MEDIA_CHANGED)) { + return Status; + } + return DefaultStatus; +} + +/** + Reset the Block Device throught Block I/O2 protocol. + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +PartitionResetEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + PARTITION_PRIVATE_DATA *Private; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO2_THIS (This); + + return Private->ParentBlockIo2->Reset ( + Private->ParentBlockIo2, + ExtendedVerification + ); +} + +/** + The general callback for the DiskIo2 interfaces. + @param Event Event whose notification function is being invoked. + @param Context The pointer to the notification function's context, + which points to the PARTITION_ACCESS_TASK instance. +**/ +VOID +EFIAPI +PartitionOnAccessComplete ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + PARTITION_ACCESS_TASK *Task; + + Task = (PARTITION_ACCESS_TASK *) Context; + + gBS->CloseEvent (Event); + + Task->BlockIo2Token->TransactionStatus = Task->DiskIo2Token.TransactionStatus; + gBS->SignalEvent (Task->BlockIo2Token->Event); + + FreePool (Task); +} + +/** + Create a new PARTITION_ACCESS_TASK instance. + + @param Token Pointer to the EFI_BLOCK_IO2_TOKEN. + + @return Pointer to the created PARTITION_ACCESS_TASK instance or NULL upon failure. +**/ +PARTITION_ACCESS_TASK * +PartitionCreateAccessTask ( + IN EFI_BLOCK_IO2_TOKEN *Token + ) +{ + EFI_STATUS Status; + PARTITION_ACCESS_TASK *Task; + + Task = AllocatePool (sizeof (*Task)); + if (Task == NULL) { + return NULL; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + PartitionOnAccessComplete, + Task, + &Task->DiskIo2Token.Event + ); + if (EFI_ERROR (Status)) { + FreePool (Task); + return NULL; + } + + Task->BlockIo2Token = Token; + + return Task; +} + +/** + Read BufferSize bytes from Lba into Buffer. + + This function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. + If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_or EFI_MEDIA_CHANGED is returned and + non-blocking I/O is being used, the Event associated with this request will + not be signaled. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId Id of the media, changes every time the media is + replaced. + @param[in] Lba The starting Logical Block Address to read from. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[out] Buffer A pointer to the destination buffer for the data. The + caller is responsible for either having implicit or + explicit ownership of the buffer. + + @retval EFI_SUCCESS The read request was queued if Token->Event is + not NULL.The data was read correctly from the + device if the Token->Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while performing + the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the + intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. +**/ +EFI_STATUS +EFIAPI +PartitionReadBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + PARTITION_PRIVATE_DATA *Private; + UINT64 Offset; + PARTITION_ACCESS_TASK *Task; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO2_THIS (This); + + if (BufferSize % Private->BlockSize != 0) { + return ProbeMediaStatusEx (Private->DiskIo2, MediaId, EFI_BAD_BUFFER_SIZE); + } + + Offset = MultU64x32 (Lba, Private->BlockSize) + Private->Start; + if (Offset + BufferSize > Private->End) { + return ProbeMediaStatusEx (Private->DiskIo2, MediaId, EFI_INVALID_PARAMETER); + } + + if ((Token != NULL) && (Token->Event != NULL)) { + Task = PartitionCreateAccessTask (Token); + if (Task == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Private->DiskIo2->ReadDiskEx (Private->DiskIo2, MediaId, Offset, &Task->DiskIo2Token, BufferSize, Buffer); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Task->DiskIo2Token.Event); + FreePool (Task); + } + } else { + Status = Private->DiskIo2->ReadDiskEx (Private->DiskIo2, MediaId, Offset, NULL, BufferSize, Buffer); + } + + return Status; +} + +/** + Write BufferSize bytes from Lba into Buffer. + + This function writes the requested number of blocks to the device. All blocks + are written, or an error is returned.If EFI_DEVICE_ERROR, EFI_NO_MEDIA, + EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED is returned and non-blocking I/O is + being used, the Event associated with this request will not be signaled. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the write request is for. + @param[in] Lba The starting logical block address to be written. The + caller is responsible for writing to only legitimate + locations. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[in] Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The write request was queued if Event is not NULL. + The data was written correctly to the device if + the Event is NULL. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. + +**/ +EFI_STATUS +EFIAPI +PartitionWriteBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + PARTITION_PRIVATE_DATA *Private; + UINT64 Offset; + PARTITION_ACCESS_TASK *Task; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO2_THIS (This); + + if (BufferSize % Private->BlockSize != 0) { + return ProbeMediaStatusEx (Private->DiskIo2, MediaId, EFI_BAD_BUFFER_SIZE); + } + + Offset = MultU64x32 (Lba, Private->BlockSize) + Private->Start; + if (Offset + BufferSize > Private->End) { + return ProbeMediaStatusEx (Private->DiskIo2, MediaId, EFI_INVALID_PARAMETER); + } + + if ((Token != NULL) && (Token->Event != NULL)) { + Task = PartitionCreateAccessTask (Token); + if (Task == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Private->DiskIo2->WriteDiskEx (Private->DiskIo2, MediaId, Offset, &Task->DiskIo2Token, BufferSize, Buffer); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Task->DiskIo2Token.Event); + FreePool (Task); + } + } else { + Status = Private->DiskIo2->WriteDiskEx (Private->DiskIo2, MediaId, Offset, NULL, BufferSize, Buffer); + } + return Status; +} + +/** + Flush the Block Device. + + If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED + is returned and non-blocking I/O is being used, the Event associated with + this request will not be signaled. + + @param[in] This Indicates a pointer to the calling context. + @param[in, out] Token A pointer to the token associated with the transaction + + @retval EFI_SUCCESS The flush request was queued if Event is not NULL. + All outstanding data was written correctly to the + device if the Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while writting back + the data. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. + +**/ +EFI_STATUS +EFIAPI +PartitionFlushBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN OUT EFI_BLOCK_IO2_TOKEN *Token + ) +{ + EFI_STATUS Status; + PARTITION_PRIVATE_DATA *Private; + PARTITION_ACCESS_TASK *Task; + + Private = PARTITION_DEVICE_FROM_BLOCK_IO2_THIS (This); + + if ((Token != NULL) && (Token->Event != NULL)) { + Task = PartitionCreateAccessTask (Token); + if (Task == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Private->DiskIo2->FlushDiskEx (Private->DiskIo2, &Task->DiskIo2Token); + if (EFI_ERROR (Status)) { + gBS->CloseEvent (Task->DiskIo2Token.Event); + FreePool (Task); + } + } else { + Status = Private->DiskIo2->FlushDiskEx (Private->DiskIo2, NULL); + } + return Status; +} + + +/** + Create a child handle for a logical block device that represents the + bytes Start to End of the Parent Block IO device. + + @param[in] This Protocol instance pointer. + @param[in] ParentHandle Parent Handle for new child. + @param[in] ParentDiskIo Parent DiskIo interface. + @param[in] ParentDiskIo2 Parent DiskIo2 interface. + @param[in] ParentBlockIo Parent BlockIo interface. + @param[in] ParentBlockIo2 Parent BlockIo2 interface. + @param[in] ParentDevicePath Parent Device Path. + @param[in] DevicePathNode Child Device Path node. + @param[in] PartitionInfo Child Partition Information interface. + @param[in] Start Start Block. + @param[in] End End Block. + @param[in] BlockSize Child block size. + @param[in] TypeGuid Partition GUID Type. + + @retval EFI_SUCCESS A child handle was added. + @retval other A child handle was not added. + +**/ +EFI_STATUS +PartitionInstallChildHandle ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ParentHandle, + IN EFI_DISK_IO_PROTOCOL *ParentDiskIo, + IN EFI_DISK_IO2_PROTOCOL *ParentDiskIo2, + IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIo, + IN EFI_BLOCK_IO2_PROTOCOL *ParentBlockIo2, + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePathNode, + IN EFI_PARTITION_INFO_PROTOCOL *PartitionInfo, + IN EFI_LBA Start, + IN EFI_LBA End, + IN UINT32 BlockSize, + IN EFI_GUID *TypeGuid + ) +{ + EFI_STATUS Status; + PARTITION_PRIVATE_DATA *Private; + + Status = EFI_SUCCESS; + Private = AllocateZeroPool (sizeof (PARTITION_PRIVATE_DATA)); + if (Private == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Private->Signature = PARTITION_PRIVATE_DATA_SIGNATURE; + + Private->Start = MultU64x32 (Start, BlockSize); + Private->End = MultU64x32 (End + 1, BlockSize); + + Private->BlockSize = BlockSize; + Private->ParentBlockIo = ParentBlockIo; + Private->ParentBlockIo2 = ParentBlockIo2; + Private->DiskIo = ParentDiskIo; + Private->DiskIo2 = ParentDiskIo2; + + // + // Set the BlockIO into Private Data. + // + Private->BlockIo.Revision = ParentBlockIo->Revision; + + Private->BlockIo.Media = &Private->Media; + CopyMem (Private->BlockIo.Media, ParentBlockIo->Media, sizeof (EFI_BLOCK_IO_MEDIA)); + + Private->BlockIo.Reset = PartitionReset; + Private->BlockIo.ReadBlocks = PartitionReadBlocks; + Private->BlockIo.WriteBlocks = PartitionWriteBlocks; + Private->BlockIo.FlushBlocks = PartitionFlushBlocks; + + // + // Set the BlockIO2 into Private Data. + // + if (Private->DiskIo2 != NULL) { + ASSERT (Private->ParentBlockIo2 != NULL); + Private->BlockIo2.Media = &Private->Media2; + CopyMem (Private->BlockIo2.Media, ParentBlockIo2->Media, sizeof (EFI_BLOCK_IO_MEDIA)); + + Private->BlockIo2.Reset = PartitionResetEx; + Private->BlockIo2.ReadBlocksEx = PartitionReadBlocksEx; + Private->BlockIo2.WriteBlocksEx = PartitionWriteBlocksEx; + Private->BlockIo2.FlushBlocksEx = PartitionFlushBlocksEx; + } + + Private->Media.IoAlign = 0; + Private->Media.LogicalPartition = TRUE; + Private->Media.LastBlock = End - Start; + + Private->Media.BlockSize = (UINT32) BlockSize; + + Private->Media2.IoAlign = 0; + Private->Media2.LogicalPartition = TRUE; + Private->Media2.LastBlock = Private->Media.LastBlock; + Private->Media2.BlockSize = (UINT32) BlockSize; + + // + // Per UEFI Spec, LowestAlignedLba, LogicalBlocksPerPhysicalBlock and OptimalTransferLengthGranularity must be 0 + // for logical partitions. + // + if (Private->BlockIo.Revision >= EFI_BLOCK_IO_PROTOCOL_REVISION2) { + Private->Media.LowestAlignedLba = 0; + Private->Media.LogicalBlocksPerPhysicalBlock = 0; + Private->Media2.LowestAlignedLba = 0; + Private->Media2.LogicalBlocksPerPhysicalBlock = 0; + if (Private->BlockIo.Revision >= EFI_BLOCK_IO_PROTOCOL_REVISION3) { + Private->Media.OptimalTransferLengthGranularity = 0; + Private->Media2.OptimalTransferLengthGranularity = 0; + } + } + + Private->DevicePath = AppendDevicePathNode (ParentDevicePath, DevicePathNode); + + if (Private->DevicePath == NULL) { + FreePool (Private); + return EFI_OUT_OF_RESOURCES; + } + + // + // Set the PartitionInfo into Private Data. + // + CopyMem (&Private->PartitionInfo, PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL)); + + if (TypeGuid != NULL) { + CopyGuid(&(Private->TypeGuid), TypeGuid); + } else { + ZeroMem ((VOID *)&(Private->TypeGuid), sizeof (EFI_GUID)); + } + + // + // Create the new handle. + // + Private->Handle = NULL; + if (Private->DiskIo2 != NULL) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Handle, + &gEfiDevicePathProtocolGuid, + Private->DevicePath, + &gEfiBlockIoProtocolGuid, + &Private->BlockIo, + &gEfiBlockIo2ProtocolGuid, + &Private->BlockIo2, + &gEfiPartitionInfoProtocolGuid, + &Private->PartitionInfo, + TypeGuid, + NULL, + NULL + ); + } else { + Status = gBS->InstallMultipleProtocolInterfaces ( + &Private->Handle, + &gEfiDevicePathProtocolGuid, + Private->DevicePath, + &gEfiBlockIoProtocolGuid, + &Private->BlockIo, + &gEfiPartitionInfoProtocolGuid, + &Private->PartitionInfo, + TypeGuid, + NULL, + NULL + ); + } + + if (!EFI_ERROR (Status)) { + // + // Open the Parent Handle for the child + // + Status = gBS->OpenProtocol ( + ParentHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &ParentDiskIo, + This->DriverBindingHandle, + Private->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + FreePool (Private->DevicePath); + FreePool (Private); + + // + // if the Status == EFI_ALREADY_STARTED, it means the child handles + // are already installed. So return EFI_SUCCESS to avoid do the next + // partition type check. + // + if (Status == EFI_ALREADY_STARTED) { + Status = EFI_SUCCESS; + } + } + + return Status; +} + + +/** + The user Entry Point for module Partition. The user code starts with this function. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializePartition ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gPartitionDriverBinding, + ImageHandle, + &gPartitionComponentName, + &gPartitionComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + + return Status; +} + + +/** + Test to see if there is any child on ControllerHandle. + + @param[in] ControllerHandle Handle of device to test. + + @retval TRUE There are children on the ControllerHandle. + @retval FALSE No child is on the ControllerHandle. + +**/ +BOOLEAN +HasChildren ( + IN EFI_HANDLE ControllerHandle + ) +{ + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; + UINTN EntryCount; + EFI_STATUS Status; + UINTN Index; + + Status = gBS->OpenProtocolInformation ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + &OpenInfoBuffer, + &EntryCount + ); + ASSERT_EFI_ERROR (Status); + + for (Index = 0; Index < EntryCount; Index++) { + if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) { + break; + } + } + FreePool (OpenInfoBuffer); + + return (BOOLEAN) (Index < EntryCount); +} + -- cgit