/** @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 ((DEBUG_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 ((DEBUG_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, ParentBlockIo->Media->BlockSize); Private->End = MultU64x32 (End + 1, ParentBlockIo->Media->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 = DivU64x32 ( MultU64x32 ( End - Start + 1, ParentBlockIo->Media->BlockSize ), BlockSize ) - 1; 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); }