diff options
Diffstat (limited to 'MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c')
| -rw-r--r-- | MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c | 2762 |
1 files changed, 1381 insertions, 1381 deletions
diff --git a/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c b/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c index bc89559727..7e81c9ccbe 100644 --- a/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c +++ b/MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c @@ -1,1381 +1,1381 @@ -/** @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.<BR>
-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);
-}
+/** @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.<BR> +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); +} |
