X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=MdeModulePkg%2FUniversal%2FDisk%2FPartitionDxe%2FPartition.c;h=d1c878ad2e7ab3e35b2cbabf76fa5e52ae6f8395;hp=b68c1db3e66cd0ffd3230c0cc214e7f562d3a5f5;hb=9d510e61fceee7b92955ef9a3c20343752d8ce3f;hpb=d38a0f446fbb13a7969294cc6665ee7c789d9fc2 diff --git a/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c b/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c index b68c1db3e6..d1c878ad2e 100644 --- a/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c +++ b/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c @@ -1,17 +1,12 @@ /** @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", Legacy + of the raw block devices media. Currently "El Torito CD-ROM", UDF, Legacy MBR, and GPT partition schemes are supported. - Copyright (c) 2006 - 2007, Intel Corporation - All rights reserved. This program and the accompanying materials - are licensed and made available under the terms and conditions of the BSD License - which accompanies this distribution. The full text of the license may be found at - http://opensource.org/licenses/bsd-license.php - - THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +Copyright (c) 2018 Qualcomm Datacenter Technologies, Inc. +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -19,35 +14,44 @@ #include "Partition.h" // -// Partition Driver Global Variables +// Partition Driver Global Variables. // EFI_DRIVER_BINDING_PROTOCOL gPartitionDriverBinding = { PartitionDriverBindingSupported, PartitionDriverBindingStart, PartitionDriverBindingStop, - 0xa, + // + // 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 }; -STATIC +// +// Prioritized function list to detect partition table. +// PARTITION_DETECT_ROUTINE mPartitionDetectRoutineTable[] = { PartitionInstallGptChildHandles, - PartitionInstallElToritoChildHandles, PartitionInstallMbrChildHandles, + PartitionInstallUdfChildHandles, NULL }; - - /** Test to see if this driver supports ControllerHandle. Any ControllerHandle - than contains a BlockIo and DiskIo protocol can be supported. + than contains a BlockIo and DiskIo protocol or a BlockIo2 protocol can be + supported. - @param This Protocol instance pointer. - @param ControllerHandle Handle of device to test - @param RemainingDevicePath Optional parameter use to pick a specific child - device to start. + @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 @@ -67,22 +71,35 @@ PartitionDriverBindingSupported ( EFI_DISK_IO_PROTOCOL *DiskIo; EFI_DEV_PATH *Node; + // + // Check RemainingDevicePath validation + // if (RemainingDevicePath != NULL) { - Node = (EFI_DEV_PATH *) RemainingDevicePath; - if (Node->DevPath.Type != MEDIA_DEVICE_PATH || + // + // 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; + 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, - &gEfiDevicePathProtocolGuid, - (VOID **) &ParentDevicePath, + &gEfiDiskIoProtocolGuid, + (VOID **) &DiskIo, This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER @@ -90,7 +107,6 @@ PartitionDriverBindingSupported ( if (Status == EFI_ALREADY_STARTED) { return EFI_SUCCESS; } - if (EFI_ERROR (Status)) { return Status; } @@ -98,19 +114,19 @@ PartitionDriverBindingSupported ( // Close the I/O Abstraction(s) used to perform the supported test // gBS->CloseProtocol ( - ControllerHandle, - &gEfiDevicePathProtocolGuid, - This->DriverBindingHandle, - ControllerHandle - ); + ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); // - // Open the IO Abstraction(s) needed to perform the supported test + // Open the EFI Device Path protocol needed to perform the supported test // Status = gBS->OpenProtocol ( ControllerHandle, - &gEfiDiskIoProtocolGuid, - (VOID **) &DiskIo, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER @@ -122,12 +138,13 @@ PartitionDriverBindingSupported ( if (EFI_ERROR (Status)) { return Status; } + // - // Close the I/O Abstraction(s) used to perform the supported test + // Close protocol, don't use device path protocol in the Support() function // gBS->CloseProtocol ( ControllerHandle, - &gEfiDiskIoProtocolGuid, + &gEfiDevicePathProtocolGuid, This->DriverBindingHandle, ControllerHandle ); @@ -147,16 +164,15 @@ PartitionDriverBindingSupported ( return Status; } - /** - Start this driver on ControllerHandle by opening a Block IO and Disk IO - protocol, reading Device Path, and creating a child handle with a - Disk IO and device path protocol. + 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 This Protocol instance pointer. - @param ControllerHandle Handle of device to bind driver to - @param RemainingDevicePath Optional parameter use to pick a specific child - device to start. + @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 @@ -174,10 +190,34 @@ PartitionDriverBindingStart ( 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, @@ -187,10 +227,23 @@ PartitionDriverBindingStart ( EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { - return 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 + // Get the Device Path Protocol on ControllerHandle's handle. // Status = gBS->OpenProtocol ( ControllerHandle, @@ -201,9 +254,12 @@ PartitionDriverBindingStart ( EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { - return Status; + goto Exit; } + // + // Get the DiskIo and DiskIo2. + // Status = gBS->OpenProtocol ( ControllerHandle, &gEfiDiskIoProtocolGuid, @@ -219,20 +275,34 @@ PartitionDriverBindingStart ( This->DriverBindingHandle, ControllerHandle ); - return Status; + 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; + } + // - // If no media is present, do nothing here. + // Try to read blocks when there's media or it is removable physical partition. // - Status = EFI_UNSUPPORTED; - if (BlockIo->Media->MediaPresent) { + Status = EFI_UNSUPPORTED; + MediaPresent = BlockIo->Media->MediaPresent; + if (BlockIo->Media->MediaPresent || + (BlockIo->Media->RemovableMedia && !BlockIo->Media->LogicalPartition)) { // - // Try for GPT, then El Torito, and then legacy MBR partition types. If the - // media supports a given partition type install child handles to represent - // the partitions described by the media. + // 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) { @@ -240,10 +310,12 @@ PartitionDriverBindingStart ( This, ControllerHandle, DiskIo, + DiskIo2, BlockIo, + BlockIo2, ParentDevicePath ); - if (!EFI_ERROR (Status) || Status == EFI_MEDIA_CHANGED) { + if (!EFI_ERROR (Status) || Status == EFI_MEDIA_CHANGED || Status == EFI_NO_MEDIA) { break; } Routine++; @@ -255,13 +327,31 @@ PartitionDriverBindingStart ( // driver. So don't try to close them. Otherwise, we will break the dependency // between the controller and the driver set up before. // - if (EFI_ERROR (Status) && !EFI_ERROR (OpenStatus) && Status != EFI_MEDIA_CHANGED) { + // 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, @@ -271,12 +361,13 @@ PartitionDriverBindingStart ( ); } +Exit: + gBS->RestoreTPL (OldTpl); return Status; } - /** - Stop this driver on ControllerHandle. Support stoping any child handles + Stop this driver on ControllerHandle. Support stopping any child handles created by this driver. @param This Protocol instance pointer. @@ -301,11 +392,27 @@ PartitionDriverBindingStop ( 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 // @@ -315,6 +422,15 @@ PartitionDriverBindingStop ( This->DriverBindingHandle, ControllerHandle ); + // + // Close Parent BlockIO2 if has. + // + gBS->CloseProtocol ( + ControllerHandle, + &gEfiDiskIo2ProtocolGuid, + This->DriverBindingHandle, + ControllerHandle + ); gBS->CloseProtocol ( ControllerHandle, @@ -322,64 +438,127 @@ PartitionDriverBindingStop ( This->DriverBindingHandle, ControllerHandle ); - return EFI_SUCCESS; } AllChildrenStopped = TRUE; for (Index = 0; Index < NumberOfChildren; Index++) { - Status = gBS->OpenProtocol ( - ChildHandleBuffer[Index], - &gEfiBlockIoProtocolGuid, - (VOID **) &BlockIo, - This->DriverBindingHandle, - ControllerHandle, - EFI_OPEN_PROTOCOL_GET_PROTOCOL - ); - if (!EFI_ERROR (Status)) { - - Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (BlockIo); - + 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) { // - // All Software protocols have be freed from the handle so remove it. + // If the child handle is going to be stopped again during the re-entry + // of DriverBindingStop, just do nothing. // - BlockIo->FlushBlocks (BlockIo); + break; + } + Private->InStop = TRUE; - Status = gBS->CloseProtocol ( - ControllerHandle, - &gEfiDiskIoProtocolGuid, - This->DriverBindingHandle, - ChildHandleBuffer[Index] - ); + BlockIo->FlushBlocks (BlockIo); - Status = gBS->UninstallMultipleProtocolInterfaces ( - ChildHandleBuffer[Index], - &gEfiDevicePathProtocolGuid, - Private->DevicePath, - &gEfiBlockIoProtocolGuid, - &Private->BlockIo, - Private->EspGuid, - NULL, - NULL - ); - if (EFI_ERROR (Status)) { - gBS->OpenProtocol ( - ControllerHandle, - &gEfiDiskIoProtocolGuid, - (VOID **) &DiskIo, - This->DriverBindingHandle, - ChildHandleBuffer[Index], - EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER - ); - } else { - FreePool (Private->DevicePath); - FreePool (Private); + 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; + } } } @@ -402,7 +581,6 @@ PartitionDriverBindingStop ( not be reset. **/ -STATIC EFI_STATUS EFIAPI PartitionReset ( @@ -420,6 +598,41 @@ PartitionReset ( ); } +/** + 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 @@ -440,7 +653,6 @@ PartitionReset ( valid for the device. **/ -STATIC EFI_STATUS EFIAPI PartitionReadBlocks ( @@ -457,12 +669,12 @@ PartitionReadBlocks ( Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (This); if (BufferSize % Private->BlockSize != 0) { - return EFI_BAD_BUFFER_SIZE; + return ProbeMediaStatus (Private->DiskIo, MediaId, EFI_BAD_BUFFER_SIZE); } Offset = MultU64x32 (Lba, Private->BlockSize) + Private->Start; if (Offset + BufferSize > Private->End) { - return EFI_INVALID_PARAMETER; + return ProbeMediaStatus (Private->DiskIo, MediaId, EFI_INVALID_PARAMETER); } // // Because some kinds of partition have different block size from their parent @@ -476,11 +688,11 @@ PartitionReadBlocks ( Write 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 + @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. @@ -492,7 +704,6 @@ PartitionReadBlocks ( valid for the device. **/ -STATIC EFI_STATUS EFIAPI PartitionWriteBlocks ( @@ -500,7 +711,7 @@ PartitionWriteBlocks ( IN UINT32 MediaId, IN EFI_LBA Lba, IN UINTN BufferSize, - OUT VOID *Buffer + IN VOID *Buffer ) { PARTITION_PRIVATE_DATA *Private; @@ -509,12 +720,12 @@ PartitionWriteBlocks ( Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (This); if (BufferSize % Private->BlockSize != 0) { - return EFI_BAD_BUFFER_SIZE; + return ProbeMediaStatus (Private->DiskIo, MediaId, EFI_BAD_BUFFER_SIZE); } Offset = MultU64x32 (Lba, Private->BlockSize) + Private->Start; if (Offset + BufferSize > Private->End) { - return EFI_INVALID_PARAMETER; + return ProbeMediaStatus (Private->DiskIo, MediaId, EFI_INVALID_PARAMETER); } // // Because some kinds of partition have different block size from their parent @@ -535,7 +746,6 @@ PartitionWriteBlocks ( @retval EFI_NO_MEDIA There is no media in the device. **/ -STATIC EFI_STATUS EFIAPI PartitionFlushBlocks ( @@ -549,6 +759,336 @@ PartitionFlushBlocks ( 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; +} /** @@ -556,19 +1096,21 @@ PartitionFlushBlocks ( bytes Start to End of the Parent Block IO device. @param[in] This Protocol instance pointer. - @param[in] This Calling context. - @param[in] ParentHandle Parent Handle for new child - @param[in] ParentDiskIo Parent DiskIo interface - @param[in] ParentBlockIo Parent BlockIo interface - @param[in] ParentDevicePath Parent Device Path - @param[in] DevicePathNode Child Device Path node - @param[in] Start Start Block - @param[in] End End Block - @param[in] BlockSize Child block size - @param[in] InstallEspGuid Flag to install EFI System Partition GUID on handle - - @retval EFI_SUCCESS A child handle was added - @retval other A child handle was not added + @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 @@ -576,18 +1118,22 @@ 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 BOOLEAN InstallEspGuid + 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; @@ -600,57 +1146,120 @@ PartitionInstallChildHandle ( Private->BlockSize = BlockSize; Private->ParentBlockIo = ParentBlockIo; + Private->ParentBlockIo2 = ParentBlockIo2; Private->DiskIo = ParentDiskIo; + Private->DiskIo2 = ParentDiskIo2; - Private->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION; + // + // 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 = DivU64x32 ( MultU64x32 ( End - Start + 1, ParentBlockIo->Media->BlockSize ), - BlockSize + BlockSize ) - 1; - Private->Media.BlockSize = (UINT32) BlockSize; + Private->Media.BlockSize = (UINT32) BlockSize; - Private->BlockIo.Reset = PartitionReset; - Private->BlockIo.ReadBlocks = PartitionReadBlocks; - Private->BlockIo.WriteBlocks = PartitionWriteBlocks; - Private->BlockIo.FlushBlocks = PartitionFlushBlocks; + Private->Media2.IoAlign = 0; + Private->Media2.LogicalPartition = TRUE; + Private->Media2.LastBlock = Private->Media.LastBlock; + Private->Media2.BlockSize = (UINT32) BlockSize; - Private->DevicePath = AppendDevicePathNode (ParentDevicePath, DevicePathNode); + // + // 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; } - if (InstallEspGuid) { - Private->EspGuid = &gEfiPartTypeSystemPartGuid; + // + // Set the PartitionInfo into Private Data. + // + CopyMem (&Private->PartitionInfo, PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL)); + + if (TypeGuid != NULL) { + CopyGuid(&(Private->TypeGuid), TypeGuid); } else { - // - // If NULL InstallMultipleProtocolInterfaces will ignore it. - // - Private->EspGuid = NULL; + ZeroMem ((VOID *)&(Private->TypeGuid), sizeof (EFI_GUID)); } + // - // Create the new handle + // Create the new handle. // Private->Handle = NULL; - Status = gBS->InstallMultipleProtocolInterfaces ( - &Private->Handle, - &gEfiDevicePathProtocolGuid, - Private->DevicePath, - &gEfiBlockIoProtocolGuid, - &Private->BlockIo, - Private->EspGuid, - NULL, - 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)) { // @@ -676,9 +1285,9 @@ PartitionInstallChildHandle ( /** 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] 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. @@ -709,3 +1318,41 @@ InitializePartition ( 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); +} +