The 3rd and 4th parameters of the CloseProtocol() call are wrong. Given that we're not dissociating a child controller from a parent controller (= closing a BY_CHILD_CONTROLLER open), but closing a BY_DRIVER open, the 4th parameter (ControllerHandle) should equal the 1st parameter (Handle). It's unclear why this code hasn't crashed before. Note that the patch doesn't fix the underlying driver model bug. I don't understand what the loop in MmcDriverBindingStop() attempts to do. Is this driver supposed to be a bus driver? It seems to create new handles, and to append device path nodes. But it doesn't set up proper parent/child protocol opens, and it doesn't close them. Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Leif Lindholm <leif.lindholm@linaro.org> Signed-off-by: Laszlo Ersek <lersek@redhat.com> Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
453 lines
12 KiB
C
453 lines
12 KiB
C
/** @file
|
|
Main file of the MMC Dxe driver. The driver entrypoint is defined into this file.
|
|
|
|
Copyright (c) 2011-2013, ARM Limited. All rights reserved.
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include <Protocol/DevicePath.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
|
|
#include "Mmc.h"
|
|
|
|
EFI_BLOCK_IO_MEDIA mMmcMediaTemplate = {
|
|
SIGNATURE_32('m','m','c','o'), // MediaId
|
|
TRUE, // RemovableMedia
|
|
FALSE, // MediaPresent
|
|
FALSE, // LogicalPartition
|
|
FALSE, // ReadOnly
|
|
FALSE, // WriteCaching
|
|
512, // BlockSize
|
|
4, // IoAlign
|
|
0, // Pad
|
|
0 // LastBlock
|
|
};
|
|
|
|
//
|
|
// This device structure is serviced as a header.
|
|
// Its next field points to the first root bridge device node.
|
|
//
|
|
LIST_ENTRY mMmcHostPool;
|
|
|
|
/**
|
|
Event triggered by the timer to check if any cards have been removed
|
|
or if new ones have been plugged in
|
|
**/
|
|
|
|
EFI_EVENT gCheckCardsEvent;
|
|
|
|
/**
|
|
Initialize the MMC Host Pool to support multiple MMC devices
|
|
**/
|
|
VOID
|
|
InitializeMmcHostPool (
|
|
VOID
|
|
)
|
|
{
|
|
InitializeListHead (&mMmcHostPool);
|
|
}
|
|
|
|
/**
|
|
Insert a new Mmc Host controller to the pool
|
|
**/
|
|
VOID
|
|
InsertMmcHost (
|
|
IN MMC_HOST_INSTANCE *MmcHostInstance
|
|
)
|
|
{
|
|
InsertTailList (&mMmcHostPool, &(MmcHostInstance->Link));
|
|
}
|
|
|
|
/*
|
|
Remove a new Mmc Host controller to the pool
|
|
*/
|
|
VOID
|
|
RemoveMmcHost (
|
|
IN MMC_HOST_INSTANCE *MmcHostInstance
|
|
)
|
|
{
|
|
RemoveEntryList (&(MmcHostInstance->Link));
|
|
}
|
|
|
|
MMC_HOST_INSTANCE* CreateMmcHostInstance (
|
|
IN EFI_MMC_HOST_PROTOCOL* MmcHost
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
MMC_HOST_INSTANCE* MmcHostInstance;
|
|
EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
|
|
MmcHostInstance = AllocateZeroPool (sizeof (MMC_HOST_INSTANCE));
|
|
if (MmcHostInstance == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
MmcHostInstance->Signature = MMC_HOST_INSTANCE_SIGNATURE;
|
|
|
|
MmcHostInstance->State = MmcHwInitializationState;
|
|
|
|
MmcHostInstance->BlockIo.Media = AllocateCopyPool (sizeof(EFI_BLOCK_IO_MEDIA), &mMmcMediaTemplate);
|
|
if (MmcHostInstance->BlockIo.Media == NULL) {
|
|
goto FREE_INSTANCE;
|
|
}
|
|
|
|
MmcHostInstance->BlockIo.Revision = EFI_BLOCK_IO_INTERFACE_REVISION;
|
|
MmcHostInstance->BlockIo.Reset = MmcReset;
|
|
MmcHostInstance->BlockIo.ReadBlocks = MmcReadBlocks;
|
|
MmcHostInstance->BlockIo.WriteBlocks = MmcWriteBlocks;
|
|
MmcHostInstance->BlockIo.FlushBlocks = MmcFlushBlocks;
|
|
|
|
MmcHostInstance->MmcHost = MmcHost;
|
|
|
|
// Create DevicePath for the new MMC Host
|
|
Status = MmcHost->BuildDevicePath (MmcHost, &NewDevicePathNode);
|
|
if (EFI_ERROR (Status)) {
|
|
goto FREE_MEDIA;
|
|
}
|
|
|
|
DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocatePool (END_DEVICE_PATH_LENGTH);
|
|
if (DevicePath == NULL) {
|
|
goto FREE_MEDIA;
|
|
}
|
|
|
|
SetDevicePathEndNode (DevicePath);
|
|
MmcHostInstance->DevicePath = AppendDevicePathNode (DevicePath, NewDevicePathNode);
|
|
|
|
// Publish BlockIO protocol interface
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&MmcHostInstance->MmcHandle,
|
|
&gEfiBlockIoProtocolGuid,&MmcHostInstance->BlockIo,
|
|
&gEfiDevicePathProtocolGuid,MmcHostInstance->DevicePath,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
goto FREE_DEVICE_PATH;
|
|
}
|
|
|
|
return MmcHostInstance;
|
|
|
|
FREE_DEVICE_PATH:
|
|
FreePool(DevicePath);
|
|
|
|
FREE_MEDIA:
|
|
FreePool(MmcHostInstance->BlockIo.Media);
|
|
|
|
FREE_INSTANCE:
|
|
FreePool(MmcHostInstance);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
EFI_STATUS DestroyMmcHostInstance (
|
|
IN MMC_HOST_INSTANCE* MmcHostInstance
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
// Uninstall Protocol Interfaces
|
|
Status = gBS->UninstallMultipleProtocolInterfaces (
|
|
MmcHostInstance->MmcHandle,
|
|
&gEfiBlockIoProtocolGuid,&(MmcHostInstance->BlockIo),
|
|
&gEfiDevicePathProtocolGuid,MmcHostInstance->DevicePath,
|
|
NULL
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
// Free Memory allocated for the instance
|
|
if (MmcHostInstance->BlockIo.Media) {
|
|
FreePool(MmcHostInstance->BlockIo.Media);
|
|
}
|
|
if (MmcHostInstance->CardInfo.ECSDData) {
|
|
FreePages (MmcHostInstance->CardInfo.ECSDData, EFI_SIZE_TO_PAGES (sizeof (ECSD)));
|
|
}
|
|
FreePool (MmcHostInstance);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
This function checks if the controller implement the Mmc Host and the Device Path Protocols
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
MmcDriverBindingSupported (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
//EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
|
|
EFI_MMC_HOST_PROTOCOL *MmcHost;
|
|
EFI_DEV_PATH_PTR 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.DevPath = RemainingDevicePath;
|
|
if (Node.DevPath->Type != HARDWARE_DEVICE_PATH ||
|
|
Node.DevPath->SubType != HW_VENDOR_DP ||
|
|
DevicePathNodeLength(Node.DevPath) != sizeof(VENDOR_DEVICE_PATH)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if Mmc Host protocol is installed by platform
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiMmcHostProtocolGuid,
|
|
(VOID **) &MmcHost,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
if (Status == EFI_ALREADY_STARTED) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Close the Mmc Host used to perform the supported test
|
|
//
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiMmcHostProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
MmcDriverBindingStart (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
MMC_HOST_INSTANCE *MmcHostInstance;
|
|
EFI_MMC_HOST_PROTOCOL *MmcHost;
|
|
|
|
//
|
|
// Check RemainingDevicePath validation
|
|
//
|
|
if (RemainingDevicePath != NULL) {
|
|
//
|
|
// Check if RemainingDevicePath is the End of Device Path Node,
|
|
// if yes, return EFI_SUCCESS
|
|
//
|
|
if (IsDevicePathEnd (RemainingDevicePath)) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the Mmc Host protocol
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiMmcHostProtocolGuid,
|
|
(VOID **) &MmcHost,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
if (Status == EFI_ALREADY_STARTED) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
MmcHostInstance = CreateMmcHostInstance(MmcHost);
|
|
if (MmcHostInstance != NULL) {
|
|
// Add the handle to the pool
|
|
InsertMmcHost (MmcHostInstance);
|
|
|
|
MmcHostInstance->Initialized = FALSE;
|
|
|
|
// Detect card presence now
|
|
CheckCardsCallback (NULL, NULL);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
MmcDriverBindingStop (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN UINTN NumberOfChildren,
|
|
IN EFI_HANDLE *ChildHandleBuffer
|
|
)
|
|
{
|
|
EFI_STATUS Status = EFI_SUCCESS;
|
|
LIST_ENTRY *CurrentLink;
|
|
MMC_HOST_INSTANCE *MmcHostInstance;
|
|
|
|
MMC_TRACE("MmcDriverBindingStop()");
|
|
|
|
// For each MMC instance
|
|
CurrentLink = mMmcHostPool.ForwardLink;
|
|
while (CurrentLink != NULL && CurrentLink != &mMmcHostPool && (Status == EFI_SUCCESS)) {
|
|
MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink);
|
|
ASSERT(MmcHostInstance != NULL);
|
|
|
|
// Close gEfiMmcHostProtocolGuid
|
|
Status = gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiMmcHostProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
// Remove MMC Host Instance from the pool
|
|
RemoveMmcHost (MmcHostInstance);
|
|
|
|
// Destroy MmcHostInstance
|
|
DestroyMmcHostInstance (MmcHostInstance);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
EFIAPI
|
|
CheckCardsCallback (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
LIST_ENTRY *CurrentLink;
|
|
MMC_HOST_INSTANCE *MmcHostInstance;
|
|
EFI_STATUS Status;
|
|
|
|
CurrentLink = mMmcHostPool.ForwardLink;
|
|
while (CurrentLink != NULL && CurrentLink != &mMmcHostPool) {
|
|
MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink);
|
|
ASSERT(MmcHostInstance != NULL);
|
|
|
|
if (MmcHostInstance->MmcHost->IsCardPresent (MmcHostInstance->MmcHost) == !MmcHostInstance->Initialized) {
|
|
MmcHostInstance->State = MmcHwInitializationState;
|
|
MmcHostInstance->BlockIo.Media->MediaPresent = !MmcHostInstance->Initialized;
|
|
MmcHostInstance->Initialized = !MmcHostInstance->Initialized;
|
|
|
|
if (MmcHostInstance->BlockIo.Media->MediaPresent) {
|
|
InitializeMmcDevice (MmcHostInstance);
|
|
}
|
|
|
|
Status = gBS->ReinstallProtocolInterface (
|
|
(MmcHostInstance->MmcHandle),
|
|
&gEfiBlockIoProtocolGuid,
|
|
&(MmcHostInstance->BlockIo),
|
|
&(MmcHostInstance->BlockIo)
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
Print(L"MMC Card: Error reinstalling BlockIo interface\n");
|
|
}
|
|
}
|
|
|
|
CurrentLink = CurrentLink->ForwardLink;
|
|
}
|
|
}
|
|
|
|
|
|
EFI_DRIVER_BINDING_PROTOCOL gMmcDriverBinding = {
|
|
MmcDriverBindingSupported,
|
|
MmcDriverBindingStart,
|
|
MmcDriverBindingStop,
|
|
0xa,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
/**
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
MmcDxeInitialize (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Initializes MMC Host pool
|
|
//
|
|
InitializeMmcHostPool ();
|
|
|
|
//
|
|
// Install driver model protocol(s).
|
|
//
|
|
Status = EfiLibInstallDriverBindingComponentName2 (
|
|
ImageHandle,
|
|
SystemTable,
|
|
&gMmcDriverBinding,
|
|
ImageHandle,
|
|
&gMmcComponentName,
|
|
&gMmcComponentName2
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
// Install driver diagnostics
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&ImageHandle,
|
|
&gEfiDriverDiagnostics2ProtocolGuid,&gMmcDriverDiagnostics2,
|
|
NULL
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
// Use a timer to detect if a card has been plugged in or removed
|
|
Status = gBS->CreateEvent (
|
|
EVT_NOTIFY_SIGNAL | EVT_TIMER,
|
|
TPL_CALLBACK,
|
|
CheckCardsCallback,
|
|
NULL,
|
|
&gCheckCardsEvent);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
Status = gBS->SetTimer(
|
|
gCheckCardsEvent,
|
|
TimerPeriodic,
|
|
(UINT64)(10*1000*200)); // 200 ms
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
return Status;
|
|
}
|