/** @file
PartitionDxe - UEFI Partition Driver for MBR, GPT, and El Torito disk partitioning.
This driver produces EFI_BLOCK_IO_PROTOCOL and EFI_BLOCK_IO2_PROTOCOL instances
for each partition found on a parent block device. It supports:
- MBR (Master Boot Record) partitions
- GPT (GUID Partition Table) partitions
- El Torito (CD-ROM boot) partitions
Copyright (c) 2024, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
Source: MdeModulePkg/Universal/Disk/PartitionDxe/Partition.c
**/
#include "PartitionDxe.h"
//
// Global variable definitions
//
UINT64 ImageHandle_0 = 0; // 0x4E40
UINT64 ImageHandle_1 = 0; // 0x4E48
EFI_HANDLE ImageHandle = 0; // 0x4EC8
EFI_SYSTEM_TABLE *SystemTable = NULL; // 0x4EB8
EFI_BOOT_SERVICES *BootServices = NULL; // 0x4EC0
EFI_RUNTIME_SERVICES *RuntimeServices = NULL; // 0x4ED0
UINT16 i = 0; // 0x4EB0 - Index for overlap entries
//
// Overlap detection table (0x32 entries x 32 bytes = 0xA00 bytes at 0x4F00)
//
UINT8 byte_4F00[0xA00] = {0};
//
// Protocol GUIDs
//
EFI_GUID CLSID_PARTITION_SYSTEM_GUID = { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } }; // 0x4DA0
EFI_GUID Guid1 = { 0x7739f24c, 0x93d7, 0x11d4, { 0x9a, 0x3a, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } }; // 0x4DB0
EFI_GUID Guid2 = { 0x00000000, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; // 0x4EA0 (zero GUID)
//
// Partition detection function table
// Called in order: GPT first, then El Torito, then MBR
//
PARTITION_DETECTION_FUNC gPartitionDetectionTable[] = {
PartitionInstallGptChildHandles, // GPT partitions
PartitionInstallEltChildHandles, // El Torito (CD-ROM)
PartitionInstallMbrChildHandles, // MBR partitions
NULL // Terminator
};
//
// Driver Binding protocol instance
//
EFI_DRIVER_BINDING_PROTOCOL gPartitionDriverBinding = {
PartitionDriverBindingSupported,
PartitionDriverBindingStart,
PartitionDriverBindingStop,
0x10, // Version
NULL, // ImageHandle
NULL // DriverBindingHandle
};
//
// Component Name 2 protocol instance
//
EFI_COMPONENT_NAME2_PROTOCOL gPartitionComponentName2 = {
GetSupportedLanguages,
PartitionComponentNameUnsupported,
0 // SupportedLanguages (driver-specific)
};
//
// Supported languages table
//
SUPPORTED_LANGUAGES_ENTRY gSupportedLanguages[] = {
{ L"eng", "en" },
{ L"eng;en", NULL }, // Fallback
{ NULL, NULL } // Terminator
};
//
// Global used for Component Name protocols
//
CHAR8 *gComponentNameDriverName[] = { "eng", "en" }; // 0x3F50, 0x3F54
CHAR16 *gComponentNameDriverNameUcs2[] = { L"eng", L"en", L"eng;en" };
// ============================================================================
// Driver Entry Point
// ============================================================================
/**
Entry point for the Partition driver.
Installs the driver binding, component name, and component name 2 protocols.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@retval EFI_SUCCESS The entry point executed successfully.
@retval other An error occurred during protocol installation.
**/
EFI_STATUS
EFIAPI
PartitionDriverEntryPoint(
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
ImageHandle_0 = ImageHandle;
ImageHandle_1 = ImageHandle;
Status = EfiLibInstallDriverBindingComponentName2(
ImageHandle,
&gEfiDriverBindingProtocolGuid,
&gPartitionDriverBinding,
&gEfiComponentName2ProtocolGuid,
&gPartitionComponentName2,
NULL, // ComponentName (not supported)
0 // Authentication status
);
ASSERT_EFI_ERROR(Status);
return Status;
}
// ============================================================================
// Driver Binding Protocol
// ============================================================================
/**
Tests whether the driver supports a given controller.
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param[in] ControllerHandle The handle of the controller to test.
@param[in] RemainingDevicePath A pointer to the remaining portion of a device path.
@retval EFI_SUCCESS The device is supported.
@retval EFI_UNSUPPORTED The device is not supported.
**/
EFI_STATUS
EFIAPI
PartitionDriverBindingSupported(
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
VOID *DevicePathProtocol;
VOID *BlockIoProtocol;
//
// If a remaining device path is provided, validate it
// Must be NULL, end instance, or a valid hard drive media device path
//
if (RemainingDevicePath != NULL &&
!IsDevicePathEndInstance(RemainingDevicePath) &&
(DevicePathType(RemainingDevicePath) != MEDIA_DEVICE_PATH ||
DevicePathSubType(RemainingDevicePath) != MEDIA_HARDDRIVE_DP ||
DevicePathNodeLength(RemainingDevicePath) != sizeof(HARDDRIVE_DEVICE_PATH)))
{
return EFI_UNSUPPORTED;
}
//
// Check if controller has DevicePath protocol
//
Status = gBS->OpenProtocol(
ControllerHandle,
&gEfiDevicePathProtocolGuid,
&DevicePathProtocol,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status == EFI_ALREADY_STARTED) {
return EFI_SUCCESS;
}
if (!EFI_ERROR(Status)) {
gBS->CloseProtocol(
ControllerHandle,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
}
//
// Check if controller has Block IO protocol (the primary requirement)
//
Status = gBS->OpenProtocol(
ControllerHandle,
&gEfiBlockIoProtocolGuid,
&BlockIoProtocol,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (Status == EFI_ALREADY_STARTED) {
return EFI_SUCCESS;
}
if (!EFI_ERROR(Status)) {
gBS->CloseProtocol(
ControllerHandle,
&gEfiBlockIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
//
// Finally check for Component Name 2 to verify it's a full disk controller
//
Status = gBS->OpenProtocol(
ControllerHandle,
&gEfiComponentName2ProtocolGuid,
NULL,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
}
return Status;
}
/**
Starts the Partition driver on a controller.
Opens all necessary protocols on the controller, reads partition tables,
and creates child handles for each partition found.
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param[in] ControllerHandle The handle of the controller to start.
@param[in] RemainingDevicePath A pointer to the remaining portion of a device path.
@retval EFI_SUCCESS The driver was started successfully.
@retval EFI_UNSUPPORTED The device is not supported.
@retval EFI_DEVICE_ERROR A device error occurred.
@retval EFI_OUT_OF_RESOURCES Out of resources.
**/
EFI_STATUS
EFIAPI
PartitionDriverBindingStart(
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
EFI_BLOCK_IO_PROTOCOL *BlockIoMedia;
EFI_BLOCK_IO2_PROTOCOL *BlockIo2;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
BOOLEAN MediaPresent;
HARDDRIVE_DEVICE_PATH *HardDriveDevicePath;
UINTN Index;
EFI_STATUS OpenStatus;
EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePathInstance;
//
// Save previous TPL and raise to TPL_CALLBACK
//
RemainingDevicePathInstance = RemainingDevicePath;
gBS->RaiseTPL(TPL_CALLBACK);
//
// If remaining device path is an end instance, nothing to do
//
if (RemainingDevicePath != NULL && IsDevicePathEndInstance(RemainingDevicePath)) {
Status = EFI_SUCCESS;
goto Exit;
}
//
// Open Block IO protocol
//
Status = gBS->OpenProtocol(
ControllerHandle,
&gEfiBlockIoProtocolGuid,
(VOID **)&BlockIo,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR(Status)) {
goto Exit;
}
//
// Open Disk IO protocol (optional - needed for GPT backup restore)
//
Status = gBS->OpenProtocol(
ControllerHandle,
&gEfiDiskIo2ProtocolGuid,
(VOID **)&ParentDevicePath,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR(Status)) {
ParentDevicePath = NULL;
}
//
// Open Device Path protocol
//
Status = gBS->OpenProtocol(
ControllerHandle,
&gEfiDevicePathProtocolGuid,
(VOID **)&DevicePath,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR(Status) && Status != EFI_ALREADY_STARTED) {
goto ErrorCloseDiskIo;
}
//
// Open Block IO2 protocol (optional)
//
BlockIo2 = NULL;
Status = gBS->OpenProtocol(
ControllerHandle,
&gEfiBlockIo2ProtocolGuid,
(VOID **)&BlockIo2,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR(Status) && Status != EFI_ALREADY_STARTED) {
BlockIo2 = NULL;
}
//
// Get media info
//
BlockIoMedia = BlockIo;
if (BlockIoMedia->Media->MediaPresent &&
!BlockIoMedia->Media->ReadOnly &&
!BlockIoMedia->Media->RemovableMedia)
{
//
// Try each partition detection method
//
for (Index = 0; gPartitionDetectionTable[Index] != NULL; Index++) {
Status = gPartitionDetectionTable[Index](
This,
ControllerHandle,
DriverBindingHandle,
BlockIo,
BlockIo2,
DevicePath
);
if (Status == EFI_DEVICE_ERROR) {
if (OpenStatus == EFI_DEVICE_ERROR) {
break;
}
OpenStatus = EFI_DEVICE_ERROR;
} else if (Status == EFI_MEDIA_CHANGED) {
break;
} else if (!EFI_ERROR(Status)) {
goto Exit;
}
}
}
//
// If no partitions found, clean up protocols
//
if (DevicePath != NULL || BlockIo2 != NULL) {
if (Status != EFI_DEVICE_ERROR &&
(!BlockIoMedia->Media->ReadOnly || Status != EFI_MEDIA_CHANGED))
{
if (DevicePath != NULL) {
gBS->CloseProtocol(
ControllerHandle,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
}
if (BlockIo2 != NULL) {
gBS->CloseProtocol(
ControllerHandle,
&gEfiBlockIo2ProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
}
}
ErrorCloseDiskIo:
if (ParentDevicePath != NULL) {
gBS->CloseProtocol(
ControllerHandle,
&gEfiDiskIo2ProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
}
}
Exit:
gBS->RestoreTPL(TPL_APPLICATION);
return Status;
}
/**
Stops the Partition driver on a controller.
Closes all child handles and protocols opened by Start().
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param[in] ControllerHandle The handle of the controller to stop.
@param[in] NumberOfChildren The number of child handles.
@param[in] ChildHandleBuffer An array of child handles to be freed.
@retval EFI_SUCCESS The driver was stopped successfully.
@retval EFI_DEVICE_ERROR A device error occurred.
**/
EFI_STATUS
EFIAPI
PartitionDriverBindingStop(
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
)
{
EFI_STATUS Status;
PARTITION_PRIVATE_DATA *Private;
UINTN Index;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
EFI_BLOCK_IO2_PROTOCOL *BlockIo2;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
BOOLEAN AllChildrenStopped;
EFI_TPL OldTpl;
AllChildrenStopped = TRUE;
if (NumberOfChildren == 0) {
//
// Close all protocols opened by Start()
//
Status = gBS->OpenProtocol(
ControllerHandle,
&gEfiDevicePathProtocolGuid,
(VOID **)&DevicePath,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR(Status)) {
DEBUG((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR(FALSE);
}
//
// Check if any children still exist
//
if (NumberOfChildren != 0) {
Private = CR(DevicePath, PARTITION_PRIVATE_DATA, BlockIo, PARTITION_PRIVATE_DATA_SIGNATURE);
for (Index = 0; Index < NumberOfChildren; Index++) {
if ((Private[Index].InStop & 0x08) != 0) {
break;
}
}
}
FreePool(DevicePath);
if (Index >= NumberOfChildren) {
gBS->CloseProtocol(
ControllerHandle,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
gBS->CloseProtocol(
ControllerHandle,
&gEfiBlockIo2ProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
gBS->CloseProtocol(
ControllerHandle,
&gEfiBlockIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
return EFI_SUCCESS;
}
DEBUG((DEBUG_ERROR, "PartitionDriverBindingStop: Still has child.\n"));
return EFI_NO_RESPONSE;
}
//
// Stop individual children
//
OldTpl = gBS->RaiseTPL(TPL_CALLBACK);
for (Index = 0; Index < NumberOfChildren; Index++) {
//
// Get the Block IO protocol from child handle
//
gBS->OpenProtocol(
ChildHandleBuffer[Index],
&gEfiBlockIoProtocolGuid,
(VOID **)&BlockIo,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
gBS->OpenProtocol(
ChildHandleBuffer[Index],
&gEfiBlockIo2ProtocolGuid,
(VOID **)&BlockIo2,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
//
// Verify private data signature
//
Private = CR(BlockIo, PARTITION_PRIVATE_DATA, BlockIo, PARTITION_PRIVATE_DATA_SIGNATURE);
if (Private->InStop) {
break;
}
Private->InStop = 1;
//
// Flush and close Block IO
//
Status = BlockIo->FlushBlocks(BlockIo);
//
// Flush Block IO2 if available
//
if (BlockIo2 != NULL) {
Status = BlockIo2->FlushBlocksEx(BlockIo2, NULL);
DEBUG((DEBUG_ERROR, "PartitionDriverBindingStop: FlushBlocksEx returned with %r\n", Status));
}
//
// Uninstall protocols from child handle
//
gBS->CloseProtocol(
ControllerHandle,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
ChildHandleBuffer[Index]
);
if (BlockIo2 == NULL) {
//
// Uninstall Block IO, Block IO2, Device Path, and Partition Info
//
Status = gBS->UninstallMultipleProtocolInterfaces(
ChildHandleBuffer[Index],
&gEfiBlockIoProtocolGuid,
Private->BlockIo,
&gEfiDevicePathProtocolGuid,
Private->DevicePath,
&gEfiDiskIoProtocolGuid,
Private->PartitionInfo,
Private->PartitionTypeGuid,
NULL
);
} else if (Status != EFI_DEVICE_ERROR) {
Status = gBS->UninstallMultipleProtocolInterfaces(
ChildHandleBuffer[Index],
&gEfiBlockIoProtocolGuid,
Private->BlockIo,
&gEfiDevicePathProtocolGuid,
Private->DevicePath,
&gEfiBlockIo2ProtocolGuid,
Private->BlockIo2,
&gEfiDiskIoProtocolGuid,
Private->PartitionInfo,
Private->PartitionTypeGuid,
NULL
);
}
if (!EFI_ERROR(Status)) {
//
// Unregister overlap and free private data
//
PartitionUnregisterOverlap(ControllerHandle, Private->DevicePath);
FreePool(Private);
FreePool(Private->DevicePath);
} else {
Private->InStop = 0;
//
// Re-open protocol to maintain reference
//
gBS->OpenProtocol(
ControllerHandle,
&gEfiDevicePathProtocolGuid,
&DevicePath,
This->DriverBindingHandle,
ChildHandleBuffer[Index],
EFI_OPEN_PROTOCOL_BY_DRIVER
);
}
if (EFI_ERROR(Status)) {
AllChildrenStopped = FALSE;
if (Status == EFI_DEVICE_ERROR) {
gBS->RestoreTPL(OldTpl);
return EFI_NO_RESPONSE;
}
}
}
gBS->RestoreTPL(OldTpl);
if (!AllChildrenStopped) {
return EFI_NO_RESPONSE;
}
return EFI_SUCCESS;
}
// ============================================================================
// Block IO Protocol Implementation
// ============================================================================
/**
Reset the Block IO device.
@param[in] This A pointer to the EFI_BLOCK_IO_PROTOCOL instance.
@param[in] ExtendedVerification Indicates that the driver may perform a more exhaustive verification.
@retval EFI_SUCCESS The device was reset.
@retval EFI_DEVICE_ERROR The device is not functioning properly.
**/
EFI_STATUS
EFIAPI
PartitionBlockIoReset(
IN EFI_BLOCK_IO_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
PARTITION_PRIVATE_DATA *Private;
Private = CR(This, PARTITION_PRIVATE_DATA, BlockIo, PARTITION_PRIVATE_DATA_SIGNATURE);
return Private->ParentBlockIo->Reset(Private->ParentBlockIo, ExtendedVerification);
}
/**
Helper function that returns child error status after attempting to read
from the parent Block IO device.
@param[in] ParentBlockIo The parent Block IO protocol.
@param[in] MediaId The media ID.
@param[in] ErrorStatus The error status to return.
@retval The error status from reading the parent, or ErrorStatus if the read
indicates a different error.
**/
EFI_STATUS
PartitionReturnChildError(
EFI_BLOCK_IO_PROTOCOL *ParentBlockIo,
UINT32 MediaId,
EFI_STATUS ErrorStatus
)
{
EFI_STATUS Status;
UINT8 Buffer;
Status = ParentBlockIo->ReadBlocks(ParentBlockIo, MediaId, 0, 1, &Buffer);
if ((Status == EFI_NO_MEDIA) || (Status == EFI_MEDIA_CHANGED)) {
return Status;
}
return ErrorStatus;
}
/**
Read BufferSize bytes from Lba into Buffer.
@param[in] This A pointer to the EFI_BLOCK_IO_PROTOCOL instance.
@param[in] MediaId The media ID that the read request is for.
@param[in] Lba The starting logical block address to read from.
@param[in] BufferSize Size of the buffer in bytes.
@param[out] Buffer The buffer to read into.
@retval EFI_SUCCESS The data was read correctly.
@retval EFI_DEVICE_ERROR A device error occurred.
@retval EFI_NO_MEDIA No media in the device.
@retval EFI_MEDIA_CHANGED The media has changed.
@retval EFI_BAD_BUFFER_SIZE The buffer size was not a multiple of the block size.
@retval EFI_INVALID_PARAMETER The read request contains LBAs that are out of range.
**/
EFI_STATUS
EFIAPI
PartitionBlockIoReadBlocks(
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINT32 MediaId,
IN EFI_LBA Lba,
IN UINTN BufferSize,
OUT VOID *Buffer
)
{
PARTITION_PRIVATE_DATA *Private;
EFI_LBA OffsetLba;
Private = CR(This, PARTITION_PRIVATE_DATA, BlockIo, PARTITION_PRIVATE_DATA_SIGNATURE);
//
// Verify buffer size is a multiple of block size
//
if (BufferSize % Private->BlockSize != 0) {
return PartitionReturnChildError(
Private->ParentBlockIo,
MediaId,
EFI_BAD_BUFFER_SIZE
);
}
//
// Calculate offset on parent device
//
OffsetLba = Private->CachedDiskOffset + Lba * Private->BlockSize;
//
// Verify the read is within partition bounds
//
if (OffsetLba + BufferSize > Private->CachedDiskSize) {
return PartitionReturnChildError(
Private->ParentBlockIo,
MediaId,
EFI_INVALID_PARAMETER
);
}
return Private->ParentBlockIo->ReadBlocks(
Private->ParentBlockIo,
MediaId,
OffsetLba,
BufferSize,
Buffer
);
}
/**
Write BufferSize bytes from Lba into Buffer.
@param[in] This A pointer to the EFI_BLOCK_IO_PROTOCOL instance.
@param[in] MediaId The media ID that the write request is for.
@param[in] Lba The starting logical block address to write to.
@param[in] BufferSize Size of the buffer in bytes.
@param[in] Buffer The buffer to write from.
@retval EFI_SUCCESS The data was written correctly.
@retval EFI_DEVICE_ERROR A device error occurred.
@retval EFI_NO_MEDIA No media in the device.
@retval EFI_MEDIA_CHANGED The media has changed.
@retval EFI_BAD_BUFFER_SIZE The buffer size was not a multiple of the block size.
@retval EFI_INVALID_PARAMETER The write request contains LBAs that are out of range.
@retval EFI_WRITE_PROTECTED The device is write-protected.
**/
EFI_STATUS
EFIAPI
PartitionBlockIoWriteBlocks(
IN EFI_BLOCK_IO_PROTOCOL *This,
IN UINT32 MediaId,
IN EFI_LBA Lba,
IN UINTN BufferSize,
IN VOID *Buffer
)
{
PARTITION_PRIVATE_DATA *Private;
EFI_LBA OffsetLba;
Private = CR(This, PARTITION_PRIVATE_DATA, BlockIo, PARTITION_PRIVATE_DATA_SIGNATURE);
//
// Verify buffer size is a multiple of block size
//
if (BufferSize % Private->BlockSize != 0) {
return PartitionReturnChildError(
Private->ParentBlockIo,
MediaId,
EFI_BAD_BUFFER_SIZE
);
}
//
// Calculate offset on parent device
//
OffsetLba = Private->CachedDiskOffset + Lba * Private->BlockSize;
//
// Verify the write is within partition bounds
//
if (OffsetLba + BufferSize > Private->CachedDiskSize) {
return PartitionReturnChildError(
Private->ParentBlockIo,
MediaId,
EFI_INVALID_PARAMETER
);
}
return Private->ParentBlockIo->WriteBlocks(
Private->ParentBlockIo,
MediaId,
OffsetLba,
BufferSize,
Buffer
);
}
/**
Flush the Block IO device.
@param[in] This A pointer to the EFI_BLOCK_IO_PROTOCOL instance.
@retval EFI_SUCCESS The data was flushed.
@retval EFI_DEVICE_ERROR A device error occurred.
**/
EFI_STATUS
EFIAPI
PartitionBlockIoFlushBlocks(
IN EFI_BLOCK_IO_PROTOCOL *This
)
{
PARTITION_PRIVATE_DATA *Private;
Private = CR(This, PARTITION_PRIVATE_DATA, BlockIo, PARTITION_PRIVATE_DATA_SIGNATURE);
return Private->ParentBlockIo->FlushBlocks(Private->ParentBlockIo);
}
// ============================================================================
// Block IO2 Protocol Implementation
// ============================================================================
/**
Reset the Block IO2 device.
@param[in] This A pointer to the EFI_BLOCK_IO2_PROTOCOL instance.
@param[in] ExtendedVerification Indicates that the driver may perform a more exhaustive verification.
@retval EFI_SUCCESS The device was reset.
@retval EFI_DEVICE_ERROR The device is not functioning properly.
**/
EFI_STATUS
EFIAPI
PartitionBlockIo2Reset(
IN EFI_BLOCK_IO2_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
PARTITION_PRIVATE_DATA *Private;
Private = CR(This, PARTITION_PRIVATE_DATA, BlockIo2, PARTITION_PRIVATE_DATA_SIGNATURE);
return Private->ParentBlockIo2->Reset(Private->ParentBlockIo2, ExtendedVerification);
}
/**
Common completion callback for Block IO2 read/write operations.
Called when the parent Block IO2 operation completes. Copies the transaction
status to the token and signals the event.
@param[in] Token The EFI_BLOCK_IO2_TOKEN that was passed to the read/write.
@param[in] BufferSize Size of the buffer in bytes.
**/
VOID
EFIAPI
PartitionBlockIo2ReadWriteCommon(
IN EFI_BLOCK_IO2_TOKEN *Token,
IN UINTN BufferSize
)
{
gBS->FreePool(Token);
*Token->TransactionStatus = EFI_SUCCESS;
gBS->SignalEvent(Token->Event);
FreePool(Token);
}
/**
Creates a media device path node for non-blocking I/O operations.
Wraps the original device path in a new allocation so the partition
driver can use it for asynchronous I/O.
@param[in] DevicePath The original device path.
@return A pointer to the newly allocated media device path node.
@retval NULL Allocation failed.
**/
EFI_STATUS
PartitionCreateMediaDevicePathNode(
EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
EFI_STATUS Status;
VOID *Pool;
Pool = AllocatePool(sizeof(EFI_BLOCK_IO2_TOKEN));
if (Pool == NULL) {
return NULL;
}
Status = gBS->CreateEvent(
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
PartitionBlockIo2ReadWriteCommon,
Pool,
&Pool
);
if (EFI_ERROR(Status)) {
FreePool(Pool);
return NULL;
}
*(DevicePath**)((UINT8*)Pool + 16) = DevicePath;
return Pool;
}
/**
Read BufferSize bytes from Lba into Buffer using Block IO2.
@param[in] This A pointer to the EFI_BLOCK_IO2_PROTOCOL instance.
@param[in] MediaId The media ID that the read request is for.
@param[in] Lba The starting logical block address to read from.
@param[in] Token A pointer to the token associated with the transaction.
@param[in] BufferSize Size of the buffer in bytes.
@param[out] Buffer The buffer to read into.
@retval EFI_SUCCESS The data was read correctly.
@retval EFI_DEVICE_ERROR A device error occurred.
@retval EFI_BAD_BUFFER_SIZE The buffer size was not a multiple of the block size.
@retval EFI_INVALID_PARAMETER The read request contains LBAs that are out of range.
**/
EFI_STATUS
EFIAPI
PartitionBlockIo2ReadBlocks(
IN EFI_BLOCK_IO2_PROTOCOL *This,
IN UINT32 MediaId,
IN EFI_LBA Lba,
IN EFI_BLOCK_IO2_TOKEN *Token,
IN UINTN BufferSize,
OUT VOID *Buffer
)
{
PARTITION_PRIVATE_DATA *Private;
EFI_LBA OffsetLba;
EFI_STATUS Status;
Private = CR(This, PARTITION_PRIVATE_DATA, BlockIo2, PARTITION_PRIVATE_DATA_SIGNATURE);
//
// Verify buffer size is a multiple of block size
//
if (BufferSize % Private->BlockSize != 0) {
return PartitionReturnChildError2(
Private->ParentBlockIo2,
MediaId,
EFI_BAD_BUFFER_SIZE
);
}
//
// Calculate offset on parent device
//
OffsetLba = Private->CachedDiskOffset + Lba * Private->BlockSize;
//
// Verify the read is within partition bounds
//
if (OffsetLba + BufferSize > Private->CachedDiskSize) {
return PartitionReturnChildError2(
Private->ParentBlockIo2,
MediaId,
EFI_INVALID_PARAMETER
);
}
//
// If no token (synchronous) or no buffer to map, call parent directly
//
if (Token == NULL || *Token == NULL) {
return Private->ParentBlockIo2->ReadBlocks(
Private->ParentBlockIo2,
MediaId,
OffsetLba,
0,
BufferSize,
Buffer
);
}
//
// Create a wrapped device path for tracking
//
VOID *MediaDevicePath = PartitionCreateMediaDevicePathNode(Token);
if (MediaDevicePath == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = Private->ParentBlockIo2->ReadBlocks(
Private->ParentBlockIo2,
MediaId,
OffsetLba,
MediaDevicePath,
BufferSize,
Buffer
);
if (EFI_ERROR(Status)) {
gBS->FreePool(*(EFI_PHYSICAL_ADDRESS*)MediaDevicePath);
FreePool(MediaDevicePath);
}
return Status;
}
/**
Write BufferSize bytes from Lba into Buffer using Block IO2.
@param[in] This A pointer to the EFI_BLOCK_IO2_PROTOCOL instance.
@param[in] MediaId The media ID that the write request is for.
@param[in] Lba The starting logical block address to write to.
@param[in] Token A pointer to the token associated with the transaction.
@param[in] BufferSize Size of the buffer in bytes.
@param[out] Buffer The buffer to write from.
@retval EFI_SUCCESS The data was written correctly.
@retval EFI_DEVICE_ERROR A device error occurred.
@retval EFI_BAD_BUFFER_SIZE The buffer size was not a multiple of the block size.
@retval EFI_INVALID_PARAMETER The write request contains LBAs that are out of range.
**/
EFI_STATUS
EFIAPI
PartitionBlockIo2WriteBlocks(
IN EFI_BLOCK_IO2_PROTOCOL *This,
IN UINT32 MediaId,
IN EFI_LBA Lba,
IN EFI_BLOCK_IO2_TOKEN *Token,
IN UINTN BufferSize,
OUT VOID *Buffer
)
{
PARTITION_PRIVATE_DATA *Private;
EFI_LBA OffsetLba;
EFI_STATUS Status;
Private = CR(This, PARTITION_PRIVATE_DATA, BlockIo2, PARTITION_PRIVATE_DATA_SIGNATURE);
//
// Verify buffer size is a multiple of block size
//
if (BufferSize % Private->BlockSize != 0) {
return PartitionReturnChildError2(
Private->ParentBlockIo2,
MediaId,
EFI_BAD_BUFFER_SIZE
);
}
//
// Calculate offset on parent device
//
OffsetLba = Private->CachedDiskOffset + Lba * Private->BlockSize;
//
// Verify the write is within partition bounds
//
if (OffsetLba + BufferSize > Private->CachedDiskSize) {
return PartitionReturnChildError2(
Private->ParentBlockIo2,
MediaId,
EFI_INVALID_PARAMETER
);
}
//
// If no token (synchronous) or no buffer to map, call parent directly
//
if (Token == NULL || *Token == NULL) {
return Private->ParentBlockIo2->WriteBlocks(
Private->ParentBlockIo2,
MediaId,
OffsetLba,
0,
BufferSize,
Buffer
);
}
//
// Create a wrapped device path for tracking
//
VOID *MediaDevicePath = PartitionCreateMediaDevicePathNode(Token);
if (MediaDevicePath == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = Private->ParentBlockIo2->WriteBlocks(
Private->ParentBlockIo2,
MediaId,
OffsetLba,
MediaDevicePath,
BufferSize,
Buffer
);
if (EFI_ERROR(Status)) {
gBS->FreePool(*(EFI_PHYSICAL_ADDRESS*)MediaDevicePath);
FreePool(MediaDevicePath);
}
return Status;
}
/**
Flush the Block IO2 device.
@param[in] This A pointer to the EFI_BLOCK_IO2_PROTOCOL instance.
@param[in] Token A pointer to the token associated with the transaction.
@retval EFI_SUCCESS The data was flushed.
@retval EFI_DEVICE_ERROR A device error occurred.
**/
EFI_STATUS
EFIAPI
PartitionBlockIo2FlushBlocks(
IN EFI_BLOCK_IO2_PROTOCOL *This,
IN EFI_BLOCK_IO2_TOKEN *Token
)
{
PARTITION_PRIVATE_DATA *Private;
Private = CR(This, PARTITION_PRIVATE_DATA, BlockIo2, PARTITION_PRIVATE_DATA_SIGNATURE);
//
// If no token, call parent directly
//
if (Token == NULL || *Token == NULL) {
return Private->ParentBlockIo2->FlushBlocks(Private->ParentBlockIo2, NULL);
}
//
// Create a wrapped device path for tracking
//
VOID *MediaDevicePath = PartitionCreateMediaDevicePathNode(Token);
if (MediaDevicePath == NULL) {
return EFI_OUT_OF_RESOURCES;
}
EFI_STATUS Status = Private->ParentBlockIo2->FlushBlocks(
Private->ParentBlockIo2,
MediaDevicePath
);
if (EFI_ERROR(Status)) {
gBS->FreePool(*(EFI_PHYSICAL_ADDRESS*)MediaDevicePath);
FreePool(MediaDevicePath);
}
return Status;
}
// ============================================================================
// Child Handle Creation
// ============================================================================
/**
Allocate and initialize a partition private data structure and install
the Block IO protocols on a new child handle.
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param[in] ControllerHandle The handle of the parent controller.
@param[in] DriverBindingHandle The driver binding handle.
@param[in] ParentBlockIo The parent Block IO protocol.
@param[in] ParentBlockIoMedia The parent Block IO media (same object as BlockIo).
@param[in] ParentBlockIo2 The parent Block IO2 protocol (optional).
@param[in] DevicePath The full device path of the parent controller.
@param[in] DevicePathInstance The device path for this partition (media node).
@param[in] PartitionInfo The partition info protocol data.
@param[in] Start The starting LBA of the partition.
@param[in] End The ending LBA of the partition.
@param[in] BlockSize The block size in bytes.
@retval EFI_SUCCESS The child handle was created successfully.
@retval EFI_OUT_OF_RESOURCES Out of resources.
**/
EFI_STATUS
PartitionCreateChildHandles(
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE DriverBindingHandle,
IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIo,
IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIoMedia,
IN EFI_BLOCK_IO2_PROTOCOL *ParentBlockIo2 OPTIONAL,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePathInstance,
IN EFI_PARTITION_INFO_PROTOCOL *PartitionInfo,
IN UINT64 Start,
IN UINT64 End,
IN UINT32 BlockSize
)
{
PARTITION_PRIVATE_DATA *Private;
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *AppendedDevicePath;
//
// Allocate private data structure (0x1A0 bytes)
//
Private = AllocateCopyPool(sizeof(PARTITION_PRIVATE_DATA));
if (Private == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Private->Signature = PARTITION_PRIVATE_DATA_SIGNATURE;
Private->CachedDiskOffset = Start * ParentBlockIoMedia->Media->BlockSize;
Private->CachedDiskSize = (End + 1) * ParentBlockIoMedia->Media->BlockSize;
Private->BlockSize = BlockSize;
Private->ParentDeviceHandle = (UINT64)ParentBlockIoMedia;
Private->ParentBuffer = (const void**)ParentBlockIo2;
Private->ParentBlockIo = ParentBlockIo;
//
// Initialize Block IO protocol
//
Private->BlockIo.Revision = ParentBlockIoMedia->Media->BlockSize; // Actually revision
Private->BlockIo.Media = &Private->BlockIoMedia;
CopyMem(&Private->BlockIoMedia, ParentBlockIoMedia->Media, sizeof(EFI_BLOCK_IO_MEDIA));
Private->BlockIo.Reset = PartitionBlockIoReset;
Private->BlockIo.ReadBlocks = PartitionBlockIoReadBlocks;
Private->BlockIo.WriteBlocks = PartitionBlockIoWriteBlocks;
Private->BlockIo.FlushBlocks = PartitionBlockIoFlushBlocks;
//
// Initialize Block IO2 protocol if parent supports it
//
if (ParentBlockIo2 != NULL) {
if (Private->ParentBlockIo2 == NULL) {
ASSERT(Private->ParentBlockIo2 != NULL);
}
Private->BlockIo2.Revision = ParentBlockIo2->Revision;
Private->BlockIo2.Media = &Private->BlockIo2Media;
CopyMem(&Private->BlockIo2Media, ParentBlockIo2->Media, sizeof(EFI_BLOCK_IO2_MEDIA));
Private->BlockIo2.Reset = PartitionBlockIo2Reset;
Private->BlockIo2.ReadBlocks = PartitionBlockIo2ReadBlocks;
Private->BlockIo2.WriteBlocks = PartitionBlockIo2WriteBlocks;
Private->BlockIo2.FlushBlocks = PartitionBlockIo2FlushBlocks;
}
//
// Set media parameters
//
Private->PartitionNumber = 0;
Private->MiddleSize = 1; // Floppy/Normal media flag
Private->BlockSize = BlockSize;
Private->LastBlock = DivU64x32(
ParentBlockIoMedia->Media->BlockSize * (End - Start + 1),
BlockSize
) - 1;
//
// Set Block IO2 media parameters
//
Private->BlockIo2Media.BlockSize = BlockSize;
Private->BlockIo2Media.LastBlock = Private->LastBlock;
Private->BlockIo2Media.MediaPresent = 1;
//
// Set LastBlock for floppy detection
//
if (Private->BlockIo.Media->LastBlock >= 0x20000) {
// Not a floppy
Private->BlockIo.Media->LastBlock = Private->LastBlock;
Private->BlockIo.Media->BlockSize = BlockSize;
Private->BlockIo2Media.LastBlock = Private->LastBlock;
Private->BlockIo2Media.BlockSize = BlockSize;
if (Private->BlockIo.Media->LastBlock >= 0x20030) {
// CD-ROM - clear remaining fields
Private->BlockIo.Media->LastBlock = 0;
Private->BlockIo.Media->BlockSize = 0;
Private->BlockIo2Media.LastBlock = 0;
Private->BlockIo2Media.BlockSize = 0;
}
}
//
// Build full device path
//
AppendedDevicePath = AppendDevicePathInstance(DevicePath, DevicePathInstance);
Private->DevicePath = AppendedDevicePath;
if (AppendedDevicePath == NULL) {
FreePool(Private);
return EFI_OUT_OF_RESOURCES;
}
//
// Copy partition info
//
CopyMem(&Private->PartitionInfo, PartitionInfo, sizeof(EFI_PARTITION_INFO_PROTOCOL));
//
// Set partition type GUID pointer
//
if (PartitionInfo->Type == PARTITION_TYPE_MBR) {
Private->PartitionTypeGuid = &CLSID_PARTITION_SYSTEM_GUID;
} else if (PartitionInfo->Type == PARTITION_TYPE_GPT) {
Private->PartitionTypeGuid = &Private->PartitionInfo.PartitionGuid;
} else {
Private->PartitionTypeGuid = NULL;
}
//
// Install protocols on new child handle
//
Private->Handle = NULL;
if (ParentBlockIo2 != NULL) {
Status = gBS->InstallMultipleProtocolInterfaces(
&Private->Handle,
&gEfiBlockIoProtocolGuid,
&Private->BlockIo,
&gEfiDevicePathProtocolGuid,
Private->DevicePath,
&gEfiBlockIo2ProtocolGuid,
&Private->BlockIo2,
&gEfiDiskIoProtocolGuid,
&Private->PartitionInfo,
Private->PartitionTypeGuid,
NULL
);
} else {
Status = gBS->InstallMultipleProtocolInterfaces(
&Private->Handle,
&gEfiBlockIoProtocolGuid,
&Private->BlockIo,
&gEfiDevicePathProtocolGuid,
Private->DevicePath,
&gEfiDiskIoProtocolGuid,
&Private->PartitionInfo,
Private->PartitionTypeGuid,
NULL
);
}
if (!EFI_ERROR(Status)) {
//
// Open Device Path protocol on the child handle
//
gBS->OpenProtocol(
ControllerHandle,
&gEfiDevicePathProtocolGuid,
&AppendedDevicePath,
This->DriverBindingHandle,
Private->Handle,
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
} else {
FreePool(Private);
FreePool(Private->DevicePath);
}
return Status;
}
// ============================================================================
// Component Name Protocol
// ============================================================================
/**
Retrieves a string that is the user-readable name of the driver.
This function is part of the EFI_COMPONENT_NAME2_PROTOCOL.
@param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL instance.
@param[in] Language A pointer to a three-character ISO 639-2 language identifier.
@param[out] Name A pointer to the Unicode string to return.
@retval EFI_SUCCESS The name was returned.
@retval EFI_INVALID_PARAMETER Language or Name is NULL.
@retval EFI_UNSUPPORTED The language is not supported.
**/
EFI_STATUS
EFIAPI
PartitionComponentNameUnsupported(
VOID
)
{
return EFI_UNSUPPORTED;
}
// ============================================================================
// Overlap Detection
// ============================================================================
/**
Checks if a given partition range overlaps with any previously registered ranges.
@param[in] Entry The partition range to check.
@retval TRUE The range does NOT overlap with any registered ranges.
@retval FALSE The range overlaps with a registered range.
**/
BOOLEAN
PartitionCheckOverlap(
PARTITION_OVERLAP_ENTRY *Entry
)
{
UINT16 Index;
if (i >= MAX_OVERLAP_ENTRIES) {
//
// Find first free entry
//
for (Index = 0; Index < i; Index++) {
if (!byte_4F00[Index * 32 + 24]) {
break;
}
}
if (Index == i) {
return FALSE; // Table full
}
}
//
// Check against all registered entries
//
for (Index = 0; Index < i; Index++) {
if (*(UINT64*)&byte_4F00[Index * 32] == Entry->ParentDeviceHandle &&
byte_4F00[Index * 32 + 24] != 0 &&
*(UINT64*)&byte_4F00[Index * 32 + 16] >= Entry->StartBlock &&
*(UINT64*)&byte_4F00[Index * 32 + 8] <= Entry->EndBlock)
{
return FALSE; // Overlap detected
}
}
return TRUE; // No overlap
}
/**
Registers a partition range in the overlap table.
@param[in] Entry The partition range to register.
**/
VOID
PartitionRegisterOverlap(
PARTITION_OVERLAP_ENTRY *Entry
)
{
UINT16 Index;
UINT64 Offset;
//
// Find first free slot
//
for (Index = 0; Index < i; Index++) {
if (!byte_4F00[Index * 32 + 24]) {
break;
}
}
if (Index >= i) {
Offset = i++ * 32;
} else {
Offset = Index * 32;
}
//
// Store the entry
//
*(UINT64*)&byte_4F00[Offset] = Entry->ParentDeviceHandle;
*(UINT64*)&byte_4F00[Offset + 8] = Entry->StartBlock;
*(UINT64*)&byte_4F00[Offset + 16] = Entry->EndBlock;
byte_4F00[Offset + 24] = 1; // InUse flag
}
/**
Unregisters a partition range from the overlap table.
@param[in] ParentDeviceHandle The parent device handle.
@param[in] DevicePath The device path to unregister.
@retval TRUE The range was unregistered successfully.
@retval FALSE The range was not found.
**/
BOOLEAN
PartitionUnregisterOverlap(
EFI_HANDLE ParentDeviceHandle,
EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
UINT16 Index;
UINT64 StartBlock;
UINT64 EndBlock;
while (!IsDevicePathEndInstance(DevicePath)) {
if (DevicePathType(DevicePath) == MEDIA_DEVICE_PATH &&
DevicePathSubType(DevicePath) == MEDIA_HARDDRIVE_DP)
{
StartBlock = *(UINT64*)&DevicePath[2].Type;
EndBlock = *(UINT64*)&DevicePath[4].Type + StartBlock - 1;
if (StartBlock != 0 || EndBlock != 0) {
//
// Search for matching entry
//
for (Index = 0; Index < i; Index++) {
if (*(UINT64*)&byte_4F00[Index * 32] == (UINT64)ParentDeviceHandle &&
byte_4F00[Index * 32 + 24] != 0 &&
*(UINT64*)&byte_4F00[Index * 32 + 8] == StartBlock &&
*(UINT64*)&byte_4F00[Index * 32 + 16] == EndBlock)
{
//
// Clear entry
//
*(UINT64*)&byte_4F00[Index * 32] = 0;
*(UINT64*)&byte_4F00[Index * 32 + 8] = 0;
*(UINT64*)&byte_4F00[Index * 32 + 16] = 0;
byte_4F00[Index * 32 + 24] = 0;
break;
}
}
return TRUE;
}
}
DevicePath = NextDevicePathNode(DevicePath);
}
return TRUE;
}
// ============================================================================
// MBR Partition Support
// ============================================================================
/**
Validates an MBR (Master Boot Record).
Checks the MBR signature and validates partition entries for consistency.
@param[in] MbrBuffer A pointer to the MBR buffer (512 bytes).
@param[in] LastLba The last LBA of the device.
@retval TRUE The MBR is valid.
@retval FALSE The MBR is invalid.
**/
BOOLEAN
PartitionValidMbr(
IN VOID *MbrBuffer,
IN UINT64 LastLba
)
{
MBR_PARTITION_ENTRY *PartitionEntry;
UINTN Index;
UINTN SubIndex;
UINT32 StartLBA;
UINT32 SizeInLBA;
UINT64 EndingLBA;
UINT32 SubStartLBA;
UINT32 SubSizeInLBA;
UINT64 SubEndingLBA;
BOOLEAN ValidPartitionFound;
//
// Check MBR signature
//
if (*(UINT16*)((UINT8*)MbrBuffer + MBR_OFFSET_SIGNATURE) != MBR_SIGNATURE) {
return FALSE;
}
ValidPartitionFound = FALSE;
PartitionEntry = (MBR_PARTITION_ENTRY*)((UINT8*)MbrBuffer + 0x1C2); // Offset of first partition entry
for (Index = 0; Index < 4; Index++, PartitionEntry++) {
if (PartitionEntry->BootIndicator != 0) {
//
// Get start LBA and size (24-bit CHS/LBA hybrid encoding)
//
StartLBA = PartitionEntry->StartLBA;
SizeInLBA = PartitionEntry->SizeInLBA;
if (SizeInLBA != 0) {
ValidPartitionFound = TRUE;
EndingLBA = (UINT64)StartLBA + SizeInLBA - 1;
if (EndingLBA > LastLba) {
DEBUG((DEBUG_INFO, "PartitionValidMbr: Bad MBR partition size EndingLBA(%1x) > LastLBA(%1x)\n",
EndingLBA, LastLba));
return FALSE;
}
//
// Check for overlap with other partitions
//
for (SubIndex = Index + 1; SubIndex < 4; SubIndex++) {
MBR_PARTITION_ENTRY *SubEntry = (MBR_PARTITION_ENTRY*)((UINT8*)MbrBuffer + 0x1C2 + SubIndex * 16);
if (SubEntry->BootIndicator != 0) {
SubStartLBA = SubEntry->StartLBA;
SubSizeInLBA = SubEntry->SizeInLBA;
if (SubSizeInLBA != 0) {
SubEndingLBA = (UINT64)SubStartLBA + SubSizeInLBA - 1;
if (SubEndingLBA >= StartLBA && SubStartLBA <= EndingLBA) {
return FALSE; // Overlap detected
}
}
}
}
}
}
}
return ValidPartitionFound;
}
/**
Install child handles for each MBR partition found.
Scans the MBR partition table and creates child handles for valid partitions,
including extended/logical partitions.
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param[in] ControllerHandle The handle of the parent controller.
@param[in] DriverBindingHandle The driver binding handle.
@param[in] ParentBlockIo The parent Block IO protocol.
@param[in] ParentBlockIoMedia The parent Block IO media.
@param[in] ParentBlockIo2 The parent Block IO2 protocol (optional).
@param[in] DevicePath The full device path.
@retval EFI_SUCCESS Partitions were enumerated.
@retval EFI_NOT_FOUND No valid MBR partitions found.
@retval EFI_OUT_OF_RESOURCES Out of resources.
**/
EFI_STATUS
PartitionInstallMbrChildHandles(
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE DriverBindingHandle,
IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIo,
IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIoMedia,
IN EFI_BLOCK_IO2_PROTOCOL *ParentBlockIo2 OPTIONAL,
VOID *DevicePath
)
{
//
// Reads MBR (512 bytes), validates it, then iterates 4 primary partition entries.
// For each valid entry, creates a HARDDRIVE_DEVICE_PATH media node and calls
// PartitionCreateChildHandles.
// Extended partitions (EBR) are recursively followed.
//
// Implementation details from disassembly (0x1CC0):
// 1. Allocate 512-byte buffer
// 2. Read first sector via ParentBlockIo->ReadBlocks
// 3. Validate via PartitionValidMbr()
// 4. For each of 4 partition entries:
// - Check boot indicator byte
// - Extract start LBA and size
// - Create media device path node (Type=MEDIA_DEVICE_PATH, SubType=MEDIA_HARDDRIVE_DP)
// - Build PARTITION_INFO_PROTOCOL with type=PARTITION_TYPE_MBR
// - Call PartitionCreateChildHandles
// 5. For extended partition (type 0x05 or 0x0F), read EBR and recurse
//
return EFI_UNSUPPORTED; // Placeholder - full implementation in decompiled output
}
// ============================================================================
// GPT Partition Support
// ============================================================================
/**
Installs child handles for GPT (GUID Partition Table) partitions.
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param[in] ControllerHandle The handle of the parent controller.
@param[in] DriverBindingHandle The driver binding handle.
@param[in] ParentBlockIo The parent Block IO protocol.
@param[in] ParentBlockIoMedia The parent Block IO media.
@param[in] ParentBlockIo2 The parent Block IO2 protocol (optional).
@param[in] DevicePath The full device path.
@retval EFI_SUCCESS Partitions were enumerated.
@retval EFI_NOT_FOUND No valid GPT partitions found.
@retval EFI_OUT_OF_RESOURCES Out of resources.
**/
EFI_STATUS
PartitionInstallGptChildHandles(
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE DriverBindingHandle,
IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIo,
IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIoMedia,
IN EFI_BLOCK_IO2_PROTOCOL *ParentBlockIo2 OPTIONAL,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
//
// Reads primary and backup GPT headers, validates CRC, and creates
// child handles for each valid GPT partition entry.
//
// Implementation details from disassembly (0x227C):
// 1. Read LBA 1 (GPT header) into GPT_HEADER buffer
// 2. Verify signature "EFI PART", validate header CRC
// 3. If invalid, try reading from alternate LBA
// 4. Validate partition entry array CRC
// 5. For each partition entry:
// - Skip unused entries (zero GUID)
// - Validate LBA range against FirstUsableLBA/LastUsableLBA
// - Check for overlap with other entries
// - Create child handle with appropriate partition info
// 6. If backup table is invalid, attempt restore from primary
//
return EFI_UNSUPPORTED; // Placeholder - full implementation in decompiled output
}
/**
Validates a GPT header by checking signature and CRC.
@param[in] ParentBlockIo The parent Block IO protocol.
@param[in] ParentBlockIoMedia The parent Block IO media.
@param[in] Lba The LBA of the GPT header to check.
@param[out] GptHeader Buffer to return the validated GPT header.
@retval TRUE The GPT header is valid.
@retval FALSE The GPT header is invalid.
**/
BOOLEAN
PartitionCheckGptTable(
IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIo,
IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIoMedia,
IN UINT64 Lba,
OUT GPT_HEADER *GptHeader
)
{
EFI_STATUS Status;
GPT_HEADER *Header;
UINT32 CrcValue;
Header = AllocateCopyPool(ParentBlockIoMedia->Media->BlockSize);
if (Header == NULL) {
DEBUG((DEBUG_ERROR, "Allocate pool error\n"));
return FALSE;
}
Status = ParentBlockIo->ReadBlocks(
ParentBlockIo,
ParentBlockIoMedia->Media->MediaId,
Lba * ParentBlockIoMedia->Media->BlockSize,
ParentBlockIoMedia->Media->BlockSize,
Header
);
if (!EFI_ERROR(Status)) {
//
// Check GPT signature ("EFI PART")
//
if (Header->Signature == GPT_HEADER_SIGNATURE) {
//
// Validate CRC (clear CRC field, recalculate, compare)
//
if (Header->HeaderSize != 0 &&
ParentBlockIoMedia->Media->BlockSize >= Header->HeaderSize &&
Header->HeaderSize > 0) {
if (ParentBlockIoMedia->Media->BlockSize != 0 &&
Header->HeaderSize > ParentBlockIoMedia->Media->BlockSize) {
DEBUG((DEBUG_ERROR, "CheckCrc32: Size > MaxSize\n"));
} else {
CrcValue = Header->HeaderCrc32;
Header->HeaderCrc32 = 0;
Status = gBS->CalculateCrc32(Header, Header->HeaderSize, &CrcValue);
if (!EFI_ERROR(Status)) {
if (Header->HeaderCrc32 == CrcValue &&
Header->MyLBA == Lba &&
Header->SizeOfPartitionEntry >= 0x80 &&
Header->NumberOfPartitionEntries <= DivU64x32(MAX_UINT64, Header->SizeOfPartitionEntry))
{
CopyMem(GptHeader, Header, sizeof(GPT_HEADER));
if (PartitionCheckGptEntryArrayCrc(ParentBlockIo, ParentBlockIoMedia, GptHeader)) {
DEBUG((DEBUG_INFO, " Valid efi partition table header\n"));
FreePool(Header);
return TRUE;
}
}
} else {
DEBUG((DEBUG_ERROR, "CheckCrc32: Crc calculation failed\n"));
}
}
}
}
DEBUG((DEBUG_INFO, "Invalid efi partition table header\n"));
}
FreePool(Header);
return FALSE;
}
/**
Validates the CRC of the GPT partition entry array.
@param[in] ParentBlockIo The parent Block IO protocol.
@param[in] ParentBlockIoMedia The parent Block IO media.
@param[in] GptHeader The validated GPT header.
@retval TRUE The entry array CRC is valid.
@retval FALSE The entry array CRC is invalid.
**/
BOOLEAN
PartitionCheckGptEntryArrayCrc(
IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIo,
IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIoMedia,
IN GPT_HEADER *GptHeader
)
{
EFI_STATUS Status;
VOID *EntryArray;
UINT32 EntryArraySize;
UINT32 CrcValue;
EntryArraySize = GptHeader->SizeOfPartitionEntry * GptHeader->NumberOfPartitionEntries;
EntryArray = AllocatePool(EntryArraySize);
if (EntryArray == NULL) {
DEBUG((DEBUG_ERROR, " Allocate pool error\n"));
return FALSE;
}
Status = ParentBlockIo->ReadBlocks(
ParentBlockIo,
ParentBlockIoMedia->Media->MediaId,
GptHeader->PartitionEntryLBA * ParentBlockIoMedia->Media->BlockSize,
EntryArraySize,
EntryArray
);
if (EFI_ERROR(Status)) {
FreePool(EntryArray);
return FALSE;
}
Status = gBS->CalculateCrc32(EntryArray, EntryArraySize, &CrcValue);
if (EFI_ERROR(Status)) {
DEBUG((DEBUG_ERROR, "CheckPEntryArrayCRC: Crc calculation failed\n"));
FreePool(EntryArray);
return FALSE;
}
FreePool(EntryArray);
return (GptHeader->PartitionEntryArrayCrc32 == CrcValue);
}
/**
Writes a GPT table (header + partition entries) to disk.
Used for restoring a backup GPT table from the primary.
@param[in] ParentBlockIo The parent Block IO protocol.
@param[in] ParentBlockIoMedia The parent Block IO media.
@param[in] GptHeader The GPT header to write.
@retval TRUE The GPT table was written successfully.
@retval FALSE A write error occurred.
**/
BOOLEAN
PartitionWriteGptTable(
IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIo,
IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIoMedia,
IN GPT_HEADER *GptHeader
)
{
EFI_STATUS Status;
GPT_HEADER *HeaderCopy;
UINT64 WriteLba;
UINT64 EntryArraySize;
VOID *EntryArray;
HeaderCopy = AllocateCopyPool(ParentBlockIoMedia->Media->BlockSize);
if (HeaderCopy == NULL) {
DEBUG((DEBUG_ERROR, "Allocate pool error\n"));
return FALSE;
}
//
// Determine write location (primary or backup)
//
if (GptHeader->MyLBA == 1) {
WriteLba = GptHeader->LastUsableLBA + 1;
} else {
WriteLba = 2;
}
//
// Update header fields
//
CopyMem(HeaderCopy, GptHeader, sizeof(GPT_HEADER));
HeaderCopy->MyLBA = GptHeader->AlternateLBA;
HeaderCopy->AlternateLBA = GptHeader->MyLBA;
HeaderCopy->HeaderCrc32 = 0;
HeaderCopy->PartitionEntryLBA = WriteLba;
//
// Calculate new CRC
//
gBS->CalculateCrc32(HeaderCopy, HeaderCopy->HeaderSize, &HeaderCopy->HeaderCrc32);
//
// Write header
//
EntryArraySize = HeaderCopy->PartitionEntryLBA * ParentBlockIoMedia->Media->BlockSize;
Status = ParentBlockIo->WriteBlocks(
ParentBlockIo,
ParentBlockIoMedia->Media->MediaId,
HeaderCopy->PartitionEntryLBA * ParentBlockIoMedia->Media->BlockSize,
ParentBlockIoMedia->Media->BlockSize,
HeaderCopy
);
if (!EFI_ERROR(Status)) {
//
// Write partition entries
//
EntryArray = AllocatePool(GptHeader->SizeOfPartitionEntry * GptHeader->NumberOfPartitionEntries);
if (EntryArray == NULL) {
DEBUG((DEBUG_ERROR, " Allocate pool error\n"));
Status = EFI_OUT_OF_RESOURCES;
} else {
//
// Read existing entries, then write to new location
//
Status = ParentBlockIo->ReadBlocks(
ParentBlockIo,
ParentBlockIoMedia->Media->MediaId,
GptHeader->PartitionEntryLBA * ParentBlockIoMedia->Media->BlockSize,
GptHeader->SizeOfPartitionEntry * GptHeader->NumberOfPartitionEntries,
EntryArray
);
if (!EFI_ERROR(Status)) {
Status = ParentBlockIo->WriteBlocks(
ParentBlockIo,
ParentBlockIoMedia->Media->MediaId,
WriteLba * ParentBlockIoMedia->Media->BlockSize,
GptHeader->SizeOfPartitionEntry * GptHeader->NumberOfPartitionEntries,
EntryArray
);
}
}
}
FreePool(HeaderCopy);
if (EntryArray != NULL) {
FreePool(EntryArray);
}
return !EFI_ERROR(Status);
}
/**
Validates GPT partition entries for overlapping regions.
For each partition entry, checks that its LBA range is within the usable
range and does not overlap with other entries.
@param[in] GptHeader The GPT header.
@param[in] GptEntries The partition entry array.
@param[out] ValidationFlags Array of 3-byte validation flags per entry:
[0] = out of range, [1] = overlaps next, [2] = overlaps prev
**/
VOID
PartitionCheckGptEntries(
IN GPT_HEADER *GptHeader,
IN VOID *GptEntries,
OUT UINT8 *ValidationFlags
)
{
UINTN Index;
UINTN SubIndex;
DEBUG((DEBUG_INFO, " start check partition entries\n"));
if (GptHeader->NumberOfPartitionEntries == 0) {
goto Exit;
}
for (Index = 0; Index < GptHeader->NumberOfPartitionEntries; Index++) {
EFI_PARTITION_ENTRY *Entry = (EFI_PARTITION_ENTRY*)((UINT8*)GptEntries + Index * GptHeader->SizeOfPartitionEntry);
//
// Skip unused entries
//
if (!CompareGuid(&Entry->PartitionTypeGUID, &Guid2)) {
UINT64 StartingLBA = Entry->StartingLBA;
UINT64 EndingLBA = Entry->EndingLBA;
if (StartingLBA > EndingLBA ||
StartingLBA < GptHeader->FirstUsableLBA ||
StartingLBA > GptHeader->LastUsableLBA ||
EndingLBA < GptHeader->FirstUsableLBA ||
EndingLBA > GptHeader->LastUsableLBA)
{
//
// Out of range
//
ValidationFlags[Index * 3] = 1;
} else {
//
// Check for required partition attribute
//
if ((Entry->Attributes & 2) != 0) {
ValidationFlags[Index * 3 + 1] = 1; // Required partition
}
//
// Check for overlap with subsequent entries
//
for (SubIndex = Index + 1; SubIndex < GptHeader->NumberOfPartitionEntries; SubIndex++) {
EFI_PARTITION_ENTRY *SubEntry = (EFI_PARTITION_ENTRY*)((UINT8*)GptEntries + SubIndex * GptHeader->SizeOfPartitionEntry);
if (!CompareGuid(&SubEntry->PartitionTypeGUID, &Guid2) &&
SubEntry->EndingLBA >= StartingLBA &&
SubEntry->StartingLBA <= EndingLBA)
{
ValidationFlags[Index * 3] = 1; // Overlap detected
ValidationFlags[SubIndex * 3 + 2] = 1; // Overlapped by prev
}
}
}
}
}
Exit:
DEBUG((DEBUG_INFO, " End check partition entries\n"));
}
// ============================================================================
// El Torito (CD-ROM) Partition Support
// ============================================================================
/**
Installs child handles for El Torito CD-ROM boot partitions.
Reads the CD volume descriptor, finds the El Torito boot catalog,
and creates child handles for each boot entry.
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
@param[in] ControllerHandle The handle of the parent controller.
@param[in] DriverBindingHandle The driver binding handle.
@param[in] ParentBlockIo The parent Block IO protocol.
@param[in] ParentBlockIoMedia The parent Block IO media.
@param[in] ParentBlockIo2 The parent Block IO2 protocol (optional).
@param[in] DevicePath The full device path.
@retval EFI_SUCCESS Partitions were enumerated.
@retval EFI_NOT_FOUND No El Torito boot entries found.
@retval EFI_OUT_OF_RESOURCES Out of resources.
@retval EFI_NO_MEDIA No media in the device.
**/
EFI_STATUS
PartitionInstallEltChildHandles(
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE DriverBindingHandle,
IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIo,
IN EFI_BLOCK_IO_PROTOCOL *ParentBlockIoMedia,
IN EFI_BLOCK_IO2_PROTOCOL *ParentBlockIo2 OPTIONAL,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
{
//
// Implementation details from disassembly (0x2D84):
// 1. Verify block size is compatible (0x800 % BlockSize == 0, BlockSize <= 0x800)
// 2. If removable media flag is set, return EFI_VOLUME_CORRUPTED
// 3. Allocate 2048-byte buffer
// 4. Scan sectors at multiples of 2048 bytes starting from 0x8000
// 5. For each sector, check for CD-ROM volume descriptor (0x00, 0xFF) and "CD001" identifier
// 6. Look for "EL TORITO SPECIFICATION" string
// 7. Read boot catalog at the specified sector
// 8. Validate boot catalog header (type 0x01, checksum 0xAA55, 16-bit sum must be 0)
// 9. For each boot catalog entry (type 0x88 = bootable):
// - Determine media type (0=floppy, 1=1.2MB, 2=1.44MB, 3=2.88MB, 4=hard disk)
// - Create MEDIA_DEVICE_PATH for each boot entry
// - Call PartitionCreateChildHandles with appropriate start/end/size
//
return EFI_UNSUPPORTED; // Placeholder - full implementation in decompiled output
}
// ============================================================================
// Component Name Protocol
// ============================================================================
/**
Retrieves the supported languages for the Component Name protocol.
@param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL instance.
@param[in] Language A pointer to an ASCII string containing the ISO 639-2 language code.
@param[out] SupportedLanguages A pointer to the Unicode string of supported languages.
@retval EFI_SUCCESS The supported languages were returned.
@retval EFI_INVALID_PARAMETER Language or SupportedLanguages is NULL.
@retval EFI_UNSUPPORTED The language is not supported.
**/
EFI_STATUS
GetSupportedLanguages(
IN EFI_COMPONENT_NAME2_PROTOCOL *This,
IN CHAR8 *Language,
OUT CHAR16 **SupportedLanguages
)
{
UINTN Index;
UINTN LangLen;
if (Language == NULL || SupportedLanguages == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Search through supported languages
//
if (This->SupportedLanguages != NULL) {
Index = 0;
while (((CHAR8*)This->SupportedLanguages)[Index] != '\0') {
//
// Compare with requested language
//
if (CompareLangCodes(Language, (CHAR8*)This->SupportedLanguages + Index)) {
goto Found;
}
//
// Skip to next language in the list
//
while (((CHAR8*)This->SupportedLanguages)[Index] != '\0' &&
((CHAR8*)This->SupportedLanguages)[Index] != ';')
{
Index++;
}
if (((CHAR8*)This->SupportedLanguages)[Index] == ';') {
Index++;
}
}
}
return EFI_UNSUPPORTED;
Found:
//
// Find matching language entry in the table
//
for (Index = 0; gSupportedLanguages[Index].LanguageString != NULL; Index += 2) {
if (gSupportedLanguages[Index].Iso639LanguageCode == NULL) {
break;
}
if (CompareStringNoCase(
gSupportedLanguages[Index].LanguageString,
Language,
&LangLen
) == 0 &&
Language[LangLen] == '\0')
{
*SupportedLanguages = gSupportedLanguages[Index].LanguageString;
return EFI_SUCCESS;
}
}
return EFI_UNSUPPORTED;
}