/*++ | |
Copyright (c) 2006, Intel Corporation | |
All rights reserved. This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
Module Name: | |
Partition.c | |
Abstract: | |
Partition driver that produces logical BlockIo devices from a physical | |
BlockIo device. The logical BlockIo devices are based on the format | |
of the raw block devices media. Currently "El Torito CD-ROM", Legacy | |
MBR, and GPT partition schemes are supported. | |
--*/ | |
#include "Partition.h" | |
// | |
// Function Prototypes | |
// | |
EFI_STATUS | |
EFIAPI | |
PartitionEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
); | |
EFI_STATUS | |
EFIAPI | |
PartitionDriverBindingSupported ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE ControllerHandle, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
); | |
EFI_STATUS | |
EFIAPI | |
PartitionDriverBindingStart ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE ControllerHandle, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
); | |
EFI_STATUS | |
EFIAPI | |
PartitionDriverBindingStop ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE ControllerHandle, | |
IN UINTN NumberOfChildren, | |
IN EFI_HANDLE *ChildHandleBuffer | |
); | |
// | |
// Partition Driver Global Variables | |
// | |
EFI_DRIVER_BINDING_PROTOCOL gPartitionDriverBinding = { | |
PartitionDriverBindingSupported, | |
PartitionDriverBindingStart, | |
PartitionDriverBindingStop, | |
0x10, | |
NULL, | |
NULL | |
}; | |
EFI_STATUS | |
EFIAPI | |
PartitionDriverBindingSupported ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE ControllerHandle, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
/*++ | |
Routine Description: | |
Test to see if this driver supports ControllerHandle. Any ControllerHandle | |
than contains a BlockIo and DiskIo protocol can be supported. | |
Arguments: | |
This - Protocol instance pointer. | |
ControllerHandle - Handle of device to test | |
RemainingDevicePath - Not used | |
Returns: | |
EFI_SUCCESS - This driver supports this device | |
EFI_ALREADY_STARTED - This driver is already running on this device | |
EFI_UNSUPPORTED - This driver does not support this device | |
--*/ | |
{ | |
EFI_STATUS Status; | |
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; | |
EFI_DISK_IO_PROTOCOL *DiskIo; | |
EFI_DEV_PATH *Node; | |
if (RemainingDevicePath != NULL) { | |
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, | |
&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 the I/O Abstraction(s) used to perform the supported test | |
// | |
gBS->CloseProtocol ( | |
ControllerHandle, | |
&gEfiDevicePathProtocolGuid, | |
This->DriverBindingHandle, | |
ControllerHandle | |
); | |
// | |
// 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 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; | |
} | |
EFI_STATUS | |
EFIAPI | |
PartitionDriverBindingStart ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE ControllerHandle, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
/*++ | |
Routine Description: | |
Start this driver on ControllerHandle by opening a Block IO and Disk IO | |
protocol, reading Device Path, and creating a child handle with a | |
Disk IO and device path protocol. | |
Arguments: | |
This - Protocol instance pointer. | |
ControllerHandle - Handle of device to bind driver to | |
RemainingDevicePath - Not used | |
Returns: | |
EFI_SUCCESS - This driver is added to DeviceHandle | |
EFI_ALREADY_STARTED - This driver is already running on DeviceHandle | |
other - This driver does not support this device | |
--*/ | |
{ | |
EFI_STATUS Status; | |
EFI_STATUS OpenStatus; | |
EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
EFI_DISK_IO_PROTOCOL *DiskIo; | |
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; | |
Status = gBS->OpenProtocol ( | |
ControllerHandle, | |
&gEfiBlockIoProtocolGuid, | |
(VOID **) &BlockIo, | |
This->DriverBindingHandle, | |
ControllerHandle, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// 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) { | |
return Status; | |
} | |
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 | |
); | |
return Status; | |
} | |
OpenStatus = Status; | |
// | |
// If no media is present, do nothing here. | |
// | |
Status = EFI_UNSUPPORTED; | |
if (BlockIo->Media->MediaPresent) { | |
// | |
// Try for GPT, then El Torito, and then legacy MBR partition types. If the | |
// media supports a given partition type install child handles to represent | |
// the partitions described by the media. | |
// | |
if (PartitionInstallGptChildHandles ( | |
This, | |
ControllerHandle, | |
DiskIo, | |
BlockIo, | |
ParentDevicePath | |
) || | |
PartitionInstallElToritoChildHandles ( | |
This, | |
ControllerHandle, | |
DiskIo, | |
BlockIo, | |
ParentDevicePath | |
) || | |
PartitionInstallMbrChildHandles ( | |
This, | |
ControllerHandle, | |
DiskIo, | |
BlockIo, | |
ParentDevicePath | |
)) { | |
Status = EFI_SUCCESS; | |
} else { | |
Status = EFI_NOT_FOUND; | |
} | |
} | |
// | |
// 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. | |
// | |
if (EFI_ERROR (Status) && !EFI_ERROR (OpenStatus)) { | |
gBS->CloseProtocol ( | |
ControllerHandle, | |
&gEfiDiskIoProtocolGuid, | |
This->DriverBindingHandle, | |
ControllerHandle | |
); | |
gBS->CloseProtocol ( | |
ControllerHandle, | |
&gEfiDevicePathProtocolGuid, | |
This->DriverBindingHandle, | |
ControllerHandle | |
); | |
} | |
return Status; | |
} | |
EFI_STATUS | |
EFIAPI | |
PartitionDriverBindingStop ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE ControllerHandle, | |
IN UINTN NumberOfChildren, | |
IN EFI_HANDLE *ChildHandleBuffer | |
) | |
/*++ | |
Routine Description: | |
Stop this driver on ControllerHandle. Support stoping any child handles | |
created by this driver. | |
Arguments: | |
This - Protocol instance pointer. | |
ControllerHandle - Handle of device to stop driver on | |
NumberOfChildren - Number of Children in the ChildHandleBuffer | |
ChildHandleBuffer - List of handles for the children we need to stop. | |
Returns: | |
EFI_SUCCESS - This driver is removed DeviceHandle | |
EFI_DEVICE_ERROR - This driver was not removed from this device | |
--*/ | |
{ | |
EFI_STATUS Status; | |
UINTN Index; | |
EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
BOOLEAN AllChildrenStopped; | |
PARTITION_PRIVATE_DATA *Private; | |
EFI_DISK_IO_PROTOCOL *DiskIo; | |
if (NumberOfChildren == 0) { | |
// | |
// Close the bus driver | |
// | |
gBS->CloseProtocol ( | |
ControllerHandle, | |
&gEfiDiskIoProtocolGuid, | |
This->DriverBindingHandle, | |
ControllerHandle | |
); | |
gBS->CloseProtocol ( | |
ControllerHandle, | |
&gEfiDevicePathProtocolGuid, | |
This->DriverBindingHandle, | |
ControllerHandle | |
); | |
return EFI_SUCCESS; | |
} | |
AllChildrenStopped = TRUE; | |
for (Index = 0; Index < NumberOfChildren; Index++) { | |
Status = gBS->OpenProtocol ( | |
ChildHandleBuffer[Index], | |
&gEfiBlockIoProtocolGuid, | |
(VOID **) &BlockIo, | |
This->DriverBindingHandle, | |
ControllerHandle, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (!EFI_ERROR (Status)) { | |
Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (BlockIo); | |
// | |
// All Software protocols have be freed from the handle so remove it. | |
// | |
BlockIo->FlushBlocks (BlockIo); | |
Status = gBS->CloseProtocol ( | |
ControllerHandle, | |
&gEfiDiskIoProtocolGuid, | |
This->DriverBindingHandle, | |
ChildHandleBuffer[Index] | |
); | |
Status = gBS->UninstallMultipleProtocolInterfaces ( | |
ChildHandleBuffer[Index], | |
&gEfiDevicePathProtocolGuid, | |
Private->DevicePath, | |
&gEfiBlockIoProtocolGuid, | |
&Private->BlockIo, | |
Private->EspGuid, | |
NULL, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
gBS->OpenProtocol ( | |
ControllerHandle, | |
&gEfiDiskIoProtocolGuid, | |
(VOID **) &DiskIo, | |
This->DriverBindingHandle, | |
ChildHandleBuffer[Index], | |
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER | |
); | |
} else { | |
gBS->FreePool (Private->DevicePath); | |
gBS->FreePool (Private); | |
} | |
} | |
if (EFI_ERROR (Status)) { | |
AllChildrenStopped = FALSE; | |
} | |
} | |
if (!AllChildrenStopped) { | |
return EFI_DEVICE_ERROR; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
PartitionReset ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN BOOLEAN ExtendedVerification | |
) | |
/*++ | |
Routine Description: | |
Reset the parent Block Device. | |
Arguments: | |
This - Protocol instance pointer. | |
ExtendedVerification - Driver may perform diagnostics on reset. | |
Returns: | |
EFI_SUCCESS - The device was reset. | |
EFI_DEVICE_ERROR - The device is not functioning properly and could | |
not be reset. | |
--*/ | |
{ | |
PARTITION_PRIVATE_DATA *Private; | |
Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (This); | |
return Private->ParentBlockIo->Reset ( | |
Private->ParentBlockIo, | |
ExtendedVerification | |
); | |
} | |
EFI_STATUS | |
EFIAPI | |
PartitionReadBlocks ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN UINT32 MediaId, | |
IN EFI_LBA Lba, | |
IN UINTN BufferSize, | |
OUT VOID *Buffer | |
) | |
/*++ | |
Routine Description: | |
Read by using the Disk IO protocol on the parent device. Lba addresses | |
must be converted to byte offsets. | |
Arguments: | |
This - Protocol instance pointer. | |
MediaId - Id of the media, changes every time the media is replaced. | |
Lba - The starting Logical Block Address to read from | |
BufferSize - Size of Buffer, must be a multiple of device block size. | |
Buffer - Buffer containing read data | |
Returns: | |
EFI_SUCCESS - The data was read correctly from the device. | |
EFI_DEVICE_ERROR - The device reported an error while performing the read. | |
EFI_NO_MEDIA - There is no media in the device. | |
EFI_MEDIA_CHANGED - The MediaId does not matched the current device. | |
EFI_BAD_BUFFER_SIZE - The Buffer was not a multiple of the block size of the | |
device. | |
EFI_INVALID_PARAMETER - The read request contains device addresses that are not | |
valid for the device. | |
--*/ | |
{ | |
PARTITION_PRIVATE_DATA *Private; | |
UINT64 Offset; | |
Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (This); | |
if (BufferSize % Private->BlockSize != 0) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
Offset = MultU64x32 (Lba, Private->BlockSize) + Private->Start; | |
if (Offset + BufferSize > Private->End) { | |
return 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); | |
} | |
EFI_STATUS | |
EFIAPI | |
PartitionWriteBlocks ( | |
IN EFI_BLOCK_IO_PROTOCOL *This, | |
IN UINT32 MediaId, | |
IN EFI_LBA Lba, | |
IN UINTN BufferSize, | |
OUT VOID *Buffer | |
) | |
/*++ | |
Routine Description: | |
Write by using the Disk IO protocol on the parent device. Lba addresses | |
must be converted to byte offsets. | |
Arguments: | |
This - Protocol instance pointer. | |
MediaId - Id of the media, changes every time the media is replaced. | |
Lba - The starting Logical Block Address to read from | |
BufferSize - Size of Buffer, must be a multiple of device block size. | |
Buffer - Buffer containing read data | |
Returns: | |
EFI_SUCCESS - The data was written correctly to the device. | |
EFI_WRITE_PROTECTED - The device can not be written to. | |
EFI_DEVICE_ERROR - The device reported an error while performing the write. | |
EFI_NO_MEDIA - There is no media in the device. | |
EFI_MEDIA_CHNAGED - The MediaId does not matched the current device. | |
EFI_BAD_BUFFER_SIZE - The Buffer was not a multiple of the block size of the | |
device. | |
EFI_INVALID_PARAMETER - The write request contains a LBA that is not | |
valid for the device. | |
--*/ | |
{ | |
PARTITION_PRIVATE_DATA *Private; | |
UINT64 Offset; | |
Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (This); | |
if (BufferSize % Private->BlockSize != 0) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
Offset = MultU64x32 (Lba, Private->BlockSize) + Private->Start; | |
if (Offset + BufferSize > Private->End) { | |
return 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); | |
} | |
EFI_STATUS | |
EFIAPI | |
PartitionFlushBlocks ( | |
IN EFI_BLOCK_IO_PROTOCOL *This | |
) | |
/*++ | |
Routine Description: | |
Flush the parent Block Device. | |
Arguments: | |
This - Protocol instance pointer. | |
Returns: | |
EFI_SUCCESS - All outstanding data was written to the device | |
EFI_DEVICE_ERROR - The device reported an error while writing back the data | |
EFI_NO_MEDIA - There is no media in the device. | |
--*/ | |
{ | |
PARTITION_PRIVATE_DATA *Private; | |
Private = PARTITION_DEVICE_FROM_BLOCK_IO_THIS (This); | |
return Private->ParentBlockIo->FlushBlocks (Private->ParentBlockIo); | |
} | |
EFI_STATUS | |
PartitionInstallChildHandle ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE ParentHandle, | |
IN EFI_DISK_IO_PROTOCOL *ParentDiskIo, | |
IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIo, | |
IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, | |
IN EFI_DEVICE_PATH_PROTOCOL *DevicePathNode, | |
IN EFI_LBA Start, | |
IN EFI_LBA End, | |
IN UINT32 BlockSize, | |
IN BOOLEAN InstallEspGuid | |
) | |
/*++ | |
Routine Description: | |
Create a child handle for a logical block device that represents the | |
bytes Start to End of the Parent Block IO device. | |
Arguments: | |
This - Calling context. | |
ParentHandle - Parent Handle for new child | |
ParentDiskIo - Parent DiskIo interface | |
ParentBlockIo - Parent BlockIo interface | |
ParentDevicePath - Parent Device Path | |
DevicePathNode - Child Device Path node | |
Start - Start Block | |
End - End Block | |
BlockSize - Child block size | |
InstallEspGuid - Flag to install EFI System Partition GUID on handle | |
Returns: | |
EFI_SUCCESS - If a child handle was added | |
EFI_OUT_OF_RESOURCES - A child handle was not added | |
--*/ | |
{ | |
EFI_STATUS Status; | |
PARTITION_PRIVATE_DATA *Private; | |
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->DiskIo = ParentDiskIo; | |
Private->BlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION; | |
Private->BlockIo.Media = &Private->Media; | |
CopyMem (Private->BlockIo.Media, ParentBlockIo->Media, sizeof (EFI_BLOCK_IO_MEDIA)); | |
Private->Media.LogicalPartition = TRUE; | |
Private->Media.LastBlock = DivU64x32 ( | |
MultU64x32 ( | |
End - Start + 1, | |
ParentBlockIo->Media->BlockSize | |
), | |
BlockSize | |
) - 1; | |
Private->Media.BlockSize = (UINT32) BlockSize; | |
Private->BlockIo.Reset = PartitionReset; | |
Private->BlockIo.ReadBlocks = PartitionReadBlocks; | |
Private->BlockIo.WriteBlocks = PartitionWriteBlocks; | |
Private->BlockIo.FlushBlocks = PartitionFlushBlocks; | |
Private->DevicePath = AppendDevicePathNode (ParentDevicePath, DevicePathNode); | |
if (Private->DevicePath == NULL) { | |
gBS->FreePool (Private); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
if (InstallEspGuid) { | |
Private->EspGuid = &gEfiPartTypeSystemPartGuid; | |
} else { | |
// | |
// If NULL InstallMultipleProtocolInterfaces will ignore it. | |
// | |
Private->EspGuid = NULL; | |
} | |
// | |
// Create the new handle | |
// | |
Private->Handle = NULL; | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&Private->Handle, | |
&gEfiDevicePathProtocolGuid, | |
Private->DevicePath, | |
&gEfiBlockIoProtocolGuid, | |
&Private->BlockIo, | |
Private->EspGuid, | |
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 { | |
gBS->FreePool (Private->DevicePath); | |
gBS->FreePool (Private); | |
} | |
return Status; | |
} |