EmbeddedPkg/MmcDxe: Card Presence Detect Race Condition

The MMC driver defaults to assume a card is not present.  It then starts a timer in MmcDxeInitialize to check for card presence every 200ms.

However it does not immediately check to see if a card is present so if the EFI driver connection process occurs less than 200ms after the driver load, the connection process for partition 
or filesystem drivers will fail because MediaPresent still is FALSE.  To resolve this race condition, we need to immediately perform the presence check in the Start routine.


EmbeddedPkg/MmcDxe: Media ID Handling

Initialize the MMC device on Start or when presence changes instead of doing it on the Block IO calls. This way the layered drivers can be stopped and rebuilt with new Media IDs instead of 
experiencing errors on calls to Block IO.


Proposed-by: Eugene Cohen (HP)
Reviewed-by: oliviermartin




git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12237 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
oliviermartin 2011-08-30 18:02:38 +00:00
parent c8ece79ccd
commit 40842a5e7c
3 changed files with 95 additions and 80 deletions

View File

@ -299,6 +299,9 @@ MmcDriverBindingStart (
InsertMmcHost (MmcHostInstance);
MmcHostInstance->Initialized = FALSE;
// Detect card presence now
CheckCardsCallback (NULL, NULL);
}
return EFI_SUCCESS;
@ -366,6 +369,10 @@ CheckCardsCallback (
MmcHostInstance->BlockIo.Media->MediaPresent = !MmcHostInstance->Initialized;
MmcHostInstance->Initialized = !MmcHostInstance->Initialized;
if(MmcHostInstance->BlockIo.Media->MediaPresent) {
InitializeMmcDevice(MmcHostInstance);
}
Status = gBS->ReinstallProtocolInterface (
(MmcHostInstance->MmcHandle),
&gEfiBlockIoProtocolGuid,
@ -382,6 +389,7 @@ CheckCardsCallback (
}
}
EFI_DRIVER_BINDING_PROTOCOL gMmcDriverBinding = {
MmcDriverBindingSupported,
MmcDriverBindingStart,

View File

@ -288,4 +288,15 @@ MmcFlushBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This
);
EFI_STATUS InitializeMmcDevice(
IN MMC_HOST_INSTANCE *MmcHost
);
VOID
EFIAPI
CheckCardsCallback (
IN EFI_EVENT Event,
IN VOID *Context
);
#endif

View File

@ -172,7 +172,6 @@ MmcGetCardStatus(
Status = MmcHost->SendCommand(MMC_CMD13, CmdArg);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "MmcGetCardStatus(MMC_CMD13): Error and Status = %r\n", Status));
ASSERT(0);
return Status;
}
@ -305,7 +304,6 @@ MmcIdentificationMode (
if (Timeout == 0) {
DEBUG((EFI_D_ERROR, "MmcIdentificationMode(): No Card\n"));
ASSERT(0);
return EFI_NO_MEDIA;
} else {
PrintOCR(Response[0]);
@ -320,7 +318,6 @@ MmcIdentificationMode (
Status = MmcHost->SendCommand(MMC_CMD2, 0);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD2): Error\n"));
ASSERT(0);
return Status;
}
MmcHost->ReceiveResponse(MMC_RESPONSE_TYPE_CID,Response);
@ -363,6 +360,81 @@ MmcIdentificationMode (
return EFI_SUCCESS;
}
EFI_STATUS InitializeMmcDevice(
IN MMC_HOST_INSTANCE *MmcHostInstance
)
{
UINT32 Response[4];
EFI_STATUS Status;
UINTN CardSize, NumBlocks, BlockSize, CmdArg;
EFI_MMC_HOST_PROTOCOL *MmcHost;
UINTN BlockCount = 1;
MmcHost = MmcHostInstance->MmcHost;
MmcIdentificationMode (MmcHostInstance);
//Send a command to get Card specific data
CmdArg = MmcHostInstance->CardInfo.RCA << 16;
Status = MmcHost->SendCommand(MMC_CMD9, CmdArg);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD9): Error, Status=%r\n", Status));
return Status;
}
//Read Response
MmcHost->ReceiveResponse(MMC_RESPONSE_TYPE_CSD,Response);
PrintCSD(Response);
if (MmcHostInstance->CardInfo.CardType == SD_CARD_2_HIGH) {
CardSize = HC_MMC_CSD_GET_DEVICESIZE(Response);
NumBlocks = ((CardSize + 1) * 1024);
BlockSize = 1 << MMC_CSD_GET_READBLLEN(Response);
} else {
CardSize = MMC_CSD_GET_DEVICESIZE(Response);
NumBlocks = (CardSize + 1) * (1 << (MMC_CSD_GET_DEVICESIZEMULT(Response) + 2));
BlockSize = 1 << MMC_CSD_GET_READBLLEN(Response);
}
//For >=2G card, BlockSize may be 1K, but the transfer size is 512 bytes.
if (BlockSize > 512) {
NumBlocks = MultU64x32(NumBlocks, BlockSize/512);
BlockSize = 512;
}
MmcHostInstance->BlockIo.Media->LastBlock = (NumBlocks - 1);
MmcHostInstance->BlockIo.Media->BlockSize = BlockSize;
MmcHostInstance->BlockIo.Media->ReadOnly = MmcHost->IsReadOnly();
MmcHostInstance->BlockIo.Media->MediaPresent = TRUE;
MmcHostInstance->BlockIo.Media->MediaId++;
CmdArg = MmcHostInstance->CardInfo.RCA << 16;
Status = MmcHost->SendCommand(MMC_CMD7, CmdArg);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD7): Error and Status = %r\n", Status));
return Status;
}
Status = MmcNotifyState (MmcHostInstance, MmcTransferState);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "MmcIdentificationMode() : Error MmcTransferState\n"));
return Status;
}
// Set Block Length
Status = MmcHost->SendCommand(MMC_CMD16, MmcHostInstance->BlockIo.Media->BlockSize);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD16): Error MmcHostInstance->BlockIo.Media->BlockSize: %d and Error = %r\n",MmcHostInstance->BlockIo.Media->BlockSize, Status));
return Status;
}
// Block Count (not used). Could return an error for SD card
if (MmcHostInstance->CardInfo.CardType == MMC_CARD) {
MmcHost->SendCommand(MMC_CMD23, BlockCount);
}
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
MmcReset (
@ -424,7 +496,7 @@ MmcIoBlocks (
{
UINT32 Response[4];
EFI_STATUS Status;
UINTN CardSize, NumBlocks, BlockSize, CmdArg;
UINTN CmdArg;
INTN Timeout;
UINTN Cmd;
MMC_HOST_INSTANCE *MmcHostInstance;
@ -446,89 +518,13 @@ MmcIoBlocks (
return EFI_NO_MEDIA;
}
// If the driver has not been initialized yet then go into Identification Mode
if (MmcHostInstance->State == MmcHwInitializationState) {
MmcIdentificationMode (MmcHostInstance);
//Send a command to get Card specific data
CmdArg = MmcHostInstance->CardInfo.RCA << 16;
Status = MmcHost->SendCommand(MMC_CMD9, CmdArg);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD9): Error, Status=%r\n", Status));
ASSERT(0);
return Status;
}
//Read Response
MmcHost->ReceiveResponse(MMC_RESPONSE_TYPE_CSD,Response);
PrintCSD(Response);
if (MmcHostInstance->CardInfo.CardType == SD_CARD_2_HIGH) {
CardSize = HC_MMC_CSD_GET_DEVICESIZE(Response);
NumBlocks = ((CardSize + 1) * 1024);
BlockSize = 1 << MMC_CSD_GET_READBLLEN(Response);
} else {
CardSize = MMC_CSD_GET_DEVICESIZE(Response);
NumBlocks = (CardSize + 1) * (1 << (MMC_CSD_GET_DEVICESIZEMULT(Response) + 2));
BlockSize = 1 << MMC_CSD_GET_READBLLEN(Response);
}
//For >=2G card, BlockSize may be 1K, but the transfer size is 512 bytes.
if (BlockSize > 512) {
NumBlocks = MultU64x32(NumBlocks, BlockSize/512);
BlockSize = 512;
}
MmcHostInstance->BlockIo.Media->LastBlock = (NumBlocks - 1);
MmcHostInstance->BlockIo.Media->BlockSize = BlockSize;
MmcHostInstance->BlockIo.Media->ReadOnly = MmcHost->IsReadOnly();
MmcHostInstance->BlockIo.Media->MediaPresent = TRUE;
MmcHostInstance->BlockIo.Media->MediaId++;
CmdArg = MmcHostInstance->CardInfo.RCA << 16;
Status = MmcHost->SendCommand(MMC_CMD7, CmdArg);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD7): Error and Status = %r\n", Status));
ASSERT(0);
return Status;
}
Status = MmcNotifyState (MmcHostInstance, MmcTransferState);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "MmcIdentificationMode() : Error MmcTransferState\n"));
return Status;
}
// Set Block Length
Status = MmcHost->SendCommand(MMC_CMD16, This->Media->BlockSize);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD16): Error This->Media->BlockSize: %d and Error = %r\n",This->Media->BlockSize, Status));
return Status;
}
// Block Count (not used). Could return an error for SD card
if (MmcHostInstance->CardInfo.CardType == MMC_CARD) {
MmcHost->SendCommand(MMC_CMD23, BlockCount);
}
} else {
// Maybe test if the card has changed to update gMmcMedia information
if (MmcHostInstance->State == MmcTransferState) {
//DEBUG((EFI_D_ERROR, "MmcIdentificationMode() : MmcTransferState\n"));
} else if (MmcHostInstance->State == MmcStandByState) {
DEBUG((EFI_D_ERROR, "MmcIdentificationMode() : MmcStandByState\n"));
} else {
ASSERT(0);
}
}
// All blocks must be within the device
if ((Lba + (BufferSize / This->Media->BlockSize)) > (This->Media->LastBlock + 1)){
ASSERT(0);
return EFI_INVALID_PARAMETER;
}
// The buffer size must not be zero and it must be an exact multiple of the block size
if ((BufferSize == 0) || ((BufferSize % This->Media->BlockSize) != 0)) {
ASSERT(0);
return EFI_BAD_BUFFER_SIZE;
}