audk/IntelFrameworkModulePkg/Csm/BiosThunk/BlockIoDxe/BiosBlkIo.c

774 lines
23 KiB
C

/** @file
EFI glue for BIOS INT 13h block devices.
This file is coded to EDD 3.0 as defined by T13 D1386 Revision 4
Availible on http://www.t13.org/#Project drafts
Currently at ftp://fission.dt.wdc.com/pub/standards/x3t13/project/d1386r4.pdf
Copyright (c) 1999 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "BiosBlkIo.h"
//
// Global data declaration
//
//
// EFI Driver Binding Protocol Instance
//
EFI_DRIVER_BINDING_PROTOCOL gBiosBlockIoDriverBinding = {
BiosBlockIoDriverBindingSupported,
BiosBlockIoDriverBindingStart,
BiosBlockIoDriverBindingStop,
0x3,
NULL,
NULL
};
//
// Semaphore to control access to global variables mActiveInstances and mBufferUnder1Mb
//
EFI_LOCK mGlobalDataLock = EFI_INITIALIZE_LOCK_VARIABLE(TPL_APPLICATION);
//
// Number of active instances of this protocol. This is used to allocate/free
// the shared buffer. You must acquire the semaphore to modify.
//
UINTN mActiveInstances = 0;
//
// Pointer to the beginning of the buffer used for real mode thunk
// You must acquire the semaphore to modify.
//
EFI_PHYSICAL_ADDRESS mBufferUnder1Mb = 0;
//
// Address packet is a buffer under 1 MB for all version EDD calls
//
EDD_DEVICE_ADDRESS_PACKET *mEddBufferUnder1Mb;
//
// This is a buffer for INT 13h func 48 information
//
BIOS_LEGACY_DRIVE *mLegacyDriverUnder1Mb;
//
// Buffer of 0xFE00 bytes for EDD 1.1 transfer must be under 1 MB
// 0xFE00 bytes is the max transfer size supported.
//
VOID *mEdd11Buffer;
/**
Driver entry point.
@param ImageHandle Handle of driver image.
@param SystemTable Pointer to system table.
@retval EFI_SUCCESS Entrypoint successfully executed.
@retval Others Fail to execute entrypoint.
**/
EFI_STATUS
EFIAPI
BiosBlockIoDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Install protocols
//
Status = EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&gBiosBlockIoDriverBinding,
ImageHandle,
&gBiosBlockIoComponentName,
&gBiosBlockIoComponentName2
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Install Legacy BIOS GUID to mark this driver as a BIOS Thunk Driver
//
return gBS->InstallMultipleProtocolInterfaces (
&ImageHandle,
&gEfiLegacyBiosGuid,
NULL,
NULL
);
}
/**
Check whether the driver supports this device.
@param This The Udriver binding protocol.
@param Controller The controller handle to check.
@param RemainingDevicePath The remaining device path.
@retval EFI_SUCCESS The driver supports this controller.
@retval other This device isn't supported.
**/
EFI_STATUS
EFIAPI
BiosBlockIoDriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
EFI_PCI_IO_PROTOCOL *PciIo;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
PCI_TYPE00 Pci;
//
// See if the Legacy BIOS Protocol is available
//
Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
if (EFI_ERROR (Status)) {
return Status;
}
Status = gBS->OpenProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **) &DevicePath,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
gBS->CloseProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
Controller
);
//
// Open the IO Abstraction(s) needed to perform the supported test
//
Status = gBS->OpenProtocol (
Controller,
&gEfiPciIoProtocolGuid,
(VOID **) &PciIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// See if this is a PCI VGA Controller by looking at the Command register and
// Class Code Register
//
Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0, sizeof (Pci) / sizeof (UINT32), &Pci);
if (EFI_ERROR (Status)) {
Status = EFI_UNSUPPORTED;
goto Done;
}
Status = EFI_UNSUPPORTED;
if (Pci.Hdr.ClassCode[2] == PCI_CLASS_MASS_STORAGE ||
(Pci.Hdr.ClassCode[2] == PCI_BASE_CLASS_INTELLIGENT && Pci.Hdr.ClassCode[1] == PCI_SUB_CLASS_INTELLIGENT)
) {
Status = EFI_SUCCESS;
}
Done:
gBS->CloseProtocol (
Controller,
&gEfiPciIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
return Status;
}
/**
Starts the device with this driver.
@param This The driver binding instance.
@param Controller Handle of device to bind driver to.
@param RemainingDevicePath Optional parameter use to pick a specific child
device to start.
@retval EFI_SUCCESS The controller is controlled by the driver.
@retval Other This controller cannot be started.
**/
EFI_STATUS
EFIAPI
BiosBlockIoDriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
EFI_PCI_IO_PROTOCOL *PciIo;
UINT8 DiskStart;
UINT8 DiskEnd;
BIOS_BLOCK_IO_DEV *BiosBlockIoPrivate;
EFI_DEVICE_PATH_PROTOCOL *PciDevPath;
UINTN Index;
UINTN Flags;
UINTN TmpAddress;
BOOLEAN DeviceEnable;
//
// Initialize variables
//
PciIo = NULL;
PciDevPath = NULL;
DeviceEnable = FALSE;
//
// See if the Legacy BIOS Protocol is available
//
Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
if (EFI_ERROR (Status)) {
goto Error;
}
//
// Open the IO Abstraction(s) needed
//
Status = gBS->OpenProtocol (
Controller,
&gEfiPciIoProtocolGuid,
(VOID **) &PciIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = gBS->OpenProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
(VOID **) &PciDevPath,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
goto Error;
}
//
// Enable the device and make sure VGA cycles are being forwarded to this VGA device
//
Status = PciIo->Attributes (
PciIo,
EfiPciIoAttributeOperationEnable,
EFI_PCI_DEVICE_ENABLE,
NULL
);
if (EFI_ERROR (Status)) {
goto Error;
}
DeviceEnable = TRUE;
//
// Check to see if there is a legacy option ROM image associated with this PCI device
//
Status = LegacyBios->CheckPciRom (
LegacyBios,
Controller,
NULL,
NULL,
&Flags
);
if (EFI_ERROR (Status)) {
goto Error;
}
//
// Post the legacy option ROM if it is available.
//
Status = LegacyBios->InstallPciRom (
LegacyBios,
Controller,
NULL,
&Flags,
&DiskStart,
&DiskEnd,
NULL,
NULL
);
if (EFI_ERROR (Status)) {
goto Error;
}
//
// All instances share a buffer under 1MB to put real mode thunk code in
// If it has not been allocated, then we allocate it.
//
if (mBufferUnder1Mb == 0) {
//
// Should only be here if there are no active instances
//
ASSERT (mActiveInstances == 0);
//
// Acquire the lock
//
EfiAcquireLock (&mGlobalDataLock);
//
// Allocate below 1MB
//
mBufferUnder1Mb = 0x00000000000FFFFF;
Status = gBS->AllocatePages (AllocateMaxAddress, EfiBootServicesData, BLOCK_IO_BUFFER_PAGE_SIZE, &mBufferUnder1Mb);
//
// Release the lock
//
EfiReleaseLock (&mGlobalDataLock);
//
// Check memory allocation success
//
if (EFI_ERROR (Status)) {
//
// In checked builds we want to assert if the allocate failed.
//
ASSERT_EFI_ERROR (Status);
Status = EFI_OUT_OF_RESOURCES;
mBufferUnder1Mb = 0;
goto Error;
}
TmpAddress = (UINTN) mBufferUnder1Mb;
//
// Adjusting the value to be on proper boundary
//
mEdd11Buffer = (VOID *) ALIGN_VARIABLE (TmpAddress);
TmpAddress = (UINTN) mEdd11Buffer + MAX_EDD11_XFER;
//
// Adjusting the value to be on proper boundary
//
mLegacyDriverUnder1Mb = (BIOS_LEGACY_DRIVE *) ALIGN_VARIABLE (TmpAddress);
TmpAddress = (UINTN) mLegacyDriverUnder1Mb + sizeof (BIOS_LEGACY_DRIVE);
//
// Adjusting the value to be on proper boundary
//
mEddBufferUnder1Mb = (EDD_DEVICE_ADDRESS_PACKET *) ALIGN_VARIABLE (TmpAddress);
}
//
// Allocate the private device structure for each disk
//
for (Index = DiskStart; Index < DiskEnd; Index++) {
Status = gBS->AllocatePool (
EfiBootServicesData,
sizeof (BIOS_BLOCK_IO_DEV),
(VOID **) &BiosBlockIoPrivate
);
if (EFI_ERROR (Status)) {
goto Error;
}
//
// Zero the private device structure
//
ZeroMem (BiosBlockIoPrivate, sizeof (BIOS_BLOCK_IO_DEV));
//
// Initialize the private device structure
//
BiosBlockIoPrivate->Signature = BIOS_CONSOLE_BLOCK_IO_DEV_SIGNATURE;
BiosBlockIoPrivate->ControllerHandle = Controller;
BiosBlockIoPrivate->LegacyBios = LegacyBios;
BiosBlockIoPrivate->PciIo = PciIo;
BiosBlockIoPrivate->Bios.Floppy = FALSE;
BiosBlockIoPrivate->Bios.Number = (UINT8) Index;
BiosBlockIoPrivate->Bios.Letter = (UINT8) (Index - 0x80 + 'C');
BiosBlockIoPrivate->BlockMedia.RemovableMedia = FALSE;
if (BiosInitBlockIo (BiosBlockIoPrivate)) {
SetBiosInitBlockIoDevicePath (PciDevPath, &BiosBlockIoPrivate->Bios, &BiosBlockIoPrivate->DevicePath);
//
// Install the Block Io Protocol onto a new child handle
//
Status = gBS->InstallMultipleProtocolInterfaces (
&BiosBlockIoPrivate->Handle,
&gEfiBlockIoProtocolGuid,
&BiosBlockIoPrivate->BlockIo,
&gEfiDevicePathProtocolGuid,
BiosBlockIoPrivate->DevicePath,
NULL
);
if (EFI_ERROR (Status)) {
gBS->FreePool (BiosBlockIoPrivate);
}
//
// Open For Child Device
//
Status = gBS->OpenProtocol (
Controller,
&gEfiPciIoProtocolGuid,
(VOID **) &BiosBlockIoPrivate->PciIo,
This->DriverBindingHandle,
BiosBlockIoPrivate->Handle,
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
} else {
gBS->FreePool (BiosBlockIoPrivate);
}
}
Error:
if (EFI_ERROR (Status)) {
if (PciIo != NULL) {
if (DeviceEnable) {
PciIo->Attributes (
PciIo,
EfiPciIoAttributeOperationDisable,
EFI_PCI_DEVICE_ENABLE,
NULL
);
}
gBS->CloseProtocol (
Controller,
&gEfiPciIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
if (PciDevPath != NULL) {
gBS->CloseProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
Controller
);
}
if (mBufferUnder1Mb != 0 && mActiveInstances == 0) {
gBS->FreePages (mBufferUnder1Mb, BLOCK_IO_BUFFER_PAGE_SIZE);
//
// Clear the buffer back to 0
//
EfiAcquireLock (&mGlobalDataLock);
mBufferUnder1Mb = 0;
EfiReleaseLock (&mGlobalDataLock);
}
}
} else {
//
// Successfully installed, so increment the number of active instances
//
EfiAcquireLock (&mGlobalDataLock);
mActiveInstances++;
EfiReleaseLock (&mGlobalDataLock);
}
return Status;
}
/**
Stop the device handled by this driver.
@param This The driver binding protocol.
@param Controller The controller to release.
@param NumberOfChildren The number of handles in ChildHandleBuffer.
@param ChildHandleBuffer The array of child handle.
@retval EFI_SUCCESS The device was stopped.
@retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
@retval Others Fail to uninstall protocols attached on the device.
**/
EFI_STATUS
EFIAPI
BiosBlockIoDriverBindingStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
)
{
EFI_STATUS Status;
BOOLEAN AllChildrenStopped;
EFI_BLOCK_IO_PROTOCOL *BlockIo;
BIOS_BLOCK_IO_DEV *BiosBlockIoPrivate;
UINTN Index;
//
// Decrement the number of active instances
//
if (mActiveInstances != 0) {
//
// Add a check since the stop function will be called 2 times for each handle
//
EfiAcquireLock (&mGlobalDataLock);
mActiveInstances--;
EfiReleaseLock (&mGlobalDataLock);
}
if ((mActiveInstances == 0) && (mBufferUnder1Mb != 0)) {
//
// Free our global buffer
//
Status = gBS->FreePages (mBufferUnder1Mb, BLOCK_IO_BUFFER_PAGE_SIZE);
ASSERT_EFI_ERROR (Status);
EfiAcquireLock (&mGlobalDataLock);
mBufferUnder1Mb = 0;
EfiReleaseLock (&mGlobalDataLock);
}
AllChildrenStopped = TRUE;
for (Index = 0; Index < NumberOfChildren; Index++) {
Status = gBS->OpenProtocol (
ChildHandleBuffer[Index],
&gEfiBlockIoProtocolGuid,
(VOID **) &BlockIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
BiosBlockIoPrivate = BIOS_BLOCK_IO_FROM_THIS (BlockIo);
//
// Release PCI I/O and Block IO Protocols on the clild handle.
//
Status = gBS->UninstallMultipleProtocolInterfaces (
ChildHandleBuffer[Index],
&gEfiBlockIoProtocolGuid,
&BiosBlockIoPrivate->BlockIo,
&gEfiDevicePathProtocolGuid,
BiosBlockIoPrivate->DevicePath,
NULL
);
if (EFI_ERROR (Status)) {
AllChildrenStopped = FALSE;
}
//
// Shutdown the hardware
//
BiosBlockIoPrivate->PciIo->Attributes (
BiosBlockIoPrivate->PciIo,
EfiPciIoAttributeOperationDisable,
EFI_PCI_DEVICE_ENABLE,
NULL
);
gBS->CloseProtocol (
Controller,
&gEfiPciIoProtocolGuid,
This->DriverBindingHandle,
ChildHandleBuffer[Index]
);
gBS->FreePool (BiosBlockIoPrivate);
}
if (!AllChildrenStopped) {
return EFI_DEVICE_ERROR;
}
Status = gBS->CloseProtocol (
Controller,
&gEfiDevicePathProtocolGuid,
This->DriverBindingHandle,
Controller
);
Status = gBS->CloseProtocol (
Controller,
&gEfiPciIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
return EFI_SUCCESS;
}
/**
Build device path for device.
@param BaseDevicePath Base device path.
@param Drive Legacy drive.
@param DevicePath Device path for output.
**/
VOID
SetBiosInitBlockIoDevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *BaseDevicePath,
IN BIOS_LEGACY_DRIVE *Drive,
OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
)
{
EFI_STATUS Status;
BLOCKIO_VENDOR_DEVICE_PATH VendorNode;
Status = EFI_UNSUPPORTED;
//
// BugBug: Check for memory leaks!
//
if (Drive->EddVersion == EDD_VERSION_30) {
//
// EDD 3.0 case.
//
Status = BuildEdd30DevicePath (BaseDevicePath, Drive, DevicePath);
}
if (EFI_ERROR (Status)) {
//
// EDD 1.1 device case or it is unrecognized EDD 3.0 device
//
ZeroMem (&VendorNode, sizeof (VendorNode));
VendorNode.DevicePath.Header.Type = HARDWARE_DEVICE_PATH;
VendorNode.DevicePath.Header.SubType = HW_VENDOR_DP;
SetDevicePathNodeLength (&VendorNode.DevicePath.Header, sizeof (VendorNode));
CopyMem (&VendorNode.DevicePath.Guid, &gBlockIoVendorGuid, sizeof (EFI_GUID));
VendorNode.LegacyDriveLetter = Drive->Number;
*DevicePath = AppendDevicePathNode (BaseDevicePath, &VendorNode.DevicePath.Header);
}
}
/**
Build device path for EDD 3.0.
@param BaseDevicePath Base device path.
@param Drive Legacy drive.
@param DevicePath Device path for output.
@retval EFI_SUCCESS The device path is built successfully.
@retval EFI_UNSUPPORTED It is failed to built device path.
**/
EFI_STATUS
BuildEdd30DevicePath (
IN EFI_DEVICE_PATH_PROTOCOL *BaseDevicePath,
IN BIOS_LEGACY_DRIVE *Drive,
IN EFI_DEVICE_PATH_PROTOCOL **DevicePath
)
{
//
// AVL UINT64 Address;
// AVL EFI_HANDLE Handle;
//
EFI_DEV_PATH Node;
UINT32 Controller;
Controller = (UINT32) Drive->Parameters.InterfacePath.Pci.Controller;
ZeroMem (&Node, sizeof (Node));
if ((AsciiStrnCmp ("ATAPI", Drive->Parameters.InterfaceType, 5) == 0) ||
(AsciiStrnCmp ("ATA", Drive->Parameters.InterfaceType, 3) == 0)
) {
//
// ATA or ATAPI drive found
//
Node.Atapi.Header.Type = MESSAGING_DEVICE_PATH;
Node.Atapi.Header.SubType = MSG_ATAPI_DP;
SetDevicePathNodeLength (&Node.Atapi.Header, sizeof (ATAPI_DEVICE_PATH));
Node.Atapi.SlaveMaster = Drive->Parameters.DevicePath.Atapi.Master;
Node.Atapi.Lun = Drive->Parameters.DevicePath.Atapi.Lun;
Node.Atapi.PrimarySecondary = (UINT8) Controller;
} else {
//
// Not an ATA/ATAPI drive
//
if (Controller != 0) {
ZeroMem (&Node, sizeof (Node));
Node.Controller.Header.Type = HARDWARE_DEVICE_PATH;
Node.Controller.Header.SubType = HW_CONTROLLER_DP;
SetDevicePathNodeLength (&Node.Controller.Header, sizeof (CONTROLLER_DEVICE_PATH));
Node.Controller.ControllerNumber = Controller;
*DevicePath = AppendDevicePathNode (*DevicePath, &Node.DevPath);
}
ZeroMem (&Node, sizeof (Node));
if (AsciiStrnCmp ("SCSI", Drive->Parameters.InterfaceType, 4) == 0) {
//
// SCSI drive
//
Node.Scsi.Header.Type = MESSAGING_DEVICE_PATH;
Node.Scsi.Header.SubType = MSG_SCSI_DP;
SetDevicePathNodeLength (&Node.Scsi.Header, sizeof (SCSI_DEVICE_PATH));
//
// Lun is miss aligned in both EDD and Device Path data structures.
// thus we do a byte copy, to prevent alignment traps on IA-64.
//
CopyMem (&Node.Scsi.Lun, &Drive->Parameters.DevicePath.Scsi.Lun, sizeof (UINT16));
Node.Scsi.Pun = Drive->Parameters.DevicePath.Scsi.Pun;
} else if (AsciiStrnCmp ("USB", Drive->Parameters.InterfaceType, 3) == 0) {
//
// USB drive
//
Node.Usb.Header.Type = MESSAGING_DEVICE_PATH;
Node.Usb.Header.SubType = MSG_USB_DP;
SetDevicePathNodeLength (&Node.Usb.Header, sizeof (USB_DEVICE_PATH));
Node.Usb.ParentPortNumber = (UINT8) Drive->Parameters.DevicePath.Usb.Reserved;
} else if (AsciiStrnCmp ("1394", Drive->Parameters.InterfaceType, 4) == 0) {
//
// 1394 drive
//
Node.F1394.Header.Type = MESSAGING_DEVICE_PATH;
Node.F1394.Header.SubType = MSG_1394_DP;
SetDevicePathNodeLength (&Node.F1394.Header, sizeof (F1394_DEVICE_PATH));
Node.F1394.Guid = Drive->Parameters.DevicePath.FireWire.Guid;
} else if (AsciiStrnCmp ("FIBRE", Drive->Parameters.InterfaceType, 5) == 0) {
//
// Fibre drive
//
Node.FibreChannel.Header.Type = MESSAGING_DEVICE_PATH;
Node.FibreChannel.Header.SubType = MSG_FIBRECHANNEL_DP;
SetDevicePathNodeLength (&Node.FibreChannel.Header, sizeof (FIBRECHANNEL_DEVICE_PATH));
Node.FibreChannel.WWN = Drive->Parameters.DevicePath.FibreChannel.Wwn;
Node.FibreChannel.Lun = Drive->Parameters.DevicePath.FibreChannel.Lun;
} else {
DEBUG (
(
DEBUG_BLKIO, "It is unrecognized EDD 3.0 device, Drive Number = %x, InterfaceType = %s\n",
Drive->Number,
Drive->Parameters.InterfaceType
)
);
}
}
if (Node.DevPath.Type == 0) {
return EFI_UNSUPPORTED;
}
*DevicePath = AppendDevicePathNode (BaseDevicePath, &Node.DevPath);
return EFI_SUCCESS;
}