]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c
MdeModulePkg/Sd: add Erase Block support on sd/emmc device
[mirror_edk2.git] / MdeModulePkg / Bus / Sd / SdDxe / SdBlockIo.c
index b7a5fe49ae1c2dc89a4c2a9dbd954afd9a16d0e3..d8b9459c63c343bc02492146333948d1129b985c 100644 (file)
@@ -970,4 +970,390 @@ SdFlushBlocksEx (
   return EFI_SUCCESS;\r
 }\r
 \r
+/**\r
+  Set the erase start address through sync or async I/O request.\r
+\r
+  @param[in]  Device            A pointer to the SD_DEVICE instance.\r
+  @param[in]  StartLba          The starting logical block address to be erased.\r
+  @param[in]  Token             A pointer to the token associated with the transaction.\r
+  @param[in]  IsEnd             A boolean to show whether it's the last cmd in a series of cmds.\r
+                                This parameter is only meaningful in async I/O request.\r
+\r
+  @retval EFI_SUCCESS           The request is executed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be executed due to a lack of resources.\r
+  @retval Others                The request could not be executed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SdEraseBlockStart (\r
+  IN  SD_DEVICE                 *Device,\r
+  IN  EFI_LBA                   StartLba,\r
+  IN  EFI_BLOCK_IO2_TOKEN       *Token,\r
+  IN  BOOLEAN                   IsEnd\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_SD_MMC_PASS_THRU_PROTOCOL        *PassThru;\r
+  SD_REQUEST                           *EraseBlockStart;\r
+  EFI_TPL                              OldTpl;\r
+\r
+  EraseBlockStart = NULL;\r
+  PassThru        = Device->Private->PassThru;\r
+\r
+  EraseBlockStart = AllocateZeroPool (sizeof (SD_REQUEST));\r
+  if (EraseBlockStart == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+\r
+  EraseBlockStart->Signature = SD_REQUEST_SIGNATURE;\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  InsertTailList (&Device->Queue, &EraseBlockStart->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
+  EraseBlockStart->Packet.SdMmcCmdBlk    = &EraseBlockStart->SdMmcCmdBlk;\r
+  EraseBlockStart->Packet.SdMmcStatusBlk = &EraseBlockStart->SdMmcStatusBlk;\r
+  EraseBlockStart->Packet.Timeout        = SD_GENERIC_TIMEOUT;\r
+\r
+  EraseBlockStart->SdMmcCmdBlk.CommandIndex = SD_ERASE_WR_BLK_START;\r
+  EraseBlockStart->SdMmcCmdBlk.CommandType  = SdMmcCommandTypeAc;\r
+  EraseBlockStart->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;\r
+\r
+  if (Device->SectorAddressing) {\r
+    EraseBlockStart->SdMmcCmdBlk.CommandArgument = (UINT32)StartLba;\r
+  } else {\r
+    EraseBlockStart->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (StartLba, Device->BlockMedia.BlockSize);\r
+  }\r
+\r
+  EraseBlockStart->IsEnd = IsEnd;\r
+  EraseBlockStart->Token = Token;\r
+\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Status = gBS->CreateEvent (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_CALLBACK,\r
+                    AsyncIoCallback,\r
+                    EraseBlockStart,\r
+                    &EraseBlockStart->Event\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
+  } else {\r
+    EraseBlockStart->Event = NULL;\r
+  }\r
+\r
+  Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlockStart->Packet, EraseBlockStart->Event);\r
+\r
+Error:\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    //\r
+    // For asynchronous operation, only free request and event in error case.\r
+    // The request and event will be freed in asynchronous callback for success case.\r
+    //\r
+    if (EFI_ERROR (Status) && (EraseBlockStart != NULL)) {\r
+      RemoveEntryList (&EraseBlockStart->Link);\r
+      if (EraseBlockStart->Event != NULL) {\r
+        gBS->CloseEvent (EraseBlockStart->Event);\r
+      }\r
+      FreePool (EraseBlockStart);\r
+    }\r
+  } else {\r
+    //\r
+    // For synchronous operation, free request whatever the execution result is.\r
+    //\r
+    if (EraseBlockStart != NULL) {\r
+      RemoveEntryList (&EraseBlockStart->Link);\r
+      FreePool (EraseBlockStart);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Set the erase end address through sync or async I/O request.\r
+\r
+  @param[in]  Device            A pointer to the SD_DEVICE instance.\r
+  @param[in]  EndLba            The ending logical block address to be erased.\r
+  @param[in]  Token             A pointer to the token associated with the transaction.\r
+  @param[in]  IsEnd             A boolean to show whether it's the last cmd in a series of cmds.\r
+                                This parameter is only meaningful in async I/O request.\r
+\r
+  @retval EFI_SUCCESS           The request is executed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be executed due to a lack of resources.\r
+  @retval Others                The request could not be executed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SdEraseBlockEnd (\r
+  IN  SD_DEVICE                 *Device,\r
+  IN  EFI_LBA                   EndLba,\r
+  IN  EFI_BLOCK_IO2_TOKEN       *Token,\r
+  IN  BOOLEAN                   IsEnd\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_SD_MMC_PASS_THRU_PROTOCOL        *PassThru;\r
+  SD_REQUEST                           *EraseBlockEnd;\r
+  EFI_TPL                              OldTpl;\r
+\r
+  EraseBlockEnd = NULL;\r
+  PassThru      = Device->Private->PassThru;\r
+\r
+  EraseBlockEnd = AllocateZeroPool (sizeof (SD_REQUEST));\r
+  if (EraseBlockEnd == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+\r
+  EraseBlockEnd->Signature = SD_REQUEST_SIGNATURE;\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  InsertTailList (&Device->Queue, &EraseBlockEnd->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
+  EraseBlockEnd->Packet.SdMmcCmdBlk    = &EraseBlockEnd->SdMmcCmdBlk;\r
+  EraseBlockEnd->Packet.SdMmcStatusBlk = &EraseBlockEnd->SdMmcStatusBlk;\r
+  EraseBlockEnd->Packet.Timeout        = SD_GENERIC_TIMEOUT;\r
+\r
+  EraseBlockEnd->SdMmcCmdBlk.CommandIndex = SD_ERASE_WR_BLK_END;\r
+  EraseBlockEnd->SdMmcCmdBlk.CommandType  = SdMmcCommandTypeAc;\r
+  EraseBlockEnd->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;\r
+\r
+  if (Device->SectorAddressing) {\r
+    EraseBlockEnd->SdMmcCmdBlk.CommandArgument = (UINT32)EndLba;\r
+  } else {\r
+    EraseBlockEnd->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (EndLba, Device->BlockMedia.BlockSize);\r
+  }\r
+\r
+  EraseBlockEnd->IsEnd = IsEnd;\r
+  EraseBlockEnd->Token = Token;\r
+\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Status = gBS->CreateEvent (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_CALLBACK,\r
+                    AsyncIoCallback,\r
+                    EraseBlockEnd,\r
+                    &EraseBlockEnd->Event\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
+  } else {\r
+    EraseBlockEnd->Event = NULL;\r
+  }\r
+\r
+  Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlockEnd->Packet, EraseBlockEnd->Event);\r
+\r
+Error:\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    //\r
+    // For asynchronous operation, only free request and event in error case.\r
+    // The request and event will be freed in asynchronous callback for success case.\r
+    //\r
+    if (EFI_ERROR (Status) && (EraseBlockEnd != NULL)) {\r
+      RemoveEntryList (&EraseBlockEnd->Link);\r
+      if (EraseBlockEnd->Event != NULL) {\r
+        gBS->CloseEvent (EraseBlockEnd->Event);\r
+      }\r
+      FreePool (EraseBlockEnd);\r
+    }\r
+  } else {\r
+    //\r
+    // For synchronous operation, free request whatever the execution result is.\r
+    //\r
+    if (EraseBlockEnd != NULL) {\r
+      RemoveEntryList (&EraseBlockEnd->Link);\r
+      FreePool (EraseBlockEnd);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Erase specified blocks through sync or async I/O request.\r
+\r
+  @param[in]  Device            A pointer to the SD_DEVICE instance.\r
+  @param[in]  Token             A pointer to the token associated with the transaction.\r
+  @param[in]  IsEnd             A boolean to show whether it's the last cmd in a series of cmds.\r
+                                This parameter is only meaningful in async I/O request.\r
+\r
+  @retval EFI_SUCCESS           The request is executed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be executed due to a lack of resources.\r
+  @retval Others                The request could not be executed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+SdEraseBlock (\r
+  IN  SD_DEVICE                 *Device,\r
+  IN  EFI_BLOCK_IO2_TOKEN       *Token,\r
+  IN  BOOLEAN                   IsEnd\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_SD_MMC_PASS_THRU_PROTOCOL        *PassThru;\r
+  SD_REQUEST                           *EraseBlock;\r
+  EFI_TPL                              OldTpl;\r
+\r
+  EraseBlock = NULL;\r
+  PassThru   = Device->Private->PassThru;\r
+\r
+  EraseBlock = AllocateZeroPool (sizeof (SD_REQUEST));\r
+  if (EraseBlock == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+\r
+  EraseBlock->Signature = SD_REQUEST_SIGNATURE;\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  InsertTailList (&Device->Queue, &EraseBlock->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
+  EraseBlock->Packet.SdMmcCmdBlk    = &EraseBlock->SdMmcCmdBlk;\r
+  EraseBlock->Packet.SdMmcStatusBlk = &EraseBlock->SdMmcStatusBlk;\r
+  EraseBlock->Packet.Timeout        = SD_GENERIC_TIMEOUT;\r
+\r
+  EraseBlock->SdMmcCmdBlk.CommandIndex = SD_ERASE;\r
+  EraseBlock->SdMmcCmdBlk.CommandType  = SdMmcCommandTypeAc;\r
+  EraseBlock->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b;\r
+\r
+  EraseBlock->IsEnd = IsEnd;\r
+  EraseBlock->Token = Token;\r
+\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Status = gBS->CreateEvent (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_CALLBACK,\r
+                    AsyncIoCallback,\r
+                    EraseBlock,\r
+                    &EraseBlock->Event\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
+  } else {\r
+    EraseBlock->Event = NULL;\r
+  }\r
+\r
+  Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlock->Packet, EraseBlock->Event);\r
+\r
+Error:\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    //\r
+    // For asynchronous operation, only free request and event in error case.\r
+    // The request and event will be freed in asynchronous callback for success case.\r
+    //\r
+    if (EFI_ERROR (Status) && (EraseBlock != NULL)) {\r
+      RemoveEntryList (&EraseBlock->Link);\r
+      if (EraseBlock->Event != NULL) {\r
+        gBS->CloseEvent (EraseBlock->Event);\r
+      }\r
+      FreePool (EraseBlock);\r
+    }\r
+  } else {\r
+    //\r
+    // For synchronous operation, free request whatever the execution result is.\r
+    //\r
+    if (EraseBlock != NULL) {\r
+      RemoveEntryList (&EraseBlock->Link);\r
+      FreePool (EraseBlock);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Erase a specified number of device blocks.\r
+\r
+  @param[in]       This           Indicates a pointer to the calling context.\r
+  @param[in]       MediaId        The media ID that the erase request is for.\r
+  @param[in]       Lba            The starting logical block address to be\r
+                                  erased. The caller is responsible for erasing\r
+                                  only legitimate locations.\r
+  @param[in, out]  Token          A pointer to the token associated with the\r
+                                  transaction.\r
+  @param[in]       Size           The size in bytes to be erased. This must be\r
+                                  a multiple of the physical block size of the\r
+                                  device.\r
+\r
+  @retval EFI_SUCCESS             The erase request was queued if Event is not\r
+                                  NULL. The data was erased correctly to the\r
+                                  device if the Event is NULL.to the device.\r
+  @retval EFI_WRITE_PROTECTED     The device cannot be erased due to write\r
+                                  protection.\r
+  @retval EFI_DEVICE_ERROR        The device reported an error while attempting\r
+                                  to perform the erase operation.\r
+  @retval EFI_INVALID_PARAMETER   The erase request contains LBAs that are not\r
+                                  valid.\r
+  @retval EFI_NO_MEDIA            There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED       The MediaId is not for the current media.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SdEraseBlocks (\r
+  IN     EFI_ERASE_BLOCK_PROTOCOL      *This,\r
+  IN     UINT32                        MediaId,\r
+  IN     EFI_LBA                       Lba,\r
+  IN OUT EFI_ERASE_BLOCK_TOKEN         *Token,\r
+  IN     UINTN                         Size\r
+  )\r
+{\r
+  EFI_STATUS                            Status;\r
+  EFI_BLOCK_IO_MEDIA                    *Media;\r
+  UINTN                                 BlockSize;\r
+  UINTN                                 BlockNum;\r
+  EFI_LBA                               LastLba;\r
+  SD_DEVICE                             *Device;\r
+\r
+  Status = EFI_SUCCESS;\r
+  Device = SD_DEVICE_DATA_FROM_ERASEBLK (This);\r
+  Media  = &Device->BlockMedia;\r
+\r
+  if (MediaId != Media->MediaId) {\r
+    return EFI_MEDIA_CHANGED;\r
+  }\r
+\r
+  if (Media->ReadOnly) {\r
+    return EFI_WRITE_PROTECTED;\r
+  }\r
+\r
+  //\r
+  // Check parameters.\r
+  //\r
+  BlockSize = Media->BlockSize;\r
+  if ((Size % BlockSize) != 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  BlockNum  = Size / BlockSize;\r
+  if ((Lba + BlockNum - 1) > Media->LastBlock) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Token->TransactionStatus = EFI_SUCCESS;\r
+  }\r
+\r
+  LastLba = Lba + BlockNum - 1;\r
+\r
+  Status = SdEraseBlockStart (Device, Lba, (EFI_BLOCK_IO2_TOKEN*)Token, FALSE);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = SdEraseBlockEnd (Device, LastLba, (EFI_BLOCK_IO2_TOKEN*)Token, FALSE);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = SdEraseBlock (Device, (EFI_BLOCK_IO2_TOKEN*)Token, TRUE);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  DEBUG ((EFI_D_ERROR, "SdEraseBlocks(): Lba 0x%x BlkNo 0x%x Event %p with %r\n", Lba, BlockNum, Token->Event, Status));\r
+\r
+  return Status;\r
+}\r
 \r