public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Chris Co <Christopher.Co@microsoft.com>
To: "edk2-devel@lists.01.org" <edk2-devel@lists.01.org>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>,
	Leif Lindholm <leif.lindholm@linaro.org>,
	Michael D Kinney <michael.d.kinney@intel.com>
Subject: [PATCH edk2-platforms 02/27] Platform/Microsoft: Add SdMmc Dxe Driver
Date: Fri, 21 Sep 2018 08:25:54 +0000	[thread overview]
Message-ID: <20180921082542.35768-3-christopher.co@microsoft.com> (raw)
In-Reply-To: <20180921082542.35768-1-christopher.co@microsoft.com>

This SdMmc driver adds support to boot Windows from SD and eMMC.
It implements RPMB protocol support for eMMC, unique device path
creation for each slot on the SD bus, high speed modes,
eMMC bus width auto-detection, and support for high capacity
SDXC cards.

Derived from: EmbeddedPkg\Universal\MmcDxe

Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Leif Lindholm <leif.lindholm@linaro.org>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Christopher Co <christopher.co@microsoft.com>
---
 Platform/Microsoft/Drivers/SdMmcDxe/BlockIo.c    |  548 ++++++
 Platform/Microsoft/Drivers/SdMmcDxe/Debug.c      |  363 ++++
 Platform/Microsoft/Drivers/SdMmcDxe/Protocol.c   | 1775 ++++++++++++++++++++
 Platform/Microsoft/Drivers/SdMmcDxe/Protocol.h   |  231 +++
 Platform/Microsoft/Drivers/SdMmcDxe/RpmbIo.c     |  609 +++++++
 Platform/Microsoft/Drivers/SdMmcDxe/SdMmc.c      |  886 ++++++++++
 Platform/Microsoft/Drivers/SdMmcDxe/SdMmc.h      |  529 ++++++
 Platform/Microsoft/Drivers/SdMmcDxe/SdMmcDxe.inf |   49 +
 Platform/Microsoft/Drivers/SdMmcDxe/SdMmcHw.h    |  505 ++++++
 Platform/Microsoft/Include/Protocol/RpmbIo.h     |  268 +++
 Platform/Microsoft/Include/Protocol/Sdhc.h       |  197 +++
 11 files changed, 5960 insertions(+)

diff --git a/Platform/Microsoft/Drivers/SdMmcDxe/BlockIo.c b/Platform/Microsoft/Drivers/SdMmcDxe/BlockIo.c
new file mode 100644
index 000000000000..cf3ab1a44a10
--- /dev/null
+++ b/Platform/Microsoft/Drivers/SdMmcDxe/BlockIo.c
@@ -0,0 +1,548 @@
+/** @file
+*
+*  Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+*  Copyright (c) 2011-2014, ARM Limited. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#include <Uefi.h>
+
+#include <Protocol/BlockIo.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/RpmbIo.h>
+#include <Protocol/Sdhc.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/TimerLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include "SdMmcHw.h"
+#include "SdMmc.h"
+#include "Protocol.h"
+
+VOID
+BenchmarkIo (
+  IN EFI_BLOCK_IO_PROTOCOL  *This,
+  IN SD_TRANSFER_DIRECTION  TransferDirection,
+  IN UINT32                 MediaId,
+  IN UINTN                  BufferSize,
+  IN UINT32                 Iterations
+  );
+
+EFI_STATUS
+IoBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL  *This,
+  IN SD_TRANSFER_DIRECTION  TransferDirection,
+  IN UINT32                 MediaId,
+  IN EFI_LBA                Lba,
+  IN UINTN                  BufferSize,
+  IN OUT VOID               *Buffer
+  );
+
+VOID
+SortIoReadStatsByTotalTransferTime (
+  IN IoReadStatsEntry*   Table,
+  IN UINT32              NumEntries
+  )
+{
+  // Using the simple insertion sort
+  UINT32 Idx;
+  UINT32 J;
+
+  for (Idx = 1; Idx < NumEntries; ++Idx) {
+    IoReadStatsEntry CurrEntry = Table[Idx];
+    J = Idx;
+    while (J > 0 &&
+           Table[J - 1].TotalTransferTimeUs < CurrEntry.TotalTransferTimeUs) {
+      Table[J] = Table[J - 1];
+      --J;
+    }
+    Table[J] = CurrEntry;
+  }
+}
+
+VOID
+BenchmarkIo (
+  IN EFI_BLOCK_IO_PROTOCOL  *This,
+  IN SD_TRANSFER_DIRECTION  TransferDirection,
+  IN UINT32                 MediaId,
+  IN UINTN                  BufferSize,
+  IN UINT32                 Iterations
+  )
+{
+  ASSERT (Iterations > 0);
+
+  VOID        *Buffer;
+  UINT32      BufferSizeKB;
+  UINT32      CurrIteration;
+  UINT64      EndTime;
+  UINT32      KBps;
+  UINT64      StartTime;
+  EFI_STATUS  Status;
+  UINT64      TotalTransfersTimeUs;
+
+  BufferSizeKB = INT_DIV_ROUND (BufferSize, 1024);
+  Buffer = AllocateZeroPool (BufferSize);
+  if (Buffer == NULL) {
+    LOG_ERROR ("BenchmarkIo() : No enough memory to allocate %dKB buffer", BufferSizeKB);
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Exit;
+  }
+
+  CurrIteration = Iterations;
+  TotalTransfersTimeUs = 0;
+
+  while (CurrIteration--) {
+    StartTime = GetPerformanceCounter ();
+    Status = IoBlocks (
+      This,
+      TransferDirection,
+      MediaId,
+      0, // Lba
+      BufferSize,
+      Buffer
+    );
+    if (EFI_ERROR (Status)) {
+      goto Exit;
+    }
+
+    EndTime = GetPerformanceCounter ();
+    TotalTransfersTimeUs += (((EndTime - StartTime) * 1000000UL) / gHpcTicksPerSeconds);
+  }
+
+  KBps = (UINT32) (((UINT64) BufferSizeKB * (UINT64) Iterations * 1000000UL) / TotalTransfersTimeUs);
+  LOG_INFO (
+    "- BenchmarkIo(%a, %dKB)\t: Xfr Avg:%dus\t%dKBps\t%dMBps",
+    (TransferDirection == SdTransferDirectionRead) ? "Read" : "Write",
+    BufferSizeKB,
+    (UINT32) (TotalTransfersTimeUs / Iterations),
+    KBps,
+    INT_DIV_ROUND (KBps, 1024));
+
+Exit:
+  if (Buffer != NULL) {
+    FreePool (Buffer);
+  }
+}
+
+EFI_STATUS
+IoBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL  *This,
+  IN SD_TRANSFER_DIRECTION  TransferDirection,
+  IN UINT32                 MediaId,
+  IN EFI_LBA                Lba,
+  IN UINTN                  BufferSize,
+  IN OUT VOID               *Buffer
+  )
+{
+  CONST SD_COMMAND  *Cmd;
+  VOID              *CurrentBuffer;
+  SDHC_INSTANCE     *HostInst;
+  UINT32            BlockCount;
+  UINT32            BytesRemaining;
+  UINTN             CurrentBufferSize;
+  UINT32            CurrentLba;
+  UINT32            Retry;
+  EFI_STATUS        Status;
+
+  HostInst = SDHC_INSTANCE_FROM_BLOCK_IO_THIS (This);
+  ASSERT (HostInst);
+  ASSERT (HostInst->HostExt);
+
+  if (This->Media->MediaId != MediaId) {
+    Status = EFI_MEDIA_CHANGED;
+    goto Exit;
+  }
+
+  if (Buffer == NULL) {
+    Status = EFI_INVALID_PARAMETER;
+    goto Exit;
+  }
+
+  // Check if a Card is Present
+  if (!HostInst->BlockIo.Media->MediaPresent) {
+    Status = EFI_NO_MEDIA;
+    goto Exit;
+  }
+
+  // Reading 0 Byte is valid
+  if (BufferSize == 0) {
+    Status = EFI_SUCCESS;
+    goto Exit;
+  }
+
+  if ((TransferDirection == SdTransferDirectionWrite) && (This->Media->ReadOnly == TRUE)) {
+    Status = EFI_WRITE_PROTECTED;
+    goto Exit;
+  }
+
+    // The buffer size must be an exact multiple of the block size
+  if ((BufferSize % This->Media->BlockSize) != 0) {
+    Status = EFI_BAD_BUFFER_SIZE;
+    goto Exit;
+  }
+
+  BlockCount = BufferSize / This->Media->BlockSize;
+
+  // All blocks must be within the device
+  if ((Lba + BlockCount - 1) > (This->Media->LastBlock)) {
+    LOG_ERROR (
+      "Data span is out of media address range. (Media Last Block LBA = 0x%lx"
+      "Data Last Block LBA = 0x%lx)",
+      This->Media->LastBlock,
+      (Lba + BlockCount - 1));
+
+    Status = EFI_INVALID_PARAMETER;
+    goto Exit;
+  }
+
+  // Check the alignment
+  if ((This->Media->IoAlign > 2) && (((UINTN) Buffer & (This->Media->IoAlign - 1)) != 0)) {
+    LOG_ERROR (
+      "Invalid buffer address alignment (Buffer = %p, IoAlign = %d)",
+      Buffer,
+      This->Media->IoAlign);
+
+    Status = EFI_INVALID_PARAMETER;
+    goto Exit;
+  }
+
+  if (TransferDirection == SdTransferDirectionRead) {
+    if (BlockCount == 1) {
+      Cmd = &CmdReadSingleBlock;
+    } else {
+      Cmd = &CmdReadMultiBlock;
+    }
+  } else {
+    if (BlockCount == 1) {
+      Cmd = &CmdWriteSingleBlock;
+    } else {
+      Cmd = &CmdWriteMultiBlock;
+    }
+  }
+
+  CONST UINT32 DataCommandRetryCount = 3;
+  CONST UINT32 MaxTransferSize =
+    HostInst->HostCapabilities.MaximumBlockCount * This->Media->BlockSize;
+  BytesRemaining = BufferSize;
+  CurrentBuffer = Buffer;
+  CurrentLba = (UINT32) Lba;
+
+  for (; BytesRemaining > 0;) {
+    if (BytesRemaining < MaxTransferSize) {
+      CurrentBufferSize = BytesRemaining;
+    } else {
+      CurrentBufferSize = MaxTransferSize;
+    }
+
+    for (Retry = 0; Retry < DataCommandRetryCount; ++Retry) {
+      Status = SdhcSendDataCommand (
+        HostInst,
+        Cmd,
+        CurrentLba,
+        CurrentBufferSize,
+        CurrentBuffer);
+
+      if (!EFI_ERROR (Status)) {
+        if (Retry > 0) {
+          LOG_TRACE ("SdhcSendDataCommand succeeded after %d retry", Retry);
+        }
+        break;
+      }
+
+      // On SdhcSendDataCommand return, proper error recovery has been performed
+      // and it should be safe to retry the same transfer.
+      LOG_ERROR ("SdhcSendDataCommand failed on retry %d", Retry);
+    }
+
+    BytesRemaining -= CurrentBufferSize;
+    CurrentLba += CurrentBufferSize / This->Media->BlockSize;
+    CurrentBuffer = (VOID*) ((UINTN) CurrentBuffer + CurrentBufferSize);
+  }
+
+Exit:
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR (
+      "IoBlocks(%c, LBA:0x%08lx, Size(B):0x%x): SdhcSendDataCommand() failed. %r",
+      ((TransferDirection == SdTransferDirectionRead) ? 'R' : 'W'),
+      Lba,
+      BufferSize,
+      Status);
+  }
+
+  return Status;
+}
+
+// EFI_BLOCK_IO Protocol Callbacks
+
+/**
+  Reset the block device.
+
+  This function implements EFI_BLOCK_IO_PROTOCOL.Reset().
+  It resets the block device hardware.
+  ExtendedVerification is ignored in this implementation.
+
+  @param  This                   Indicates a pointer to the calling context.
+  @param  ExtendedVerification   Indicates that the driver may perform a more exhaustive
+                                 verification operation of the device during reset.
+
+  @retval EFI_SUCCESS            The block device was reset.
+  @retval EFI_DEVICE_ERROR       The block device is not functioning correctly and could not be reset.
+
+**/
+EFI_STATUS
+EFIAPI
+BlockIoReset (
+  IN EFI_BLOCK_IO_PROTOCOL  *This,
+  IN BOOLEAN                ExtendedVerification
+  )
+{
+  SDHC_INSTANCE   *HostInst;
+
+  LOG_TRACE ("BlockIoReset()");
+
+  HostInst = SDHC_INSTANCE_FROM_BLOCK_IO_THIS (This);
+  return SoftReset (HostInst);
+}
+
+/**
+  Reads the requested number of blocks from the device.
+
+  This function implements EFI_BLOCK_IO_PROTOCOL.ReadBlocks().
+  It reads the requested number of blocks from the device.
+  All the blocks are read, or an error is returned.
+
+  @param  This                   Indicates a pointer to the calling context.
+  @param  MediaId                The media ID that the read request is for.
+  @param  Lba                    The starting logical block address to read from on the device.
+  @param  BufferSize             The size of the Buffer in bytes.
+                                 This must be a multiple of the intrinsic block size of the device.
+  @param  Buffer                 A pointer to the destination buffer for the data. The caller is
+                                 responsible for either having implicit or explicit ownership of the buffer.
+
+  @retval EFI_SUCCESS            The data was read correctly from the device.
+  @retval EFI_DEVICE_ERROR       The device reported an error while attempting to perform the read operation.
+  @retval EFI_NO_MEDIA           There is no media in the device.
+  @retval EFI_MEDIA_CHANGED      The MediaId is not for the current media.
+  @retval EFI_BAD_BUFFER_SIZE    The BufferSize parameter is not a multiple of the intrinsic block size of the device.
+  @retval EFI_INVALID_PARAMETER  The read request contains LBAs that are not valid,
+                                 or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+BlockIoReadBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL  *This,
+  IN UINT32                 MediaId,
+  IN EFI_LBA                Lba,
+  IN UINTN                  BufferSize,
+  OUT VOID                  *Buffer
+  )
+{
+  LOG_TRACE ("BlockIoReadBlocks()");
+
+#if SDMMC_BENCHMARK_IO
+  STATIC BOOLEAN  BenchmarkDone;
+  UINT32          CurrByteSize;
+
+  BenchmarkDone = FALSE;
+  if (!BenchmarkDone) {
+
+    LOG_INFO ("Benchmarking BlockIo Read");
+
+    CurrByteSize = 512;
+    CONST UINT32 MaxByteSize = 8388608; // 8MB Max
+    for (; CurrByteSize <= MaxByteSize; CurrByteSize *= 2) {
+      BenchmarkIo (This, SdTransferDirectionRead, MediaId, CurrByteSize, 10);
+    }
+
+    BenchmarkDone = TRUE;
+  }
+#endif // SDMMC_BENCHMARK_IO
+
+#if SDMMC_COLLECT_STATISTICS
+  SDHC_INSTANCE   *HostInst;
+  UINT32          BlockIdx;
+  UINT64          EndTime;
+  UINT32          NumBlocks;
+  UINT64          StartTime;
+  EFI_STATUS      Status;
+  UINT32          TotalReadBlocksCount;
+  UINT32          TotalReadTimeUs;
+
+  NumBlocks = BufferSize / This->Media->BlockSize;
+  HostInst = SDHC_INSTANCE_FROM_BLOCK_IO_THIS (This);
+  CONST UINT32 TableSize = ARRAYSIZE (HostInst->IoReadStats);
+  IoReadStatsEntry *CurrentReadEntry = NULL;
+
+  for (BlockIdx = 0; BlockIdx < TableSize; ++BlockIdx) {
+    IoReadStatsEntry *Entry = HostInst->IoReadStats + BlockIdx;
+    // Reached end of table and didn't find a match, append an entry
+    if (Entry->NumBlocks == 0) {
+      ++HostInst->IoReadStatsNumEntries;
+      Entry->NumBlocks = NumBlocks;
+    }
+
+    if (Entry->NumBlocks == NumBlocks) {
+      CurrentReadEntry = Entry;
+      ++Entry->Count;
+      break;
+    }
+  }
+  ASSERT (BlockIdx < TableSize);
+
+  StartTime = GetPerformanceCounter ();
+
+  Status = IoBlocks (
+    This,
+    SdTransferDirectionRead,
+    MediaId,
+    Lba,
+    BufferSize,
+    Buffer
+  );
+  if (EFI_ERROR (Status)) {
+    goto Exit;
+  }
+
+  EndTime = GetPerformanceCounter ();
+
+  ASSERT (CurrentReadEntry != NULL);
+  CurrentReadEntry->TotalTransferTimeUs +=
+    (UINT32) (((EndTime - StartTime) * 1000000UL) / gHpcTicksPerSeconds);
+
+  // Run statistics and dump updates
+  SortIoReadStatsByTotalTransferTime (
+    HostInst->IoReadStats,
+    HostInst->IoReadStatsNumEntries);
+
+  IoReadStatsEntry *MaxNumBlocksEntry = HostInst->IoReadStats;
+  IoReadStatsEntry *MaxCountEntry = HostInst->IoReadStats;
+  TotalReadTimeUs = 0;
+  TotalReadBlocksCount = 0;
+
+  LOG_INFO (" #Blks\tCnt\tAvg(us)\tAll(us)");
+
+  for (BlockIdx = 0; BlockIdx < HostInst->IoReadStatsNumEntries; ++BlockIdx) {
+    IoReadStatsEntry *CurrEntry = HostInst->IoReadStats + BlockIdx;
+
+    if (CurrEntry->NumBlocks > MaxNumBlocksEntry->NumBlocks) {
+      MaxNumBlocksEntry = CurrEntry;
+    }
+
+    if (CurrEntry->Count > MaxCountEntry->Count) {
+      MaxCountEntry = CurrEntry;
+    }
+
+    TotalReadTimeUs += CurrEntry->TotalTransferTimeUs;
+    TotalReadBlocksCount += (CurrEntry->NumBlocks * CurrEntry->Count);
+
+    // Show only the top 5 time consuming transfers
+    if (BlockIdx < 5) {
+      LOG_INFO (
+        " %d\t%d\t%d\t%d",
+        (UINT32) CurrEntry->NumBlocks,
+        (UINT32) CurrEntry->Count,
+        (UINT32) (CurrEntry->TotalTransferTimeUs / CurrEntry->Count),
+        (UINT32) CurrEntry->TotalTransferTimeUs);
+    }
+  }
+
+  LOG_INFO (
+    "MaxNumBlocksEntry: %d %d %dus",
+    (UINT32) MaxNumBlocksEntry->NumBlocks,
+    (UINT32) MaxNumBlocksEntry->Count,
+    (UINT32) MaxNumBlocksEntry->TotalTransferTimeUs);
+
+  LOG_INFO (
+    "MaxCountEntry: %d %d %dus",
+    (UINT32) MaxCountEntry->NumBlocks,
+    (UINT32) MaxCountEntry->Count,
+    (UINT32) MaxCountEntry->TotalTransferTimeUs);
+
+  LOG_INFO (
+    "UEFI spent %dus~%ds reading %dMB from SDCard\n",
+    TotalReadTimeUs,
+    INT_DIV_ROUND (TotalReadTimeUs, 1000000),
+    INT_DIV_ROUND (TotalReadBlocksCount * This->Media->BlockSize, (1024 * 1024)));
+Exit:
+  return Status;
+
+#else
+  return IoBlocks (This, SdTransferDirectionRead, MediaId, Lba, BufferSize, Buffer);
+#endif // SDMMC_COLLECT_STATISTICS
+}
+
+/**
+  Writes a specified number of blocks to the device.
+
+  This function implements EFI_BLOCK_IO_PROTOCOL.WriteBlocks().
+  It writes a specified number of blocks to the device.
+  All blocks are written, or an error is returned.
+
+  @param  This                   Indicates a pointer to the calling context.
+  @param  MediaId                The media ID that the write request is for.
+  @param  Lba                    The starting logical block address to be written.
+  @param  BufferSize             The size of the Buffer in bytes.
+                                 This must be a multiple of the intrinsic block size of the device.
+  @param  Buffer                 Pointer to the source buffer for the data.
+
+  @retval EFI_SUCCESS            The data were written correctly to the device.
+  @retval EFI_WRITE_PROTECTED    The device cannot be written to.
+  @retval EFI_NO_MEDIA           There is no media in the device.
+  @retval EFI_MEDIA_CHANGED      The MediaId is not for the current media.
+  @retval EFI_DEVICE_ERROR       The device reported an error while attempting to perform the write operation.
+  @retval EFI_BAD_BUFFER_SIZE    The BufferSize parameter is not a multiple of the intrinsic
+                                 block size of the device.
+  @retval EFI_INVALID_PARAMETER  The write request contains LBAs that are not valid,
+                                 or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+BlockIoWriteBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL  *This,
+  IN UINT32                 MediaId,
+  IN EFI_LBA                Lba,
+  IN UINTN                  BufferSize,
+  IN VOID                   *Buffer
+  )
+{
+  LOG_TRACE ("BlockIoWriteBlocks()");
+
+  return IoBlocks (This, SdTransferDirectionWrite, MediaId, Lba, BufferSize, Buffer);
+}
+
+/**
+  Flushes all modified data to a physical block device.
+
+  @param  This                   Indicates a pointer to the calling context.
+
+  @retval EFI_SUCCESS            All outstanding data were written correctly to the device.
+  @retval EFI_DEVICE_ERROR       The device reported an error while attempting to write data.
+  @retval EFI_NO_MEDIA           There is no media in the device.
+
+**/
+EFI_STATUS
+EFIAPI
+BlockIoFlushBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL  *This
+  )
+{
+  LOG_TRACE ("BlockIoFlushBlocks()");
+
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Microsoft/Drivers/SdMmcDxe/Debug.c b/Platform/Microsoft/Drivers/SdMmcDxe/Debug.c
new file mode 100644
index 000000000000..79e56543a3ea
--- /dev/null
+++ b/Platform/Microsoft/Drivers/SdMmcDxe/Debug.c
@@ -0,0 +1,363 @@
+/** @file
+*
+*  Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+*  Copyright (c) 2011-2014, ARM Limited. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#include <Uefi.h>
+
+#include <Protocol/BlockIo.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/RpmbIo.h>
+#include <Protocol/Sdhc.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/TimerLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include "SdMmcHw.h"
+#include "SdMmc.h"
+#include "Protocol.h"
+
+CONST CHAR8 *mStrUnit[] = { "100kbit/s", "1Mbit/s", "10Mbit/s", "100MBit/s",
+                            "Unknown", "Unknown", "Unknown", "Unknown" };
+CONST CHAR8 *mStrValue[] = { "UNDEF", "1.0", "1.2", "1.3", "1.5", "2.0", "2.5", "3.0", "3.5", "4.0", "4.5", "5.0",
+                             "Unknown", "Unknown", "Unknown", "Unknown" };
+
+VOID
+PrintCid (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  MMC_CID   *MmcCid;
+  SD_CID    *SdCid;
+
+  LOG_INFO ("- PrintCid");;
+
+  if (HostInst->CardInfo.CardFunction == CardFunctionSd) {
+    SdCid = &HostInst->CardInfo.Registers.Sd.Cid;
+    LOG_INFO ("\t- Manufacturer ID: 0x%x", (UINT32) SdCid->MID);
+    LOG_INFO ("\t- OEM ID: 0x%x", (UINT32) SdCid->OID);
+    LOG_INFO (
+      "\t- Product name: %c%c%c%c%c",
+      SdCid->PNM[4],
+      SdCid->PNM[3],
+      SdCid->PNM[2],
+      SdCid->PNM[1],
+      SdCid->PNM[0]);
+
+    LOG_INFO (
+      "\t- Manufacturing date: %d/%d",
+      (UINT32) (SdCid->MDT & 0xF),
+      (UINT32) (((SdCid->MDT >> 4) & 0x3F) + 2000));
+    LOG_INFO ("\t- Product serial number: 0x%X", SdCid->PSN);
+    LOG_INFO ("\t- Product revision: %d", (UINT32) SdCid->PRV);
+
+    LOG_INFO ("\t- OEM ID: 0x%x", (UINT32) SdCid->OID);
+    LOG_INFO ("\t- Manufacturer ID: 0x%x", (UINT32) SdCid->MID);
+  } else {
+    MmcCid = &HostInst->CardInfo.Registers.Mmc.Cid;
+    LOG_INFO ("\t- Manufacturer ID: 0x%x", (UINT32) MmcCid->MID);
+    LOG_INFO ("\t- OEM ID: 0x%x", (UINT32) MmcCid->OID);
+    LOG_INFO (
+      "\t- Product name: %c%c%c%c%c%c\n",
+      MmcCid->PNM[5],
+      MmcCid->PNM[4],
+      MmcCid->PNM[3],
+      MmcCid->PNM[2],
+      MmcCid->PNM[1],
+      MmcCid->PNM[0]);
+
+    LOG_INFO (
+      "\t- Manufacturing date: %d/%d",
+      (UINT32) (MmcCid->MDT >> 4),
+      (UINT32) (MmcCid->MDT & 0xF) + 1997);
+
+    LOG_INFO ("\t- Product serial number: 0x%X", MmcCid->PSN);
+    LOG_INFO ("\t- Product revision: %d", (UINT32) MmcCid->PRV);
+  }
+}
+
+VOID
+PrintCsd (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  MMC_CSD   *MmcCsd;
+  MMC_EXT_CSD *MmcExtCsd;
+  SD_CSD    *SdCsd;
+  UINT32    CardSizeGB;
+
+  LOG_INFO ("- PrintCsd");
+  CardSizeGB =
+    (UINT32) INT_DIV_ROUND (HostInst->CardInfo.ByteCapacity, (UINT64) (1024 * 1024 * 1024));
+
+  LOG_INFO (
+    "\t- CardSize: %ld~%dGB, BlockSize:%d LastBlock LBA:0x%08x",
+    HostInst->CardInfo.ByteCapacity,
+    CardSizeGB,
+    HostInst->BlockIo.Media->BlockSize,
+    (UINT32) HostInst->BlockIo.Media->LastBlock);
+
+  if (HostInst->CardInfo.CardFunction == CardFunctionSd) {
+    SdCsd = &HostInst->CardInfo.Registers.Sd.Csd;
+    if (SdCsd->CSD_STRUCTURE == 0) {
+      LOG_INFO ("- SD CSD Version 1.01-1.10/Version 2.00/Standard Capacity");
+    } else if (SdCsd->CSD_STRUCTURE == 1) {
+      LOG_INFO ("- SD CSD Version 2.00/High Capacity");
+    } else {
+      LOG_INFO ("- SD CSD Version Higher than v3.3");
+    }
+
+    LOG_INFO (
+      "\t- SW Write Protect: Temp:%d Permanent:%d",
+      (UINT32) SdCsd->TMP_WRITE_PROTECT,
+      (UINT32) SdCsd->PERM_WRITE_PROTECT);
+
+    LOG_INFO ("\t- Supported card command class: 0x%X", SdCsd->CCC);
+
+    LOG_INFO (
+      "\t- Speed: %a %a (TRAN_SPEED:%x)",
+      mStrValue[(SdCsd->TRAN_SPEED >> 3) & 0xF],
+      mStrUnit[SdCsd->TRAN_SPEED & 7],
+      (UINT32) SdCsd->TRAN_SPEED);
+
+    LOG_INFO (
+      "\t- Maximum Read Data Block: %d (READ_BL_LEN:%x)",
+      (UINT32) (1 << SdCsd->READ_BL_LEN),
+      (UINT32) SdCsd->READ_BL_LEN);
+
+    LOG_INFO (
+      "\t- Maximum Write Data Block: %d (WRITE_BL_LEN:%x)",
+      (UINT32) (1 << SdCsd->WRITE_BL_LEN),
+      (UINT32) SdCsd->WRITE_BL_LEN);
+
+    if (!SdCsd->FILE_FORMAT_GRP) {
+      if (SdCsd->FILE_FORMAT == 0) {
+        LOG_INFO ("\t- Format (0): Hard disk-like file system with partition table");
+      } else if (SdCsd->FILE_FORMAT == 1) {
+        LOG_INFO ("\t- Format (1): DOS FAT (floppy-like) with boot sector only (no partition table)");
+      } else if (SdCsd->FILE_FORMAT == 2) {
+        LOG_INFO ("\t- Format (2): Universal File Format");
+      } else {
+        LOG_INFO ("\t- Format (3): Others/Unknown");
+      }
+    } else {
+      LOG_INFO ("\t- Format: Reserved");
+    }
+  } else {
+    MmcCsd = &HostInst->CardInfo.Registers.Mmc.Csd;
+    MmcExtCsd = &HostInst->CardInfo.Registers.Mmc.ExtCsd;
+
+    if (MmcExtCsd->CardType & MmcExtCsdCardTypeNormalSpeed) {
+      LOG_INFO ("\t- Normal-Speed MMC @ 26MHz");
+    }
+
+    if (MmcExtCsd->CardType & MmcExtCsdCardTypeHighSpeed) {
+      LOG_INFO ("\t- High-Speed MMC @ 52MHz");
+    }
+
+    if (MmcExtCsd->CardType & MmcExtCsdCardTypeDdr1v8) {
+      LOG_INFO ("\t- High-Speed DDR MMC @ 52MHz - 1.8V or 3V I/O");
+    }
+
+    if (MmcExtCsd->CardType & MmcExtCsdCardTypeDdr1v2) {
+      LOG_INFO ("\t- High-Speed DDR MMC @ 52MHz - 1.2VI/O");
+    }
+
+    LOG_INFO (
+      "\t- SW Write Protect: Temp:%d Permenant:%d",
+      (UINT32) MmcCsd->TMP_WRITE_PROTECT,
+      (UINT32) MmcCsd->PERM_WRITE_PROTECT);
+
+    LOG_INFO ("\t- SpecVersion: %d.x", (UINT32) MmcCsd->SPEC_VERS);
+    LOG_INFO ("\t- Supported card command class: 0x%X", MmcCsd->CCC);
+
+    LOG_INFO (
+      "\t- Current Max Speed: %a %a (TRAN_SPEED:%x)",
+      mStrValue[(MmcCsd->TRAN_SPEED >> 3) & 0xF],
+      mStrUnit[MmcCsd->TRAN_SPEED & 7],
+      (UINT32) MmcCsd->TRAN_SPEED);
+
+    LOG_INFO (
+      "\t- Maximum Read Data Block: %d (READ_BL_LEN:%x)",
+      (UINT32) (1 << MmcCsd->READ_BL_LEN),
+      (UINT32) MmcCsd->READ_BL_LEN);
+
+    LOG_INFO (
+      "\t- Maximum Write Data Block: %d (WRITE_BL_LEN:%x)",
+      (UINT32) (1 << MmcCsd->WRITE_BL_LEN),
+      (UINT32) MmcCsd->WRITE_BL_LEN);
+
+    if (!MmcCsd->FILE_FORMAT_GRP) {
+      if (MmcCsd->FILE_FORMAT == 0) {
+        LOG_INFO ("\t- Format (0): Hard disk-like file system with partition table");
+      } else if (MmcCsd->FILE_FORMAT == 1) {
+        LOG_INFO ("\t- Format (1): DOS FAT (floppy-like) with boot sector only (no partition table)");
+      } else if (MmcCsd->FILE_FORMAT == 2) {
+        LOG_INFO ("\t- Format (2): Universal File Format");
+      } else {
+        LOG_INFO ("\t- Format (3): Others/Unknown");
+      }
+    } else {
+      LOG_INFO ("\t- Format: Reserved");
+    }
+  }
+}
+
+VOID
+GetAndPrintCardStatus (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  CARD_STATUS CardStatus;
+  EFI_STATUS Status;
+
+  // If the card has not been selected, then we can't get its status
+  if (HostInst->CardInfo.RCA == 0x0) {
+    return;
+  }
+
+  Status = SdhcSendCommand (HostInst, &CmdSendStatus, 0);
+  if (EFI_ERROR (Status)) {
+    return;
+  }
+
+  CardStatus.AsUint32 = HostInst->CmdResponse[0];
+  PrintCardStatus (HostInst, CardStatus);
+}
+
+VOID
+PrintCardStatus (
+  IN SDHC_INSTANCE  *HostInst,
+  IN CARD_STATUS    CardStatus
+  )
+{
+  switch (HostInst->CardInfo.CardFunction) {
+  case CardFunctionSd:
+    LOG_INFO (
+      "Status: APP_CMD:%d READY_FOR_DATA:%d CURRENT_STATE:%d(%a) "
+      "CARD_IS_LOCKED:%d",
+      CardStatus.Fields.APP_CMD,
+      CardStatus.Fields.READY_FOR_DATA,
+      CardStatus.Fields.CURRENT_STATE,
+      CardStateToString (CardStatus.Fields.CURRENT_STATE),
+      CardStatus.Fields.CARD_IS_LOCKED);
+
+    break;
+
+  case CardFunctionMmc:
+    LOG_INFO (
+      "Status: APP_CMD:%d URGENT_BKOPS:%d READY_FOR_DATA:%d "
+      "CURRENT_STATE:%d(%a) CARD_IS_LOCKED:%d",
+      CardStatus.Fields.APP_CMD,
+      CardStatus.Fields.URGENT_BKOPS,
+      CardStatus.Fields.READY_FOR_DATA,
+      CardStatus.Fields.CURRENT_STATE,
+      CardStateToString (CardStatus.Fields.CURRENT_STATE),
+      CardStatus.Fields.CARD_IS_LOCKED);
+
+    break;
+
+  default:
+    ASSERT (FALSE);
+  }
+
+  if (IsCardStatusError (HostInst, CardStatus)) {
+    LOG_INFO ("Errors:");
+
+    if (HostInst->CardInfo.CardFunction == CardFunctionSd) {
+      if (CardStatus.Fields.AKE_SEQ_ERROR) {
+        LOG_INFO ("\t- SWITCH_ERROR");
+      }
+    }
+
+    if (HostInst->CardInfo.CardFunction == CardFunctionMmc) {
+      if (CardStatus.Fields.SWITCH_ERROR) {
+        LOG_INFO ("\t- SWITCH_ERROR");
+      }
+
+      if (CardStatus.Fields.OVERRUN) {
+        LOG_INFO ("\t- OVERRUN");
+      }
+
+      if (CardStatus.Fields.UNDERRUN) {
+        LOG_INFO ("\t- UNDERRUN");
+      }
+    }
+
+    if (CardStatus.Fields.ERASE_RESET) {
+      LOG_INFO ("\t- ERASE_RESET");
+    }
+
+    if (CardStatus.Fields.WP_ERASE_SKIP) {
+      LOG_INFO ("\t- WP_ERASE_SKIP");
+    }
+
+    if (CardStatus.Fields.CID_CSD_OVERWRITE) {
+      LOG_INFO ("\t- CID_CSD_OVERWRITE");
+    }
+
+    if (CardStatus.Fields.ERROR) {
+      LOG_INFO ("\t- ERROR");
+    }
+
+    if (CardStatus.Fields.CC_ERROR) {
+      LOG_INFO ("\t- CC_ERROR");
+    }
+
+    if (CardStatus.Fields.CARD_ECC_FAILED) {
+      LOG_INFO ("\t- CARD_ECC_FAILED");
+    }
+
+    if (CardStatus.Fields.ILLEGAL_COMMAND) {
+      LOG_INFO ("\t- ILLEGAL_COMMAND");
+    }
+
+    if (CardStatus.Fields.COM_CRC_ERROR) {
+      LOG_INFO ("\t- COM_CRC_ERROR");
+    }
+
+    if (CardStatus.Fields.LOCK_UNLOCK_FAILED) {
+      LOG_INFO ("\t- LOCK_UNLOCK_FAILED");
+    }
+
+    if (CardStatus.Fields.WP_VIOLATION) {
+      LOG_INFO ("\t- WP_VIOLATION");
+    }
+
+    if (CardStatus.Fields.ERASE_PARAM) {
+      LOG_INFO ("\t- ERASE_PARAM");
+    }
+
+    if (CardStatus.Fields.ERASE_SEQ_ERROR) {
+      LOG_INFO ("\t- ERASE_SEQ_ERROR");
+    }
+
+    if (CardStatus.Fields.BLOCK_LEN_ERROR) {
+      LOG_INFO ("\t- BLOCK_LEN_ERROR");
+    }
+
+    if (CardStatus.Fields.ADDRESS_MISALIGN) {
+      LOG_INFO ("\t- ADDRESS_MISALIGN");
+    }
+
+    if (CardStatus.Fields.ADDRESS_OUT_OF_RANGE) {
+      LOG_INFO ("\t- ADDRESS_OUT_OF_RANGE");
+    }
+  }
+}
diff --git a/Platform/Microsoft/Drivers/SdMmcDxe/Protocol.c b/Platform/Microsoft/Drivers/SdMmcDxe/Protocol.c
new file mode 100644
index 000000000000..0e14aa5301a2
--- /dev/null
+++ b/Platform/Microsoft/Drivers/SdMmcDxe/Protocol.c
@@ -0,0 +1,1775 @@
+/** @file
+*
+*  Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+*  Copyright (c) 2011-2014, ARM Limited. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#include <Uefi.h>
+
+#include <Protocol/BlockIo.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/RpmbIo.h>
+#include <Protocol/Sdhc.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/TimerLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include "SdMmcHw.h"
+#include "SdMmc.h"
+#include "Protocol.h"
+
+EFI_STATUS
+InitializeDevice (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  EFI_SDHC_PROTOCOL   *HostExt;
+  EFI_STATUS          Status;
+
+  LOG_TRACE ("InitializDevice()");
+  ASSERT (!HostInst->SlotInitialized);
+
+#if SDMMC_BENCHMARK_IO
+  UINT64              InitializationStartTime;
+  InitializationStartTime = HpcTimerStart ();
+#endif // SDMMC_BENCHMARK_IO
+
+  HostExt = HostInst->HostExt;
+  ASSERT (HostExt);
+  ASSERT (HostInst->BlockIo.Media->MediaPresent);
+
+  ZeroMem (&HostInst->CardInfo, sizeof (HostInst->CardInfo));
+
+  // SD/MMC cards on reset start in default normal speed mode
+  HostInst->CardInfo.CurrentSpeedMode = CardSpeedModeNormalSpeed;
+
+  Status = HostExt->SoftwareReset (HostExt, SdhcResetTypeAll);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("HostExt->SoftwareReset() failed. %r", Status);
+    return Status;
+  }
+
+  Status = HostExt->SetClock (HostExt, SD_IDENT_MODE_CLOCK_FREQ_HZ);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("HostExt->SetClock() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcQueryCardType (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcQueryCardType() failed. %r", Status);
+    return Status;
+  }
+
+  switch (HostInst->CardInfo.CardFunction) {
+  case CardFunctionComboSdSdio:
+    LOG_ERROR ("Combo SD/SDIO function is not supported");
+    return EFI_UNSUPPORTED;
+  case CardFunctionSdio:
+    LOG_ERROR ("SDIO function is not supported");
+    return EFI_UNSUPPORTED;
+  case CardFunctionSd:
+    Status = InitializeSdDevice (HostInst);
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR ("InitializeSdDevice() failed. %r", Status);
+      return Status;
+    }
+    break;
+  case CardFunctionMmc:
+    Status = InitializeMmcDevice (HostInst);
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR ("InitializeMmcDevice() failed. %r", Status);
+      return Status;
+    }
+    break;
+  default:
+    LOG_ASSERT ("Unknown device function");
+    return EFI_UNSUPPORTED;
+  }
+
+  PrintCid (HostInst);
+  PrintCsd (HostInst);
+
+  Status = SdhcSetBlockLength (HostInst, SD_BLOCK_LENGTH_BYTES);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSetBlockLength() failed. %r", Status);
+    return Status;
+  }
+
+  ASSERT (HostInst->CardInfo.CardFunction == CardFunctionSd ||
+          HostInst->CardInfo.CardFunction == CardFunctionMmc);
+
+  if (HostInst->CardInfo.CardFunction == CardFunctionSd) {
+    HostInst->BlockIo.Media->RemovableMedia = TRUE;
+    HostInst->BlockIo.Media->MediaId = HostInst->CardInfo.Registers.Sd.Cid.PSN;
+  } else {
+    // Assume BGA form factor eMMC
+    HostInst->BlockIo.Media->RemovableMedia = FALSE;
+
+    // Use the card product serial number as MediaId
+    HostInst->BlockIo.Media->MediaId = HostInst->CardInfo.Registers.Mmc.Cid.PSN;
+  }
+
+  HostInst->SlotInitialized = TRUE;
+
+#if SDMMC_BENCHMARK_IO
+  UINT64 ElapsedTimeMs;
+  ElapsedTimeMs = HpcTimerElapsedMilliseconds (InitializationStartTime);
+  LOG_INFO ("Initialization completed in %ldms", ElapsedTimeMs);
+#endif // SDMMC_BENCHMARK_IO
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+InitializeSdDevice (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  EFI_STATUS Status;
+
+  Status = SdhcSendOpCondSd (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSendOpCondSd() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcSendCidAll (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSendCidAll() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcSendRelativeAddr (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSendRelativeAddr() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcSendCid (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSendCid() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcSendCsd (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSendCsd() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcSelectDevice (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSelectDevice() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcSwitchBusWidthSd (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSwitchBusWidthSd() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcSwitchSpeedModeSd (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSwitchSpeedModeSd() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcSetMaxClockFrequency (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSetMaxClockFrequency() failed. %r", Status);
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+InitializeMmcDevice (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  EFI_STATUS Status;
+
+  Status = SdhcSendCidAll (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSendCidAll() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcSendRelativeAddr (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSendRelativeAddr() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcSendCid (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSendCid() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcSendCsd (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSendCsd() failed. %r", Status);
+    return Status;
+  }
+
+  if (HostInst->CardInfo.Registers.Mmc.Csd.SPEC_VERS < MMC_MIN_SUPPORTED_SPEC_VERS) {
+    LOG_ERROR (
+      "MMC %d.x is not supported, Minimum supported is MMC %d.x",
+      HostInst->CardInfo.Registers.Mmc.Csd.SPEC_VERS,
+      MMC_MIN_SUPPORTED_SPEC_VERS);
+    return EFI_UNSUPPORTED;
+  }
+
+  Status = SdhcSelectDevice (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSelectDevice() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcSendExtCsdMmc (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSendExtCsdMmc() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcSwitchSpeedModeMmc (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSwitchSpeedModeMmc() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcSwitchBusWidthMmc (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSwitchBusWidthMmc() failed. %r", Status);
+    return Status;
+  }
+
+  Status = SdhcSetMaxClockFrequency (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSetMaxClockFrequency() failed. %r", Status);
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcRecoverFromErrors (
+  IN SDHC_INSTANCE      *HostInst,
+  IN CONST SD_COMMAND   *Cmd
+  )
+{
+  EFI_SDHC_PROTOCOL   *HostExt;
+  CARD_STATUS         CardStatus;
+  EFI_STATUS          Status;
+
+  LOG_TRACE (
+    "*** %cCMD%d Error recovery sequence start ***",
+    (Cmd->Class == SdCommandClassApp ? 'A' : ' '),
+    (UINT32) Cmd->Index);
+
+  HostExt = HostInst->HostExt;
+  HostInst->ErrorRecoveryAttemptCount += 1;
+
+  if (HostInst->ErrorRecoveryAttemptCount > SDMMC_ERROR_RECOVERY_ATTEMPT_THRESHOLD) {
+    LOG_ERROR ("RecursiveErrorRecoveryCount exceeded the threshold. Error is unrecoverable!");
+    Status = EFI_PROTOCOL_ERROR;
+    goto Exit;
+  }
+
+  GetAndPrintCardStatus (HostInst);
+
+  LOG_TRACE ("Reseting CMD line ...");
+  Status = HostExt->SoftwareReset (HostExt, SdhcResetTypeCmd);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("HostExt->SoftwareReset(SdhcResetTypeCmd) failed. %r", Status);
+  }
+
+  if (Cmd->TransferType != SdTransferTypeNone &&
+      Cmd->TransferType != SdTransferTypeUndefined) {
+
+    LOG_TRACE ("Reseting DAT line ...");
+    Status = HostExt->SoftwareReset (HostExt, SdhcResetTypeData);
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR ("HostExt->SoftwareReset(SdhcResetTypeData) failed. %r", Status);
+    }
+  }
+
+  if (EFI_ERROR (Status)) {
+    LOG_TRACE ("CMD and/or DATA normal error recovery failed, trying SDHC soft-reset");
+    Status = SoftReset (HostInst);
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR ("All trials to recover from errors failed. %r");
+      goto Exit;
+    }
+  }
+
+  if (CmdsAreEqual (Cmd, &CmdWriteMultiBlock) ||
+      CmdsAreEqual (Cmd, &CmdReadMultiBlock)) {
+
+    Status = SdhcSendStatus (HostInst, &CardStatus);
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR ("SdhcSendStatus() failed. (Status = %r)", Status);
+      goto Exit;
+    }
+
+    if (CardStatus.Fields.CURRENT_STATE != CardStateTran) {
+      LOG_TRACE ("Stopping transmission ...");
+      Status = SdhcStopTransmission (HostInst);
+      if (EFI_ERROR (Status)) {
+        goto Exit;
+      }
+
+      // Multi read/write failure reason will be written in the STOP_TRANSMISSION
+      // response as part of the card status error flags.
+      CardStatus.AsUint32 = HostInst->CmdResponse[0];
+      PrintCardStatus (HostInst, CardStatus);
+    }
+  }
+
+Exit:
+
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR (
+      "*** %cCMD%d Error recovery sequence failed "
+      "(Status = %r, ErrorRecoveryAttemptCount = %d) ***",
+      (Cmd->Class == SdCommandClassApp ? 'A' : ' '),
+      (UINT32) Cmd->Index,
+      Status,
+      HostInst->ErrorRecoveryAttemptCount);
+
+  } else {
+    LOG_TRACE (
+      "*** %cCMD%d Error recovery sequence completed successfully "
+      "(ErrorRecoveryAttemptCount = %d) ***",
+      (Cmd->Class == SdCommandClassApp ? 'A' : ' '),
+      (UINT32) Cmd->Index,
+      HostInst->ErrorRecoveryAttemptCount);
+  }
+
+  HostInst->ErrorRecoveryAttemptCount -= 1;
+
+  return Status;
+}
+
+EFI_STATUS
+SdhcSendCommandHelper (
+  IN SDHC_INSTANCE        *HostInst,
+  IN CONST SD_COMMAND     *Cmd,
+  IN UINT32               Arg,
+  IN SD_COMMAND_XFR_INFO  *XfrInfo
+  )
+{
+  EFI_SDHC_PROTOCOL   *HostExt;
+  CARD_STATUS         CardStatus;
+  UINT32              CmdAppArg;
+  EFI_STATUS          Status;
+
+  HostExt = HostInst->HostExt;
+  if (Cmd->Class == SdCommandClassApp) {
+    CmdAppArg = HostInst->CardInfo.RCA << 16;
+    Status = HostExt->SendCommand (HostExt, &CmdAppSd, CmdAppArg, NULL);
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR (
+        "HostExt->SendCommand(%cCMD%d, 0x%08x) failed. %r",
+        (Cmd->Class == SdCommandClassApp ? 'A' : ' '),
+        (UINT32) Cmd->Index,
+        CmdAppArg,
+        Status);
+
+      return Status;
+    }
+  }
+
+  Status = HostExt->SendCommand (HostExt, Cmd, Arg, XfrInfo);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR (
+      "HostExt->SendCommand(%cCMD%d, 0x%08x) failed. %r",
+      (Cmd->Class == SdCommandClassApp ? 'A' : ' '),
+      (UINT32) Cmd->Index,
+      Arg,
+      Status);
+
+    return Status;
+  }
+
+  Status = HostExt->ReceiveResponse (HostExt, Cmd, HostInst->CmdResponse);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("HostExt->ReceiveResponse() failed. %r", Status);
+    return Status;
+  }
+
+  if ((Cmd->ResponseType == SdResponseTypeR1) ||
+      (Cmd->ResponseType == SdResponseTypeR1B)) {
+
+    CardStatus.AsUint32 = HostInst->CmdResponse[0];
+
+    if (IsCardStatusError (HostInst, CardStatus)) {
+      LOG_ERROR (
+        "%cCMD%d card status error detected",
+        (Cmd->Class == SdCommandClassApp ? 'A' : ' '),
+        (UINT32) Cmd->Index);
+
+      PrintCardStatus (HostInst, CardStatus);
+
+      return EFI_DEVICE_ERROR;
+    }
+  }
+
+  HostInst->PreLastSuccessfulCmd = HostInst->LastSuccessfulCmd;
+  HostInst->LastSuccessfulCmd = Cmd;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcSendCommand (
+  IN SDHC_INSTANCE      *HostInst,
+  IN CONST SD_COMMAND   *Cmd,
+  IN UINT32             Arg
+  )
+{
+  EFI_STATUS Status;
+
+  Status = SdhcSendCommandHelper (HostInst, Cmd, Arg, NULL);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("Send no-data command failed. %r", Status);
+    SdhcRecoverFromErrors (HostInst, Cmd);
+    return Status;
+  }
+
+  // SWITCH command can change card state to prog, we should wait the card to
+  // transfer back to tran state and rais the READY_FOR_DATA flag to make sure
+  // that switch operation was completed successfully
+  if (CmdsAreEqual (Cmd, &CmdSwitchMmc) ||
+      CmdsAreEqual (Cmd, &CmdSwitchSd)) {
+    Status = SdhcWaitForTranStateAndReadyForData (HostInst);
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR (
+        "SdhcWaitForTranStateAndReadyForData() failed after successful "
+        "SWITCH command. (Status = %r)",
+        Status);
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcSendDataCommand (
+  IN SDHC_INSTANCE      *HostInst,
+  IN CONST SD_COMMAND   *Cmd,
+  IN UINT32             Arg,
+  IN UINT32             BufferByteSize,
+  IN VOID               *Buffer
+  )
+{
+  EFI_SDHC_PROTOCOL     *HostExt;
+  EFI_STATUS            Status;
+  SD_COMMAND_XFR_INFO   XfrInfo;
+
+  HostExt = HostInst->HostExt;
+  ASSERT (BufferByteSize % SD_BLOCK_LENGTH_BYTES == 0);
+  XfrInfo.BlockCount = BufferByteSize / SD_BLOCK_LENGTH_BYTES;
+  XfrInfo.BlockSize = SD_BLOCK_LENGTH_BYTES;
+  XfrInfo.Buffer = Buffer;
+
+  Status = SdhcSendCommandHelper (HostInst, Cmd, Arg, &XfrInfo);
+  if (EFI_ERROR (Status)) {
+    goto Exit;
+  }
+
+  if (Cmd->TransferDirection == SdTransferDirectionRead) {
+    Status = HostExt->ReadBlockData (
+      HostExt,
+      BufferByteSize,
+      (UINT32*) Buffer);
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR (
+        "HostExt->ReadBlockData(Size: 0x%xB) failed. %r",
+        BufferByteSize,
+        Status);
+      goto Exit;
+    }
+  } else {
+    ASSERT (Cmd->TransferDirection == SdTransferDirectionWrite);
+    Status = HostExt->WriteBlockData (
+      HostExt,
+      BufferByteSize,
+      (UINT32*) Buffer);
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR (
+        "HostExt->WriteBlockData(Size: 0x%xB) failed. %r",
+        BufferByteSize,
+        Status);
+      goto Exit;
+    }
+  }
+
+  // If this is an open-ended multi-block read/write then explicitly send
+  // STOP_TRANSMISSION. A multi-block read/write with pre-defined block count
+  // will be preceeded with SET_BLOCK_COUNT.
+  if ((CmdsAreEqual (Cmd, &CmdWriteMultiBlock) ||
+       CmdsAreEqual (Cmd, &CmdReadMultiBlock)) &&
+       ((HostInst->LastSuccessfulCmd == NULL) ||
+        !CmdsAreEqual (HostInst->PreLastSuccessfulCmd, &CmdSetBlockCount))) {
+
+    Status = SdhcStopTransmission (HostInst);
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR ("SdhcStopTransmission() failed. (Status = %r)", Status);
+      goto Exit;
+    }
+  }
+
+  Status = SdhcWaitForTranStateAndReadyForData (HostInst);
+  if (EFI_ERROR (Status)) {
+    goto Exit;
+  }
+
+Exit:
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("Send data command failed. %r", Status);
+    SdhcRecoverFromErrors (HostInst, Cmd);
+  }
+
+  return Status;
+}
+
+EFI_STATUS
+SdhcGoIdleState (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  return SdhcSendCommand (HostInst, &CmdGoIdleState, 0);
+}
+
+EFI_STATUS
+SdhcQueryCardType (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  EFI_STATUS Status;
+
+  Status = SdhcGoIdleState (HostInst);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  HostInst->CardInfo.CardFunction = CardFunctionUnknown;
+
+  Status = SdhcSendIfCondSd (HostInst);
+  if (!EFI_ERROR (Status)) {
+    HostInst->CardInfo.CardFunction = CardFunctionSd;
+  }
+
+  Status = SdhcSendOpCondSdio (HostInst);
+  if (!EFI_ERROR (Status)) {
+    if (HostInst->CardInfo.CardFunction == CardFunctionSd) {
+      // SD/SDIO Combo Device
+      HostInst->CardInfo.CardFunction = CardFunctionComboSdSdio;
+    } else {
+      HostInst->CardInfo.CardFunction = CardFunctionSdio;
+    }
+  }
+
+  if (HostInst->CardInfo.CardFunction != CardFunctionSd &&
+      HostInst->CardInfo.CardFunction != CardFunctionSdio &&
+      HostInst->CardInfo.CardFunction != CardFunctionComboSdSdio) {
+    Status = SdhcGoIdleState (HostInst);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    Status = SdhcSendOpCondMmc (HostInst);
+    if (!EFI_ERROR (Status)) {
+      HostInst->CardInfo.CardFunction = CardFunctionMmc;
+    }
+  }
+
+  if (HostInst->CardInfo.CardFunction == CardFunctionUnknown) {
+    return EFI_NO_MEDIA;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcWaitForTranStateAndReadyForData (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  CARD_STATUS     CardStatus;
+  UINT32          Retry;
+  EFI_STATUS      Status;
+
+  Status = SdhcSendStatus (HostInst, &CardStatus);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Retry = SDMMC_POLL_WAIT_COUNT;
+
+  while (((!CardStatus.Fields.READY_FOR_DATA && Retry) ||
+           (CardStatus.Fields.CURRENT_STATE != CardStateTran)) &&
+            Retry) {
+    gBS->Stall (SDMMC_POLL_WAIT_TIME_US);
+    --Retry;
+
+    Status = SdhcSendStatus (HostInst, &CardStatus);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  if (!Retry) {
+    LOG_ERROR ("Time-out waiting for card READY_FOR_DATA status flag");
+    PrintCardStatus (HostInst, CardStatus);
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcSendCid (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  UINT32      CmdArg;
+  EFI_STATUS  Status;
+
+  CmdArg = HostInst->CardInfo.RCA << 16;
+
+  Status = SdhcSendCommand (HostInst, &CmdSendCid, CmdArg);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (HostInst->CardInfo.CardFunction == CardFunctionSd) {
+    gBS->CopyMem (
+      (VOID*) &HostInst->CardInfo.Registers.Sd.Cid,
+      (VOID*) HostInst->CmdResponse,
+      sizeof (SD_CID));
+
+  } else if (HostInst->CardInfo.CardFunction == CardFunctionMmc) {
+    gBS->CopyMem (
+      (VOID*) &HostInst->CardInfo.Registers.Mmc.Cid,
+      (VOID*) HostInst->CmdResponse,
+      sizeof (MMC_CID));
+
+    C_ASSERT (sizeof (HostInst->RpmbIo.Cid) == EFI_RPMB_CID_SIZE);
+    gBS->CopyMem (
+      (VOID*) &HostInst->RpmbIo.Cid,
+      (VOID*) HostInst->CmdResponse,
+      EFI_RPMB_CID_SIZE);
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcSendCsd (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  MMC_CSD     *MmcCsd;
+  SD_CSD      *SdCsd;
+  SD_CSD_2    *SdCsd2;
+  UINT32      BlockNr;
+  UINT64      ByteCapacity;
+  UINT32      CmdArg;
+  UINT32      DeviceSize;
+  UINT32      MaxBlockLen;
+  UINT32      Mult;
+  UINT64      NumBlocks;
+  EFI_STATUS  Status;
+
+  CmdArg = HostInst->CardInfo.RCA << 16;
+  Status = SdhcSendCommand (HostInst, &CmdSendCsd, CmdArg);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  ByteCapacity = 0;
+
+  if (HostInst->CardInfo.CardFunction == CardFunctionSd) {
+    gBS->CopyMem (
+      (VOID*) &HostInst->CardInfo.Registers.Sd.Csd,
+      (VOID*) HostInst->CmdResponse,
+      sizeof (SD_CSD));
+    if (HostInst->CardInfo.Registers.Sd.Csd.CSD_STRUCTURE == 0) {
+      SdCsd = (SD_CSD*) &HostInst->CardInfo.Registers.Sd.Csd;
+      DeviceSize = ((SdCsd->C_SIZEHigh10 << 2) | SdCsd->C_SIZELow2);
+      Mult = 1 << (SdCsd->C_SIZE_MULT + 2);
+      BlockNr = (DeviceSize + 1) * Mult;
+      MaxBlockLen = 1 << SdCsd->READ_BL_LEN;
+      ByteCapacity = BlockNr * MaxBlockLen;
+    } else {
+      SdCsd2 = (SD_CSD_2*) &HostInst->CardInfo.Registers.Sd.Csd;
+      MaxBlockLen = 1 << SdCsd2->READ_BL_LEN;
+      ByteCapacity = (UINT64) (SdCsd2->C_SIZE + 1) * 512llu * 1024llu;
+    }
+  } else if (HostInst->CardInfo.CardFunction == CardFunctionMmc) {
+    gBS->CopyMem (
+      (VOID*) &HostInst->CardInfo.Registers.Mmc.Csd,
+      (VOID*) HostInst->CmdResponse,
+      sizeof (MMC_CSD));
+    // HighCapacity MMC requires reading EXT_CSD to calculate capacity
+    if (!HostInst->CardInfo.HighCapacity) {
+      MmcCsd = (MMC_CSD*) &HostInst->CardInfo.Registers.Mmc.Csd;
+      DeviceSize = ((MmcCsd->C_SIZEHigh10 << 2) | MmcCsd->C_SIZELow2);
+      Mult = 1 << (MmcCsd->C_SIZE_MULT + 2);
+      BlockNr = (DeviceSize + 1) * Mult;
+      MaxBlockLen = 1 << MmcCsd->READ_BL_LEN;
+      ByteCapacity = BlockNr * MaxBlockLen;
+    }
+  }
+
+  HostInst->CardInfo.ByteCapacity = ByteCapacity;
+  HostInst->BlockIo.Media->BlockSize = SD_BLOCK_LENGTH_BYTES;
+  NumBlocks = (ByteCapacity / SD_BLOCK_LENGTH_BYTES);
+  if (NumBlocks > 0) {
+    HostInst->BlockIo.Media->LastBlock = (NumBlocks - 1);
+  } else {
+    HostInst->BlockIo.Media->LastBlock = 0;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcSelectDevice (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  UINT32 CmdArg;
+
+  CmdArg = HostInst->CardInfo.RCA << 16;
+  return SdhcSendCommand (HostInst, &CmdSelect, CmdArg);
+}
+
+EFI_STATUS
+SdhcDeselectDevice (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  return SdhcSendCommand (HostInst, &CmdDeselect, 0);
+}
+
+EFI_STATUS
+SdhcSendAppCmd (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  UINT32 CmdArg;
+
+  CmdArg = HostInst->CardInfo.RCA << 16;
+  return SdhcSendCommand (HostInst, &CmdAppSd, CmdArg);
+}
+
+EFI_STATUS
+SdhcStopTransmission (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  UINT32 CmdArg;
+
+  CmdArg = HostInst->CardInfo.RCA << 16;
+  return SdhcSendCommand (HostInst, &CmdStopTransmission, CmdArg);
+}
+
+EFI_STATUS
+SdhcSendCidAll (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  return SdhcSendCommand (HostInst, &CmdSendCidAll, 0);
+}
+
+EFI_STATUS
+SdhcSendRelativeAddr (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  UINT32      CmdArg;
+  EFI_STATUS  Status;
+
+  CmdArg = 0;
+  // Unlike SD memory, MMC cards don't publish their RCA, instead it should be
+  // manually assigned by the SDHC
+  if (HostInst->CardInfo.CardFunction == CardFunctionMmc) {
+    HostInst->CardInfo.RCA = 0xCCCC;
+    CmdArg = HostInst->CardInfo.RCA << 16;
+  }
+
+  Status = SdhcSendCommand (HostInst, &CmdSendRelativeAddr, CmdArg);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (HostInst->CardInfo.CardFunction != CardFunctionMmc) {
+    HostInst->CardInfo.RCA = (HostInst->CmdResponse[0] >> 16);
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+CalculateCardMaxFreq (
+  IN SDHC_INSTANCE *HostInst,
+  OUT UINT32* MaxClkFreqHz
+  )
+{
+  UINT32    TimeValue;
+  UINT32    TranSpeed;
+  UINT32    TransferRateBitPerSecond;
+
+  ASSERT (HostInst != NULL);
+  ASSERT (MaxClkFreqHz != NULL);
+
+  TransferRateBitPerSecond = 0;
+  TimeValue = 0;
+
+  if (HostInst->CardInfo.CardFunction == CardFunctionSd) {
+    TranSpeed = HostInst->CardInfo.Registers.Sd.Csd.TRAN_SPEED;
+  } else {
+    TranSpeed = HostInst->CardInfo.Registers.Mmc.Csd.TRAN_SPEED;
+  }
+
+  // Calculate Transfer rate unit (Bits 2:0 of TRAN_SPEED)
+  switch (TranSpeed & 0x7) { // 2
+  case 0: // 100kbit/s
+    TransferRateBitPerSecond = 100 * 1000;
+    break;
+
+  case 1: // 1Mbit/s
+    TransferRateBitPerSecond = 1 * 1000 * 1000;
+    break;
+
+  case 2: // 10Mbit/s
+    TransferRateBitPerSecond = 10 * 1000 * 1000;
+    break;
+
+  case 3: // 100Mbit/s
+    TransferRateBitPerSecond = 100 * 1000 * 1000;
+    break;
+
+  default:
+    LOG_ERROR ("Invalid parameter");
+    ASSERT (FALSE);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //Calculate Time value (Bits 6:3 of TRAN_SPEED)
+  switch ((TranSpeed >> 3) & 0xF) { // 6
+  case 0x1:
+    TimeValue = 10;
+    break;
+
+  case 0x2:
+    TimeValue = 12;
+    break;
+
+  case 0x3:
+    TimeValue = 13;
+    break;
+
+  case 0x4:
+    TimeValue = 15;
+    break;
+
+  case 0x5:
+    TimeValue = 20;
+    break;
+
+  case 0x6:
+    if (HostInst->CardInfo.CardFunction == CardFunctionSd) {
+      TimeValue = 25;
+    } else {
+      TimeValue = 26;
+    }
+    break;
+
+  case 0x7:
+    TimeValue = 30;
+    break;
+
+  case 0x8:
+    TimeValue = 35;
+    break;
+
+  case 0x9:
+    TimeValue = 40;
+    break;
+
+  case 0xA:
+    TimeValue = 45;
+    break;
+
+  case 0xB:
+    if (HostInst->CardInfo.CardFunction == CardFunctionSd) {
+      TimeValue = 50;
+    } else {
+      TimeValue = 52;
+    }
+    break;
+
+  case 0xC:
+    TimeValue = 55;
+    break;
+
+  case 0xD:
+    TimeValue = 60;
+    break;
+
+  case 0xE:
+    TimeValue = 70;
+    break;
+
+  case 0xF:
+    TimeValue = 80;
+    break;
+
+  default:
+    LOG_ERROR ("Invalid parameter");
+    ASSERT (FALSE);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *MaxClkFreqHz = (TransferRateBitPerSecond * TimeValue) / 10;
+
+  LOG_TRACE (
+    "TransferRateUnitId=%d TimeValue*10=%d, CardFrequency=%dKHz",
+    TranSpeed & 0x7,
+    TimeValue,
+    *MaxClkFreqHz / 1000);
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcSetMaxClockFrequency (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  EFI_SDHC_PROTOCOL   *HostExt;
+  UINT32              MaxClkFreqHz;
+  EFI_STATUS          Status;
+
+  HostExt = HostInst->HostExt;
+  MaxClkFreqHz = 0;
+
+  // Currently only NormalSpeed and HighSpeed supported
+  ASSERT (HostInst->CardInfo.CurrentSpeedMode == CardSpeedModeNormalSpeed ||
+          HostInst->CardInfo.CurrentSpeedMode == CardSpeedModeHighSpeed);
+
+  if (HostInst->CardInfo.CardFunction == CardFunctionSd) {
+    Status = CalculateCardMaxFreq (HostInst, &MaxClkFreqHz);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  } else {
+    if (HostInst->CardInfo.CurrentSpeedMode == CardSpeedModeNormalSpeed) {
+      Status = CalculateCardMaxFreq (HostInst, &MaxClkFreqHz);
+      if (EFI_ERROR (Status)) {
+        return Status;
+      }
+    } else if (HostInst->CardInfo.CurrentSpeedMode == CardSpeedModeHighSpeed) {
+      MaxClkFreqHz = MMC_HIGH_SPEED_MODE_CLOCK_FREQ_HZ;
+    }
+  }
+
+  Status = HostExt->SetClock (HostExt, MaxClkFreqHz);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcSetBlockLength (
+  IN SDHC_INSTANCE  *HostInst,
+  IN UINT32         BlockByteLength
+  )
+{
+  LOG_TRACE ("SdhcSetBlockLength(BlockByteLength=%d)", BlockByteLength);
+
+  return SdhcSendCommand (HostInst, &CmdSetBlockLength, BlockByteLength);
+}
+
+EFI_STATUS
+SdhcSetBlockCount (
+  IN SDHC_INSTANCE  *HostInst,
+  IN UINT32         BlockCount,
+  IN BOOLEAN        ReliableWrite
+  )
+{
+  UINT32 CmdArg;
+
+  LOG_TRACE (
+    "SdhcSetBlockCount(BlockCount=%d, ReliableWrite=%d)",
+    BlockCount,
+    ReliableWrite);
+
+  // JEDEC Standard No. 84-A441, Page 76
+  // Set bit[31] as 1 to indicate Reliable Write type of programming access.
+  if (ReliableWrite) {
+    CmdArg = BlockCount | (1 << 31);
+  } else {
+    CmdArg = BlockCount;
+  }
+
+  return SdhcSendCommand (HostInst, &CmdSetBlockCount, CmdArg);
+}
+
+EFI_STATUS
+SdhcSendStatus (
+  IN SDHC_INSTANCE  *HostInst,
+  OUT CARD_STATUS   *CardStatus
+  )
+{
+  UINT32      CmdArg;
+  EFI_STATUS  Status;
+
+  LOG_TRACE ("SdhcSendStatus()");
+
+  CmdArg = HostInst->CardInfo.RCA << 16;
+  Status = SdhcSendCommand (HostInst, &CmdSendStatus, CmdArg);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  CardStatus->AsUint32 = HostInst->CmdResponse[0];
+
+  return EFI_SUCCESS;
+}
+
+// SD Specific Functions
+EFI_STATUS
+SdhcSendScrSd (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  SD_SCR      *Scr;
+  UINT32      CmdArg;
+  EFI_STATUS  Status;
+
+  CmdArg = HostInst->CardInfo.RCA << 16;
+  Status = SdhcSendDataCommand (
+    HostInst,
+    &CmdAppSendScrSd,
+    CmdArg,
+    sizeof (HostInst->BlockBuffer),
+    HostInst->BlockBuffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  gBS->CopyMem (
+    &HostInst->CardInfo.Registers.Sd.Scr,
+    HostInst->BlockBuffer,
+    sizeof (SD_SCR));
+
+  Scr = (SD_SCR*) &HostInst->BlockBuffer;
+  LOG_TRACE ("SD_SCR:");
+  LOG_TRACE ("  SD_SPEC=%d", (UINT32) Scr->SD_SPEC);
+  LOG_TRACE ("  SD_SPEC3=%d", (UINT32) Scr->SD_SPEC3);
+  LOG_TRACE (
+    "  SD_BUS_WIDTHS=%x, 1-Bit?%d, 4-Bit?%d",
+    (UINT32) Scr->SD_BUS_WIDTH,
+    (UINT32) ((Scr->SD_BUS_WIDTH & BIT1) ? 1 : 0),
+    (UINT32) ((Scr->SD_BUS_WIDTH & BIT2) ? 1 : 0));
+  LOG_TRACE (
+    "  CMD_SUPPORT=%x, CMD23?%d, CMD20?%d",
+    (UINT32) Scr->CMD_SUPPORT,
+    (UINT32) ((Scr->CMD_SUPPORT & BIT2) ? 1 : 0),
+    (UINT32) ((Scr->CMD_SUPPORT & BIT1) ? 1 : 0));
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcSendIfCondSd (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  SEND_IF_COND_ARG            CmdArg;
+  SEND_IF_COND_CMD_RESPONSE   CmdStatus;
+  EFI_STATUS                  Status;
+
+  CmdArg.AsUint32 = 0;
+  // Recommended check pattern per SD Specs
+  CmdArg.Fields.CheckPattern = 0xAA;
+
+  // Our current implementation does not support more than HighSpeed voltage 2V7-3V6 (i.e no 1V8)
+  CmdArg.Fields.VoltageSupplied = SD_CMD8_VOLTAGE_27_36;
+
+  Status = SdhcSendCommand (HostInst, &CmdSendIfCondSd, CmdArg.AsUint32);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  CmdStatus.AsUint32 = HostInst->CmdResponse[0];
+  if (CmdStatus.Fields.CheckPattern != CmdArg.Fields.CheckPattern ||
+      CmdStatus.Fields.VoltageSupplied != CmdArg.Fields.VoltageSupplied) {
+    return EFI_UNSUPPORTED;
+  }
+
+  HostInst->CardInfo.HasExtendedOcr = TRUE;
+
+  return EFI_SUCCESS;;
+}
+
+EFI_STATUS
+SdhcSendOpCondSd (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  SD_OCR_EX             *OcrEx;
+  SD_SEND_OP_COND_ARG   CmdArg;
+  SD_OCR                Ocr;
+  UINT32                Retry;
+  EFI_STATUS            Status;
+
+  // With arg set to 0, it means read OCR
+  Status = SdhcSendCommand (HostInst, &CmdAppSendOpCondSd, 0);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  HostInst->CardInfo.Registers.Sd.Ocr.AsUint32 = HostInst->CmdResponse[0];
+
+  CmdArg.AsUint32 = 0;
+  Retry = SDMMC_POLL_WAIT_COUNT;
+
+  CmdArg.Fields.VoltageWindow = HostInst->CardInfo.Registers.Sd.Ocr.Fields.VoltageWindow;
+  // Host support for High Capacity is assumed
+  CmdArg.Fields.HCS = 1;
+  while (Retry) {
+    Status = SdhcSendCommand (HostInst, &CmdAppSendOpCondSd, CmdArg.AsUint32);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    Ocr.AsUint32 = HostInst->CmdResponse[0];
+    if (Ocr.Fields.PowerUp) {
+      LOG_TRACE ("SD Card PowerUp Complete");
+      if (HostInst->CardInfo.HasExtendedOcr) {
+        OcrEx = (SD_OCR_EX*) &Ocr;
+        if (OcrEx->Fields.CCS) {
+          LOG_TRACE ("Card is SD2.0 or later HighCapacity SDHC or SDXC");
+          HostInst->CardInfo.HighCapacity = TRUE;
+        } else {
+          LOG_TRACE ("Card is SD2.0 or later StandardCapacity SDSC");
+          HostInst->CardInfo.HighCapacity = FALSE;
+        }
+      }
+      break;
+    }
+    gBS->Stall (SDMMC_POLL_WAIT_TIME_US);
+    --Retry;
+  }
+
+  if (!Retry) {
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcSwitchBusWidthSd (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  EFI_SDHC_PROTOCOL *HostExt;
+  UINT32 CmdArg;
+  EFI_STATUS Status;
+
+  HostExt = HostInst->HostExt;
+  CmdArg = 0x2; // 4-bit
+  Status = SdhcSendCommand (HostInst, &CmdAppSetBusWidthSd, CmdArg);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = HostExt->SetBusWidth (HostExt, SdBusWidth4Bit);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcSwitchSpeedModeSd (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  return EFI_SUCCESS;
+}
+
+// SDIO Specific Functions
+EFI_STATUS
+SdhcSendOpCondSdio (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  return SdhcSendCommand (HostInst, &CmdSendOpCondSdio, 0);
+}
+
+// Mmc Specific Functions
+EFI_STATUS
+SdhcSendOpCondMmc (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  MMC_OCR               *Ocr;
+  MMC_SEND_OP_COND_ARG  CmdArg;
+  UINT32                Retry;
+  EFI_STATUS            Status;
+
+  CmdArg.AsUint32 = 0;
+  Retry = SDMMC_POLL_WAIT_COUNT;
+  Ocr = &HostInst->CardInfo.Registers.Mmc.Ocr;
+  CmdArg.Fields.VoltageWindow = SD_OCR_HIGH_VOLTAGE_WINDOW;
+  CmdArg.Fields.AccessMode = SdOcrAccessSectorMode;
+  while (Retry) {
+    Status = SdhcSendCommand (HostInst, &CmdSendOpCondMmc, CmdArg.AsUint32);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    HostInst->CardInfo.Registers.Mmc.Ocr.AsUint32 = HostInst->CmdResponse[0];
+    if (Ocr->Fields.PowerUp) {
+      LOG_TRACE ("MMC Card PowerUp Complete");
+      if (Ocr->Fields.AccessMode == SdOcrAccessSectorMode) {
+        LOG_TRACE ("Card is MMC HighCapacity");
+        HostInst->CardInfo.HighCapacity = TRUE;
+      } else {
+        LOG_TRACE ("Card is MMC StandardCapacity");
+        HostInst->CardInfo.HighCapacity = FALSE;
+      }
+
+      if ((Ocr->Fields.VoltageWindow & SD_OCR_HIGH_VOLTAGE_WINDOW) != SD_OCR_HIGH_VOLTAGE_WINDOW) {
+        LOG_ERROR (
+          "MMC Card does not support High Voltage, expected profile:%x actual profile:%x",
+          (UINT32) SD_OCR_HIGH_VOLTAGE_WINDOW,
+          (UINT32) Ocr->Fields.VoltageWindow);
+        return EFI_UNSUPPORTED;
+      }
+      break;
+    }
+    gBS->Stall (SDMMC_POLL_WAIT_TIME_US);
+    --Retry;
+  }
+
+  if (!Retry) {
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcSwitchBusWidthMmc (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  MMC_EXT_CSD         *ExtCsd;
+  EFI_SDHC_PROTOCOL   *HostExt;
+  SD_BUS_WIDTH        BusWidth;
+  UINT32              CmdArg;
+  UINT8               ExtCsdPowerClass;
+  EFI_STATUS          Status;
+  MMC_SWITCH_CMD_ARG  SwitchCmdArg;
+
+  HostExt = HostInst->HostExt;
+  BusWidth = SdBusWidth8Bit;
+  ExtCsd = &HostInst->CardInfo.Registers.Mmc.ExtCsd;
+  Status = SdhcSendExtCsdMmc (HostInst);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Figure out current requirements for target bus width. An inrease in current consumption
+  // may require switching the card to a higher power class
+  if (BusWidth == SdBusWidth8Bit) {
+    if (HostInst->CardInfo.CurrentSpeedMode == CardSpeedModeHighSpeed) {
+      ExtCsdPowerClass = MMC_EXT_CSD_POWER_CLASS_8BIT (ExtCsd->PowerClass52Mhz36V);
+    } else {
+      ExtCsdPowerClass = MMC_EXT_CSD_POWER_CLASS_8BIT (ExtCsd->PowerClass26Mhz36V);
+    }
+  } else if (BusWidth == SdBusWidth4Bit) {
+    if (HostInst->CardInfo.CurrentSpeedMode == CardSpeedModeHighSpeed) {
+      ExtCsdPowerClass = MMC_EXT_CSD_POWER_CLASS_4BIT (ExtCsd->PowerClass52Mhz36V);
+    } else {
+      ExtCsdPowerClass = MMC_EXT_CSD_POWER_CLASS_4BIT (ExtCsd->PowerClass26Mhz36V);
+    }
+  } else {
+    return EFI_UNSUPPORTED;
+  }
+
+  // Only do power class switch if the target bus width requires more current than the
+  // allowed by the current power class in EXT_CSD
+  if (ExtCsdPowerClass > HostInst->CardInfo.Registers.Mmc.ExtCsd.PowerClass) {
+    CmdArg = ExtCsdPowerClass;
+    CmdArg <<= 8;
+    CmdArg |= 0x03BB0000;
+    Status = SdhcSendCommand (
+      HostInst,
+      &CmdSwitchMmc,
+      CmdArg);
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR ("Error detected on switching PowerClass function");
+      return Status;
+    }
+
+    Status = SdhcSendExtCsdMmc (HostInst);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    // Sanity check that wanted power-class are set per requested
+    if (HostInst->CardInfo.Registers.Mmc.ExtCsd.PowerClass != ExtCsdPowerClass) {
+      LOG_ERROR (
+        "MMC EXT_CSD not reporting correct PowerClass after switch. Expected:%x Actual:%x",
+        (UINT32) ExtCsdPowerClass,
+        (UINT32) HostInst->CardInfo.Registers.Mmc.ExtCsd.PowerClass);
+      return EFI_PROTOCOL_ERROR;
+    }
+  }
+
+  // Switch bus width
+  SwitchCmdArg.AsUint32 = 0;
+  SwitchCmdArg.Fields.Access = MmcSwitchCmdAccessTypeWriteByte;
+  SwitchCmdArg.Fields.Index = MmcExtCsdBitIndexBusWidth;
+
+  if (BusWidth == SdBusWidth8Bit) {
+    SwitchCmdArg.Fields.Value = MmcExtCsdBusWidth8Bit;
+  } else {
+    SwitchCmdArg.Fields.Value = MmcExtCsdBusWidth4Bit;
+  }
+
+  Status = SdhcSendCommand (
+    HostInst,
+    &CmdSwitchMmc,
+    SwitchCmdArg.AsUint32);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("Error detected on switching BusWidth function");
+    return Status;
+  }
+
+  Status = HostExt->SetBusWidth (HostExt, BusWidth);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcSwitchSpeedModeMmc (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  MMC_SWITCH_CMD_ARG  CmdArg;
+  EFI_STATUS          Status;
+
+  CmdArg.AsUint32 = 0;
+  CmdArg.Fields.Access = MmcSwitchCmdAccessTypeWriteByte;
+  CmdArg.Fields.Index = MmcExtCsdBitIndexHsTiming;
+  CmdArg.Fields.Value = 1;
+
+  Status = SdhcSendCommand (
+    HostInst,
+    &CmdSwitchMmc,
+    CmdArg.AsUint32);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("Error detected on switching HighSpeed function");
+    return Status;
+  }
+
+  HostInst->CardInfo.CurrentSpeedMode = CardSpeedModeHighSpeed;
+
+  Status = SdhcSendExtCsdMmc (HostInst);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (!HostInst->CardInfo.Registers.Mmc.ExtCsd.HighSpeedTiming) {
+    LOG_ERROR ("MMC EXT_CSD not reporting HighSpeed timing after switch");
+    return EFI_PROTOCOL_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcSendExtCsdMmc (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  MMC_EXT_CSD   *ExtCsd;
+  UINT32        CmdArg;
+  EFI_STATUS    Status;
+
+  CmdArg = HostInst->CardInfo.RCA << 16;
+  ExtCsd = &HostInst->CardInfo.Registers.Mmc.ExtCsd;
+  Status = SdhcSendDataCommand (
+    HostInst,
+    &CmdSendExtCsdMmc,
+    CmdArg,
+    sizeof (HostInst->BlockBuffer),
+    HostInst->BlockBuffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  gBS->CopyMem (ExtCsd, HostInst->BlockBuffer, sizeof (MMC_EXT_CSD));
+  HostInst->CardInfo.ByteCapacity = (UINT64) ExtCsd->SectorCount * 512llu;
+  HostInst->BlockIo.Media->LastBlock = ExtCsd->SectorCount - 1;
+  HostInst->RpmbIo.ReliableSectorCount = ExtCsd->ReliableWriteSectorCount;
+  HostInst->RpmbIo.RpmbSizeMult = ExtCsd->RpmbSizeMult;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SdhcSwitchPartitionMmc (
+  IN SDHC_INSTANCE                  *HostInst,
+  IN MMC_EXT_CSD_PARTITION_ACCESS   Partition
+  )
+{
+  MMC_SWITCH_CMD_ARG            CmdArg;
+  MMC_EXT_CSD_PARTITION_CONFIG  PartConfig;
+  EFI_STATUS                    Status;
+
+  LOG_TRACE (
+    "SdhcSwitchPartitionMmc(Partition=%a)",
+    MmcPartitionAccessToString (Partition));
+
+  PartConfig.AsUint8 = HostInst->CardInfo.Registers.Mmc.ExtCsd.PartitionConfig;
+  PartConfig.Fields.PARTITION_ACCESS = Partition;
+
+  // Write the partition type to EXT_CSD[PARTITION_CONFIG].PARTITION_ACCESS
+  ZeroMem (&CmdArg, sizeof (MMC_SWITCH_CMD_ARG));
+  CmdArg.Fields.Access = MmcSwitchCmdAccessTypeWriteByte;
+  CmdArg.Fields.Index = MmcExtCsdBitIndexPartitionConfig;
+  CmdArg.Fields.Value = PartConfig.AsUint8;
+
+  Status = SdhcSendCommand (HostInst, &CmdSwitchMmc, CmdArg.AsUint32);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSendCommand failed. (Status = %r)");
+    return Status;
+  }
+
+  // Re-read the EXT_CSD to verify the partition switch physically happenned
+  Status = SdhcSendExtCsdMmc (HostInst);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  PartConfig.AsUint8 = HostInst->CardInfo.Registers.Mmc.ExtCsd.PartitionConfig;
+  if (PartConfig.Fields.PARTITION_ACCESS != Partition) {
+    LOG_ERROR (
+      "Current partition indicated by EXT_CSD doesn't match the "
+      "partition the MMC should have switched to. Expected:%a Actual:%a",
+      MmcPartitionAccessToString (Partition),
+      MmcPartitionAccessToString (PartConfig.Fields.PARTITION_ACCESS));
+
+    return EFI_DEVICE_ERROR;
+  }
+
+  LOG_TRACE (
+    "Switched from partition %a to %a successfully",
+    MmcPartitionAccessToString (HostInst->CurrentMmcPartition),
+    MmcPartitionAccessToString (Partition));
+
+  HostInst->CurrentMmcPartition = Partition;
+
+  return EFI_SUCCESS;
+}
+
+// SD command definitions
+CONST SD_COMMAND CmdGoIdleState = {
+  0,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeNone,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdSendOpCondMmc = {
+  1,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR3,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdSendCidAll = {
+  2,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR2,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdSendRelativeAddr = {
+  3,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR6,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdSendOpCondSdio = {
+  5,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR4,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdSwitchSd = {
+  6,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR1,
+  SdTransferTypeSingleBlock,
+  SdTransferDirectionRead
+};
+
+CONST SD_COMMAND CmdSwitchMmc = {
+  6,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR1B,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdAppSetBusWidthSd = {
+  6,
+  SdCommandTypeUndefined,
+  SdCommandClassApp,
+  SdResponseTypeR1,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdSelect = {
+  7,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR1B,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdDeselect = {
+  7,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeNone,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdSendIfCondSd = {
+  8,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR6,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdSendExtCsdMmc = {
+  8,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR1,
+  SdTransferTypeSingleBlock,
+  SdTransferDirectionRead
+};
+
+CONST SD_COMMAND CmdSendCsd = {
+  9,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR2,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdSendCid = {
+  10,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR2,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdSwitchVoltageSd = {
+  11,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR1,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdStopTransmission = {
+  12,
+  SdCommandTypeAbort,
+  SdCommandClassStandard,
+  SdResponseTypeR1B,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdSendStatus = {
+  13,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR1,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdBusTestReadMmc = {
+  14,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR1,
+  SdTransferTypeSingleBlock,
+  SdTransferDirectionRead
+};
+
+CONST SD_COMMAND CmdSetBlockLength = {
+  16,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR1,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdReadSingleBlock = {
+  17,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR1,
+  SdTransferTypeSingleBlock,
+  SdTransferDirectionRead
+};
+
+CONST SD_COMMAND CmdReadMultiBlock = {
+  18,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR1,
+  SdTransferTypeMultiBlock,
+  SdTransferDirectionRead
+};
+
+CONST SD_COMMAND CmdBusTestWriteMmc = {
+  19,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR1,
+  SdTransferTypeSingleBlock,
+  SdTransferDirectionWrite
+};
+
+CONST SD_COMMAND CmdSetBlockCount = {
+  23,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR1,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdWriteSingleBlock = {
+  24,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR1,
+  SdTransferTypeSingleBlock,
+  SdTransferDirectionWrite
+};
+
+CONST SD_COMMAND CmdWriteMultiBlock = {
+  25,
+  SdCommandTypeUndefined,
+  SdCommandClassStandard,
+  SdResponseTypeR1,
+  SdTransferTypeMultiBlock,
+  SdTransferDirectionWrite
+};
+
+CONST SD_COMMAND CmdAppSendOpCondSd = {
+  41,
+  SdCommandTypeUndefined,
+  SdCommandClassApp,
+  SdResponseTypeR3,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdAppSetClrCardDetectSd = {
+  42,
+  SdCommandTypeUndefined,
+  SdCommandClassApp,
+  SdResponseTypeR1,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
+
+CONST SD_COMMAND CmdAppSendScrSd = {
+  51,
+  SdCommandTypeUndefined,
+  SdCommandClassApp,
+  SdResponseTypeR1,
+  SdTransferTypeSingleBlock,
+  SdTransferDirectionRead
+};
+
+CONST SD_COMMAND CmdAppSd = {
+  55,
+  SdCommandTypeUndefined,
+  SdCommandClassApp,
+  SdResponseTypeR1,
+  SdTransferTypeNone,
+  SdTransferDirectionUndefined
+};
diff --git a/Platform/Microsoft/Drivers/SdMmcDxe/Protocol.h b/Platform/Microsoft/Drivers/SdMmcDxe/Protocol.h
new file mode 100644
index 000000000000..b6c09435989c
--- /dev/null
+++ b/Platform/Microsoft/Drivers/SdMmcDxe/Protocol.h
@@ -0,0 +1,231 @@
+/** @file
+*
+*  Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#ifndef __PROTOCOL_H__
+#define __PROTOCOL_H__
+
+EFI_STATUS
+InitializeDevice (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+InitializeSdDevice (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+InitializeMmcDevice (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSendCommand (
+  IN SDHC_INSTANCE      *HostInst,
+  IN CONST SD_COMMAND   *Cmd,
+  IN UINT32             Arg
+  );
+
+EFI_STATUS
+SdhcSendDataCommand (
+  IN SDHC_INSTANCE      *HostInst,
+  IN CONST SD_COMMAND   *Cmd,
+  IN UINT32             Arg,
+  IN UINT32             BufferByteSize,
+  IN OUT VOID           *Buffer
+  );
+
+// SD/SDIO/MMC Generic Functions
+
+EFI_STATUS
+SdhcRecoverFromErrors (
+  IN SDHC_INSTANCE      *HostInst,
+  IN CONST SD_COMMAND   *Cmd
+  );
+
+EFI_STATUS
+SdhcQueryCardType (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcGoIdleState (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSendCidAll (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSendRelativeAddr (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcWaitForTranStateAndReadyForData (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSendCid (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSendCsd (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSendAppCmd (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSelectDevice (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcDeselectDevice (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcStopTransmission (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSetMaxClockFrequency (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSetBlockLength (
+  IN SDHC_INSTANCE  *HostInst,
+  IN UINT32          BlockByteLength
+  );
+
+EFI_STATUS
+SdhcSetBlockCount (
+  IN SDHC_INSTANCE  *HostInst,
+  IN UINT32         BlockCount,
+  IN BOOLEAN        ReliableWrite
+  );
+
+EFI_STATUS
+SdhcSendStatus (
+  IN SDHC_INSTANCE  *HostInst,
+  OUT CARD_STATUS   *CardStatus
+  );
+
+// SD Specific Functions
+
+EFI_STATUS
+SdhcSendScrSd (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSendOpCondSd (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSendIfCondSd (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSwitchBusWidthSd (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSwitchSpeedModeSd (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+// SDIO Specific Functions
+
+EFI_STATUS
+SdhcSendOpCondSdio (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+// MMC Specific Functions
+
+EFI_STATUS
+SdhcSendOpCondMmc (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSwitchBusWidthMmc (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+
+EFI_STATUS
+SdhcSwitchSpeedModeMmc (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSendExtCsdMmc (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+EFI_STATUS
+SdhcSwitchPartitionMmc (
+  IN SDHC_INSTANCE                  *HostInst,
+  IN MMC_EXT_CSD_PARTITION_ACCESS   Partition
+  );
+
+// SD/MMC Commands
+
+extern CONST SD_COMMAND CmdGoIdleState;
+extern CONST SD_COMMAND CmdSendOpCondMmc;
+extern CONST SD_COMMAND CmdSendCidAll;
+extern CONST SD_COMMAND CmdSendRelativeAddr;
+extern CONST SD_COMMAND CmdSendOpCondSdio;
+extern CONST SD_COMMAND CmdSwitchSd;
+extern CONST SD_COMMAND CmdSwitchMmc;
+extern CONST SD_COMMAND CmdAppSetBusWidthSd;
+extern CONST SD_COMMAND CmdSelect;
+extern CONST SD_COMMAND CmdDeselect;
+extern CONST SD_COMMAND CmdSendIfCondSd;
+extern CONST SD_COMMAND CmdSendExtCsdMmc;
+extern CONST SD_COMMAND CmdSendCsd;
+extern CONST SD_COMMAND CmdSendCid;
+extern CONST SD_COMMAND CmdSwitchVoltageSd;
+extern CONST SD_COMMAND CmdStopTransmission;
+extern CONST SD_COMMAND CmdSendStatus;
+extern CONST SD_COMMAND CmdBusTestReadMmc;
+extern CONST SD_COMMAND CmdSetBlockLength;
+extern CONST SD_COMMAND CmdReadSingleBlock;
+extern CONST SD_COMMAND CmdReadMultiBlock;
+extern CONST SD_COMMAND CmdBusTestWriteMmc;
+extern CONST SD_COMMAND CmdSetBlockCount;
+extern CONST SD_COMMAND CmdWriteSingleBlock;
+extern CONST SD_COMMAND CmdWriteMultiBlock;
+extern CONST SD_COMMAND CmdAppSendOpCondSd;
+extern CONST SD_COMMAND CmdAppSetClrCardDetectSd;
+extern CONST SD_COMMAND CmdAppSendScrSd;
+extern CONST SD_COMMAND CmdAppSd;
+
+#endif // __PROTOCOL_H__
diff --git a/Platform/Microsoft/Drivers/SdMmcDxe/RpmbIo.c b/Platform/Microsoft/Drivers/SdMmcDxe/RpmbIo.c
new file mode 100644
index 000000000000..b0af15b7a749
--- /dev/null
+++ b/Platform/Microsoft/Drivers/SdMmcDxe/RpmbIo.c
@@ -0,0 +1,609 @@
+/** @file
+*
+*  Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#include <Uefi.h>
+
+#include <Protocol/BlockIo.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/RpmbIo.h>
+#include <Protocol/Sdhc.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/TimerLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include "SdMmcHw.h"
+#include "SdMmc.h"
+#include "Protocol.h"
+
+C_ASSERT(sizeof(EFI_RPMB_DATA_PACKET) == SD_BLOCK_LENGTH_BYTES);
+
+EFI_RPMB_DATA_PACKET ResultRequest = {
+  { 0 }, // Stuff
+  { 0 }, // MAC
+  { 0 }, // Data
+  { 0 }, // Nonce
+  { 0 }, // WriteCounter
+  { 0 }, // Address
+  { 0 }, // PacketCount
+  { 0 }, // OperationResult
+  { 0x00, EFI_RPMB_REQUEST_RESULT_REQUEST } // RequestType
+};
+
+// JEDEC Standard No. 84-A441:
+// Byte order of the RPMB data frame is MSB first, e.g. Write Counter MSB [11]
+// is storing the upmost byte of the counter value.
+
+VOID
+Uint16ToRpmbBytes (
+  IN UINT16   Value,
+  OUT UINT8   *RpmbBytes
+  )
+{
+  ASSERT (RpmbBytes != NULL);
+
+  RpmbBytes[0] = (UINT8) (Value >> 8);
+  RpmbBytes[1] = (UINT8) (Value & 0xF);
+}
+
+
+UINT16
+RpmbBytesToUint16 (
+  IN CONST UINT8  *RpmbBytes
+  )
+{
+  ASSERT (RpmbBytes != NULL);
+
+  return ((UINT16) RpmbBytes[0] << 8) | ((UINT16) RpmbBytes[1]);
+}
+
+EFI_STATUS
+RpmbRead (
+  IN SDHC_INSTANCE          *HostInst,
+  IN EFI_RPMB_DATA_PACKET   *Request,
+  OUT EFI_RPMB_DATA_BUFFER  *Response
+  )
+{
+  EFI_STATUS Status;
+
+  Status = SdhcSetBlockCount (HostInst, 1, FALSE);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSetBlockCount() failed. (Status = %r)", Status);
+    return Status;
+  }
+
+  Status = SdhcSendDataCommand (
+    HostInst,
+    &CmdWriteMultiBlock,
+    0, // LBA argument is ignored for RPMB access, the eMMC will use instead the Address
+       // field of the RPMB packet
+    sizeof (*Request),
+    Request);
+
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR (
+      "Failed to write the Read request data packet. (Status = %r)",
+      Status);
+
+    return Status;
+  }
+
+  Status = SdhcSetBlockCount (HostInst, Response->PacketCount, FALSE);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSetBlockCount() failed. (Status = %r)", Status);
+    return Status;
+  }
+
+  Status = SdhcSendDataCommand (
+    HostInst,
+    &CmdReadMultiBlock,
+    0, // LBA argument is ignored for RPMB access, the eMMC will use instead the Address
+       // field of the RPMB packet
+    Response->PacketCount * sizeof(*Response->Packets),
+    Response->Packets);
+
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR (
+      "Failed to read the Read request data packet. (Status = %r)",
+      Status);
+
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+RpmbWrite (
+  IN SDHC_INSTANCE          *HostInst,
+  IN EFI_RPMB_DATA_BUFFER   *Request
+  )
+{
+  EFI_STATUS Status;
+
+  Status = SdhcSetBlockCount (HostInst, Request->PacketCount, TRUE);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SdhcSetBlockCount() failed. (Status = %r)", Status);
+    return Status;
+  }
+
+  Status = SdhcSendDataCommand (
+    HostInst,
+    &CmdWriteMultiBlock,
+    0, // LBA argument is ignored for RPMB access, the eMMC will use instead the Address
+       // field of the RPMB packet
+    Request->PacketCount * sizeof(*Request->Packets),
+    Request->Packets);
+
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR (
+      "Failed to write the Read request data packet. (Status = %r)",
+      Status);
+
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+RpmbQueryResult (
+  IN SDHC_INSTANCE          *HostInst,
+  OUT EFI_RPMB_DATA_PACKET  *Response
+  )
+{
+  EFI_RPMB_DATA_BUFFER  ResponseBuffer;
+  EFI_STATUS            Status;
+
+  ResponseBuffer.PacketCount = 1;
+  ResponseBuffer.Packets = Response;
+
+  Status = RpmbRead (HostInst, &ResultRequest, &ResponseBuffer);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("RpmbRead() failed. (Status = %r)", Status);
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+RpmbProgramKeyRequest (
+  IN SDHC_INSTANCE          *HostInst,
+  IN EFI_RPMB_DATA_PACKET   *Request,
+  OUT EFI_RPMB_DATA_PACKET  *Response
+  )
+{
+  EFI_RPMB_DATA_BUFFER  RequestBuffer;
+  EFI_STATUS            Status;
+
+  RequestBuffer.PacketCount = 1;
+  RequestBuffer.Packets = Request;
+
+  Status = RpmbWrite (HostInst, &RequestBuffer);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("RpmbWrite() failed. (Status = %r)", Status);
+    return Status;
+  }
+
+  Status = RpmbQueryResult (HostInst, Response);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("RpmbQueryResult() failed. (Status = %r)", Status);
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+RpmbReadCounterRequest (
+  IN SDHC_INSTANCE          *HostInst,
+  IN EFI_RPMB_DATA_PACKET   *Request,
+  OUT EFI_RPMB_DATA_PACKET  *Response
+  )
+{
+  EFI_RPMB_DATA_BUFFER  ResponseBuffer;
+  EFI_STATUS            Status;
+
+  ResponseBuffer.PacketCount = 1;
+  ResponseBuffer.Packets = Response;
+
+  Status = RpmbRead (HostInst, Request, &ResponseBuffer);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("RpmbRead() failed. (Status = %r)", Status);
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+RpmbAuthenticatedWriteRequest (
+  IN SDHC_INSTANCE          *HostInst,
+  IN EFI_RPMB_DATA_BUFFER   *Request,
+  OUT EFI_RPMB_DATA_PACKET  *Response
+  )
+{
+  EFI_STATUS Status;
+
+  Status = RpmbWrite (HostInst, Request);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("RpmbWrite() failed. (Status = %r)", Status);
+    return Status;
+  }
+
+  Status = RpmbQueryResult (HostInst, Response);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("RpmbQueryResult() failed. (Status = %r)", Status);
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+RpmbAuthenticatedReadRequest (
+  IN SDHC_INSTANCE          *HostInst,
+  IN EFI_RPMB_DATA_PACKET   *Request,
+  OUT EFI_RPMB_DATA_BUFFER  *Response
+  )
+{
+  EFI_STATUS Status;
+
+  Status = RpmbRead (HostInst, Request, Response);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("RpmbRead() failed. (Status = %r)", Status);
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+VOID
+RpmbHexDump (
+  IN CONST UINT8  *Bytes,
+  IN UINTN        Len
+  )
+{
+  UINTN i;
+
+  for (i = 0; i < Len; i++) {
+    if ((i > 0) && (i % 16) == 0) {
+      LOG_VANILLA_TRACE ("\n");
+    }
+    LOG_VANILLA_TRACE ("%02x ", *Bytes);
+    ++Bytes;
+  }
+  LOG_VANILLA_TRACE ("\n");
+}
+
+VOID
+RpmbDumpPacket (
+  IN EFI_RPMB_DATA_PACKET *Packet
+  )
+{
+  LOG_TRACE ("*** RPMB Packet Dump (Packet = %p) ***", Packet);
+  LOG_TRACE ("- Write Counter");
+  RpmbHexDump (Packet->WriteCounter, EFI_RPMB_PACKET_WCOUNTER_SIZE);
+  LOG_TRACE ("- Address:");
+  RpmbHexDump (Packet->Address, EFI_RPMB_PACKET_ADDRESS_SIZE);
+  LOG_TRACE ("- Block Count:");
+  RpmbHexDump (Packet->BlockCount, EFI_RPMB_PACKET_BLOCKCOUNT_SIZE);
+  LOG_TRACE ("- Result:");
+  RpmbHexDump (Packet->OperationResult, EFI_RPMB_PACKET_RESULT_SIZE);
+  LOG_TRACE ("- Req/Res Type:");
+  RpmbHexDump (Packet->RequestOrResponseType, EFI_RPMB_PACKET_TYPE_SIZE);
+}
+
+EFI_STATUS
+RpmbRequest (
+  IN EFI_RPMB_IO_PROTOCOL   *This,
+  IN EFI_RPMB_DATA_BUFFER   *Request,
+  OUT EFI_RPMB_DATA_BUFFER  *Response
+  )
+{
+  SDHC_INSTANCE                 *HostInst;
+  MMC_EXT_CSD_PARTITION_ACCESS  CurrentPartition;
+  UINT16                        RequestType;
+  EFI_STATUS                    Status;
+  BOOLEAN                       SwitchPartition;
+  EFI_STATUS                    SwitchStatus;
+
+  SwitchPartition = FALSE;
+  Status = EFI_SUCCESS;
+  SwitchStatus = EFI_SUCCESS;
+
+  ASSERT (This);
+  ASSERT (Request);
+  ASSERT (Response);
+
+  HostInst = SDHC_INSTANCE_FROM_RPMB_IO_THIS (This);
+  ASSERT (HostInst);
+  ASSERT (HostInst->HostExt);
+
+  CurrentPartition = HostInst->CurrentMmcPartition;
+  SwitchStatus = SdhcSwitchPartitionMmc (HostInst, MmcExtCsdPartitionAccessRpmb);
+  if (EFI_ERROR (SwitchStatus)) {
+    LOG_ERROR (
+      "SdhcSwitchPartitionMmc() failed. (SwitchStatus = %r)",
+      SwitchStatus);
+
+    goto Exit;
+  }
+
+  SwitchPartition = TRUE;
+
+  ASSERT (Request->PacketCount > 0);
+  ASSERT (Request->Packets != NULL);
+  RequestType = RpmbBytesToUint16 (Request->Packets[0].RequestOrResponseType);
+
+  switch (RequestType) {
+  case EFI_RPMB_REQUEST_PROGRAM_KEY:
+    Status = RpmbProgramKeyRequest (HostInst, Request->Packets, Response->Packets);
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR ("RpmbProgramKeyRequest() failed. (Status = %r)", Status);
+      goto Exit;
+    }
+    break;
+
+  case EFI_RPMB_REQUEST_COUNTER_VALUE:
+    Status = RpmbReadCounterRequest (HostInst, Request->Packets, Response->Packets);
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR ("RpmbReadCounterRequest() failed. (Status = %r)", Status);
+      goto Exit;
+    }
+    break;
+
+  case EFI_RPMB_REQUEST_AUTH_READ:
+    Status = RpmbAuthenticatedReadRequest (HostInst, Request->Packets, Response);
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR ("RpmbAuthenticatedReadRequest() failed. (Status = %r)", Status);
+      goto Exit;
+    }
+    break;
+
+  case EFI_RPMB_REQUEST_AUTH_WRITE:
+    Status = RpmbAuthenticatedWriteRequest (HostInst, Request, Response->Packets);
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR ("RpmbAuthenticatedWriteRequest() failed. (Status = %r)", Status);
+      goto Exit;
+    }
+    break;
+
+  default:
+
+    ASSERT (FALSE);
+  }
+
+Exit:
+
+  if (SwitchPartition) {
+    SwitchStatus = SdhcSwitchPartitionMmc (HostInst, CurrentPartition);
+    if (EFI_ERROR (SwitchStatus)) {
+
+      LOG_ERROR (
+        "SdhcSwitchPartitionMmc() failed. (SwitchStatus = %r)",
+        SwitchStatus);
+    }
+  }
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  } else {
+    return SwitchStatus;
+  }
+}
+
+/** Authentication key programming request.
+
+  @param[in] This Indicates a pointer to the calling context.
+  @param[in] ProgrammingRequest A data packet describing a key programming request.
+  @param[out] ResultResponse A caller allocated data packet which will receive the
+  key programming result.
+
+  @retval EFI_SUCCESS RPMB communication sequence with the eMMC succeeded
+  according to specs, other values are returned in case of any protocol error.
+  Failure during key programming any other eMMC internal failure is reported
+  in the Result field of the returned response data packet.
+**/
+EFI_STATUS
+EFIAPI
+RpmbIoProgramKey (
+  IN EFI_RPMB_IO_PROTOCOL   *This,
+  IN EFI_RPMB_DATA_PACKET   *ProgrammingRequest,
+  OUT EFI_RPMB_DATA_PACKET  *ResultResponse
+  )
+{
+  EFI_RPMB_DATA_BUFFER  RequestBuffer;
+  EFI_RPMB_DATA_BUFFER  ResponseBuffer;
+  EFI_STATUS            Status;
+
+  LOG_TRACE ("RpmbIoProgramKey()");
+
+  if ((This == NULL) || (ProgrammingRequest == NULL) || (ResultResponse == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (RpmbBytesToUint16 (ProgrammingRequest->RequestOrResponseType) != EFI_RPMB_REQUEST_PROGRAM_KEY) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  RequestBuffer.PacketCount = 1;
+  RequestBuffer.Packets = ProgrammingRequest;
+
+  ResponseBuffer.PacketCount = 1;
+  ResponseBuffer.Packets = ResultResponse;
+
+  Status = RpmbRequest (This, &RequestBuffer, &ResponseBuffer);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("RpmbRequest() failed. (Status = %r)", Status);
+    return Status;
+  }
+
+  return Status;
+}
+
+/** Reading of the write counter value request.
+
+  @param[in] This Indicates a pointer to the calling context.
+  @param[in] ReadRequest A data packet describing a read counter value request.
+  @param[out] ReadResponse A caller allocated data packet which will receive
+  the counter value read response. If counter has expired bit 7 is set to 1 in
+  returned Result.
+
+  @retval EFI_SUCCESS RPMB communication sequence with the eMMC succeeded
+  according to specs, other values are returned in case of any protocol error.
+  Failure during counter read or any other eMMC internal failure is reported
+  in the Result field of the returned response data packet.
+**/
+EFI_STATUS
+EFIAPI
+RpmbIoReadCounter (
+  IN EFI_RPMB_IO_PROTOCOL   *This,
+  IN EFI_RPMB_DATA_PACKET   *ReadRequest,
+  OUT EFI_RPMB_DATA_PACKET  *ReadResponse
+  )
+{
+  EFI_RPMB_DATA_BUFFER  RequestBuffer;
+  EFI_RPMB_DATA_BUFFER  ResponseBuffer;
+  EFI_STATUS            Status;
+
+  LOG_TRACE ("RpmbIoReadCounter()");
+
+  if ((This == NULL) || (ReadRequest == NULL) || (ReadResponse == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (RpmbBytesToUint16 (ReadRequest->RequestOrResponseType) != EFI_RPMB_REQUEST_COUNTER_VALUE) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  RequestBuffer.PacketCount = 1;
+  RequestBuffer.Packets = ReadRequest;
+
+  ResponseBuffer.PacketCount = 1;
+  ResponseBuffer.Packets = ReadResponse;
+
+  Status = RpmbRequest (This, &RequestBuffer, &ResponseBuffer);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("RpmbRequest() failed. (Status = %r)", Status);
+    return Status;
+  }
+
+  return Status;
+}
+
+/** Authenticated data write request.
+
+  @param[in] This Indicates a pointer to the calling context.
+  @param[in] WriteRequest A sequence of data packets describing data write
+  requests and holds the data to be written.
+  @param[out] ResultResponse A caller allocated data packet which will receive
+  the data write programming result.
+
+  @retval EFI_SUCCESS RPMB communication sequence with the eMMC succeeded
+  according to specs, other values are returned in case of any protocol error.
+  Failure during data programming or any other eMMC internal failure is reported
+  in the Result field of the returned data packet.
+**/
+EFI_STATUS
+EFIAPI
+RpmbIoAuthenticatedWrite (
+  IN EFI_RPMB_IO_PROTOCOL   *This,
+  IN EFI_RPMB_DATA_BUFFER   *Request,
+  OUT EFI_RPMB_DATA_PACKET  *Response
+  )
+{
+  EFI_RPMB_DATA_BUFFER  ResponseBuffer;
+  EFI_STATUS            Status;
+
+  LOG_TRACE ("RpmbIoAuthenticatedWrite()");
+
+  if ((This == NULL) || (Request == NULL) || (Response == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((Request->Packets == NULL) || (Request->PacketCount == 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (RpmbBytesToUint16 (Request->Packets->RequestOrResponseType) != EFI_RPMB_REQUEST_AUTH_WRITE) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ResponseBuffer.PacketCount = 1;
+  ResponseBuffer.Packets = Response;
+
+  Status = RpmbRequest (This, Request, &ResponseBuffer);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("RpmbRequest() failed. (Status = %r)", Status);
+    return Status;
+  }
+
+  return Status;
+}
+
+/** Authenticated data read request.
+
+  @param[in] This Indicates a pointer to the calling context.
+  @param[in] ReadRequest A data packet that describes a data read request.
+  @param[out] ReadResponse A caller allocated data packets which will receive
+  the data read.
+
+  @retval EFI_SUCCESS RPMB communication sequence with the eMMC succeeded
+  according to specs, other values are returned in case of any protocol error.
+  Failure during data fetch from the eMMC or any other eMMC internal failure
+  is reported in the Result field of the returned data packet.
+**/
+EFI_STATUS
+EFIAPI
+RpmbIoAuthenticatedRead (
+  IN EFI_RPMB_IO_PROTOCOL   *This,
+  IN EFI_RPMB_DATA_PACKET   *ReadRequest,
+  OUT EFI_RPMB_DATA_BUFFER  *ReadResponse
+  )
+{
+  EFI_RPMB_DATA_BUFFER  RequestBuffer;
+  EFI_STATUS            Status;
+
+  LOG_TRACE ("RpmbIoAuthenticatedRead()");
+
+  if ((This == NULL) || (ReadRequest == NULL) || (ReadResponse == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((ReadResponse->Packets == NULL) || (ReadResponse->PacketCount == 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (RpmbBytesToUint16 (ReadRequest->RequestOrResponseType) != EFI_RPMB_REQUEST_AUTH_READ) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  RequestBuffer.PacketCount = 1;
+  RequestBuffer.Packets = ReadRequest;
+
+  Status = RpmbRequest (This, &RequestBuffer, ReadResponse);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("RpmbRequest() failed. (Status = %r)", Status);
+    return Status;
+  }
+
+  return Status;
+}
diff --git a/Platform/Microsoft/Drivers/SdMmcDxe/SdMmc.c b/Platform/Microsoft/Drivers/SdMmcDxe/SdMmc.c
new file mode 100644
index 000000000000..bdfb5bf84083
--- /dev/null
+++ b/Platform/Microsoft/Drivers/SdMmcDxe/SdMmc.c
@@ -0,0 +1,886 @@
+/** @file
+*
+*  Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+*  Copyright (c) 2011-2014, ARM Limited. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#include <Uefi.h>
+
+#include <Protocol/BlockIo.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/RpmbIo.h>
+#include <Protocol/Sdhc.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/TimerLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include "SdMmcHw.h"
+#include "SdMmc.h"
+#include "Protocol.h"
+
+// A template EFI_BLOCK_IO media to use for creating new EFI_BLOCK_IO protocols
+// for new SDHC instances.
+EFI_BLOCK_IO_MEDIA gSdhcMediaTemplate = {
+  0,                      // MediaId
+  TRUE,                   // RemovableMedia
+  FALSE,                  // MediaPresent
+  FALSE,                  // LogicalPartition
+  FALSE,                  // ReadOnly
+  FALSE,                  // WriteCaching
+  SD_BLOCK_LENGTH_BYTES,  // BlockSize
+  4,                      // IoAlign
+  0,                      // Pad
+  0                       // LastBlock
+};
+
+// This structure is serviced as a header.Its next field points to the first root
+// bridge device node.
+LIST_ENTRY  gSdhcInstancePool;
+UINT32 gNextSdhcInstanceId = 0;
+
+// Event triggered by the timer to check if any cards have been removed
+// or if new ones have been plugged in.
+EFI_EVENT gCheckCardsEvent;
+
+// The ARM high-performance counter frequency.
+UINT64 gHpcTicksPerSeconds = 0;
+
+VOID
+EFIAPI
+CheckCardsCallback (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  );
+
+EFI_STATUS
+EFIAPI
+UninstallAllProtocols (
+  IN SDHC_INSTANCE *HostInst
+  );
+
+BOOLEAN
+EFIAPI
+IsRpmbInstalledOnTheSystem (
+  VOID
+  );
+
+/** Initialize the SDHC Pool to support multiple SD/MMC devices.
+**/
+VOID
+InitializeSdhcPool (
+  VOID
+  )
+{
+  InitializeListHead (&gSdhcInstancePool);
+}
+
+/** Insert a new SDHC instance in the host pool.
+
+  @param[in] HostInst The SDHC instance context data.
+**/
+VOID
+InsertSdhcInstance (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  InsertTailList (&gSdhcInstancePool, &(HostInst->Link));
+}
+
+/** Removes an existing SDHC instance context data from the host pool.
+
+  @param[in] HostInst The SDHC instance data.
+**/
+VOID
+RemoveSdhcInstance (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  RemoveEntryList (&(HostInst->Link));
+}
+
+/** Creates a new SDHC instance context data.
+
+  It initializes the context data but does not attempt to perform any hardware
+  access nor install any EFI protocol. This happens later on when a card presence
+  state changes during the card check callback.
+
+  @param[in] HostExt The EFI_SDHC_PROTOCOL instance data to use as the basis for
+  SDHC instance context data creation.
+
+  @retval An SDHC instance context data on successful instance creation and return
+  NULL otherwise.
+**/
+SDHC_INSTANCE*
+CreateSdhcInstance (
+  IN EFI_SDHC_PROTOCOL  *HostExt
+  )
+{
+  SDHC_DEVICE_PATH  *DevicePath;
+  SDHC_INSTANCE     *HostInst;
+  GUID              SdhcDevicePathGuid = SDHC_DEVICE_PATH_GUID;
+  EFI_STATUS        Status;
+
+  HostInst = NULL;
+  HostInst = AllocateZeroPool (sizeof (SDHC_INSTANCE));
+  if (HostInst == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Exit;
+  }
+
+  HostInst->Signature = SDHC_INSTANCE_SIGNATURE;
+  HostInst->InstanceId = gNextSdhcInstanceId;
+  HostInst->HostExt = HostExt;
+
+  HostExt->GetCapabilities (HostExt, &HostInst->HostCapabilities);
+  ASSERT (HostInst->HostCapabilities.MaximumBlockSize > 0);
+  ASSERT (HostInst->HostCapabilities.MaximumBlockCount > 0);
+
+  // We will support 512 byte blocks only.
+  if (HostInst->HostCapabilities.MaximumBlockSize < SD_BLOCK_LENGTH_BYTES) {
+    LOG_ERROR (
+      "Unsupported max block size of %d bytes",
+      HostInst->HostCapabilities.MaximumBlockSize);
+    Status = EFI_UNSUPPORTED;
+    goto Exit;
+  }
+
+  LOG_TRACE (
+    "Host Capabilities: MaximumBlockSize:%d MaximumBlockCount:%d",
+    HostInst->HostCapabilities.MaximumBlockSize,
+    HostInst->HostCapabilities.MaximumBlockCount);
+
+  HostInst->DevicePathProtocolInstalled = FALSE;
+  HostInst->BlockIoProtocolInstalled = FALSE;
+  HostInst->RpmbIoProtocolInstalled = FALSE;
+
+  // Initialize BlockIo Protocol.
+  HostInst->BlockIo.Media = AllocateCopyPool (sizeof (EFI_BLOCK_IO_MEDIA), &gSdhcMediaTemplate);
+  if (HostInst->BlockIo.Media == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Exit;
+  }
+
+  HostInst->BlockIo.Revision = EFI_BLOCK_IO_INTERFACE_REVISION;
+  HostInst->BlockIo.Reset = BlockIoReset;
+  HostInst->BlockIo.ReadBlocks = BlockIoReadBlocks;
+  HostInst->BlockIo.WriteBlocks = BlockIoWriteBlocks;
+  HostInst->BlockIo.FlushBlocks = BlockIoFlushBlocks;
+
+  // Initialize DevicePath Protocol.
+  DevicePath = &HostInst->DevicePath;
+
+  // Initialize device path based on SDHC DeviceId and delay SlotNode initialization
+  // until the card in Slot0 is type identified.
+  DevicePath->SdhcNode.Header.Type = HARDWARE_DEVICE_PATH;
+  DevicePath->SdhcNode.Header.SubType = HW_VENDOR_DP;
+  *((UINT16*) &DevicePath->SdhcNode.Header.Length) = SDHC_NODE_PATH_LENGTH;
+  DevicePath->SdhcId = HostInst->HostExt->SdhcId;
+  CopyGuid (&DevicePath->SdhcNode.Guid, &SdhcDevicePathGuid);
+
+  SetDevicePathEndNode (&DevicePath->EndNode);
+
+  // Initialize RpmbIo Protocol.
+  HostInst->RpmbIo.Revision = EFI_RPMB_IO_PROTOCOL_REVISION;
+  HostInst->RpmbIo.AuthenticatedRead = RpmbIoAuthenticatedRead;
+  HostInst->RpmbIo.AuthenticatedWrite = RpmbIoAuthenticatedWrite;
+  HostInst->RpmbIo.ProgramKey = RpmbIoProgramKey;
+  HostInst->RpmbIo.ReadCounter = RpmbIoReadCounter;
+
+  // Don't publish any protocol yet, until the SDHC device is fully initialized and
+  // ready for IO.
+  ++gNextSdhcInstanceId;
+
+  Status = EFI_SUCCESS;
+
+Exit:
+  if (EFI_ERROR (Status)) {
+    if (HostInst != NULL && HostInst->BlockIo.Media != NULL) {
+      FreePool (HostInst->BlockIo.Media);
+      HostInst->BlockIo.Media = NULL;
+    }
+
+    if (HostInst != NULL) {
+      FreePool (HostInst);
+      HostInst = NULL;
+    }
+  }
+
+  return HostInst;
+}
+
+/** Destroys an existing SDHC instance context data.
+
+  It uninstalls all protocols installed on that instance, free allocated memory
+  and invokes the SDHC clean-up callback.
+
+  @param[in] HostInst The SDHC instance context data to destroy.
+
+  @retval EFI_SUCCESS on successful destruction and cleanup, return an EFI error
+  code otherwise.
+**/
+EFI_STATUS
+DestroySdhcInstance (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  EFI_STATUS Status;
+
+  Status = UninstallAllProtocols (HostInst);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Free Memory allocated for the EFI_BLOCK_IO protocol
+  if (HostInst->BlockIo.Media) {
+    FreePool (HostInst->BlockIo.Media);
+  }
+
+  HostInst->HostExt->Cleanup (HostInst->HostExt);
+  HostInst->HostExt = NULL;
+
+  FreePool (HostInst);
+
+  return EFI_SUCCESS;
+}
+
+// UEFI Driver Model EFI_DRIVER_BINDING_PROTOCOL Callbacks
+
+/**
+    Tests to see if this driver supports a given controller. If a child device is provided,
+    it further tests to see if this driver supports creating a handle for the specified child device.
+
+    @param This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+    @param ControllerHandle     The handle of the controller to test. This handle must support a protocol
+                                interface that supplies an I/O abstraction to the driver. Sometimes
+                                just the presence of this I/O abstraction is enough for the driver to
+                                determine if it supports ControllerHandle. Sometimes, the driver may
+                                use the services of the I/O abstraction to determine if this driver
+                                supports ControllerHandle.
+    @param RemainingDevicePath  A pointer to the remaining portion of a device path. For bus drivers,
+                                if this parameter is not NULL, then the bus driver must determine if
+                                the bus controller specified by ControllerHandle and the child controller
+                                specified by RemainingDevicePath
+**/
+EFI_STATUS
+EFIAPI
+SdMmcDriverSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_SDHC_PROTOCOL   *HostExt;
+  EFI_DEV_PATH_PTR    Node;
+  EFI_STATUS          Status;
+
+  // 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)) {
+        Status = EFI_UNSUPPORTED;
+        goto Exit;
+      }
+    }
+  }
+
+  // Check if SDHC protocol is installed by platform.
+
+  Status = gBS->OpenProtocol (
+    Controller,
+    &gEfiSdhcProtocolGuid,
+    (VOID **) &HostExt,
+    This->DriverBindingHandle,
+    Controller,
+    EFI_OPEN_PROTOCOL_BY_DRIVER);
+  if (Status == EFI_ALREADY_STARTED) {
+    Status = EFI_SUCCESS;
+    goto Exit;
+  }
+
+  if (EFI_ERROR (Status)) {
+    goto Exit;
+  }
+
+  // Close the SDHC used to perform the supported test.
+  gBS->CloseProtocol (
+    Controller,
+    &gEfiSdhcProtocolGuid,
+    This->DriverBindingHandle,
+    Controller);
+
+Exit:
+  return Status;
+}
+
+/**
+    Starts a device controller or a bus controller. The Start() and Stop() services of the
+    EFI_DRIVER_BINDING_PROTOCOL mirror each other.
+
+    @param This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+    @param ControllerHandle     The handle of the controller to start. This handle must support a
+                                protocol interface that supplies an I/O abstraction to the driver.
+    @param RemainingDevicePath  A pointer to the remaining portion of a device path. For a bus driver,
+                                if this parameter is NULL, then handles for all the children of Controller
+                                are created by this driver.
+                                If this parameter is not NULL and the first Device Path Node is not
+                                the End of Device Path Node, then only the handle for the child device
+                                specified by the first Device Path Node of RemainingDevicePath is created
+                                by this driver.
+                                If the first Device Path Node of RemainingDevicePath is the End of Device
+                                Path Node, no child handle is created by this driver.
+**/
+EFI_STATUS
+EFIAPI
+SdMmcDriverStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_SDHC_PROTOCOL   *HostExt;
+  SDHC_INSTANCE       *HostInst;
+  EFI_STATUS          Status;
+
+  LOG_TRACE ("SdMmcDriverStart()");
+
+  // Check RemainingDevicePath validation.
+  if (RemainingDevicePath != NULL) {
+
+    // Check if RemainingDevicePath is the End of Device Path Node,
+    // if yes, return EFI_SUCCESS.
+    if (IsDevicePathEnd (RemainingDevicePath)) {
+      Status = EFI_SUCCESS;
+      goto Exit;
+    }
+  }
+
+  // Get the SDHC protocol.
+
+  Status = gBS->OpenProtocol (
+    Controller,
+    &gEfiSdhcProtocolGuid,
+    (VOID **) &HostExt,
+    This->DriverBindingHandle,
+    Controller,
+    EFI_OPEN_PROTOCOL_BY_DRIVER);
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_ALREADY_STARTED) {
+      Status = EFI_SUCCESS;
+    }
+    goto Exit;
+  }
+
+  HostInst = CreateSdhcInstance (HostExt);
+  if (HostInst != NULL) {
+    HostInst->MmcHandle = Controller;
+    InsertSdhcInstance (HostInst);
+
+    LOG_INFO (
+      "SDHC%d instance creation completed. Detecting card presence...",
+      HostInst->HostExt->SdhcId);
+
+    // Detect card presence now which will initialize the SDHC.
+    CheckCardsCallback (NULL, NULL);
+  } else {
+    LOG_ERROR ("CreateSdhcInstance failed. %r", Status);
+  }
+
+Exit:
+  return Status;
+}
+
+/**
+    Stops a device controller or a bus controller. The Start() and Stop() services of the
+    EFI_DRIVER_BINDING_PROTOCOL mirror each other.
+
+    @param This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+                                Type EFI_DRIVER_BINDING_PROTOCOL is defined in Section 10.1.
+    @param ControllerHandle     A handle to the device being stopped. The handle must support a bus
+                                specific I/O protocol for the driver to use to stop the device.
+    @param NumberOfChildren     The number of child device handles in ChildHandleBuffer.
+    @param ChildHandleBuffer    An array of child handles to be freed. May be NULL if NumberOfChildren is 0.
+**/
+EFI_STATUS
+EFIAPI
+SdMmcDriverStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN UINTN                        NumberOfChildren,
+  IN EFI_HANDLE                   *ChildHandleBuffer
+  )
+{
+  LIST_ENTRY      *CurrentLink;
+  SDHC_INSTANCE   *HostInst;
+  EFI_STATUS      Status;
+
+  Status = EFI_SUCCESS;
+
+  LOG_TRACE ("SdMmcDriverStop()");
+
+  // For each registered SDHC instance.
+  CurrentLink = gSdhcInstancePool.ForwardLink;
+  while (CurrentLink != NULL && CurrentLink != &gSdhcInstancePool && (Status == EFI_SUCCESS)) {
+    HostInst = SDHC_INSTANCE_FROM_LINK (CurrentLink);
+    ASSERT (HostInst != NULL);
+
+    // Close gEfiMmcHostProtocolGuid opened on the SDHC.
+    Status = gBS->CloseProtocol (
+      Controller,
+      &gEfiSdhcProtocolGuid,
+      (VOID **) &HostInst->HostExt,
+      This->DriverBindingHandle);
+
+    // Remove SDHC instance from the pool.
+    RemoveSdhcInstance (HostInst);
+
+    // Destroy SDHC instance.
+    DestroySdhcInstance (HostInst);
+  }
+
+  return Status;
+}
+
+EFI_STATUS
+EFIAPI
+SoftReset (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  SDHC_DEVICE_PATH    *DevicePath;
+  CHAR16              *DevicePathText;
+  EFI_SDHC_PROTOCOL   *HostExt;
+  MMC_EXT_CSD_PARTITION_CONFIG PartConfig;
+  EFI_STATUS          Status;
+
+  HostExt = HostInst->HostExt;
+  DevicePathText = NULL;
+
+  ASSERT (HostInst->HostExt != NULL);
+  LOG_TRACE ("Performing Soft-Reset for SDHC%d", HostExt->SdhcId);
+
+  Status = UninstallAllProtocols (HostInst);
+  if (EFI_ERROR (Status)) {
+    goto Exit;
+  }
+
+  HostInst->SlotInitialized = FALSE;
+
+  // Clear all media settings regardless of card presence.
+  HostInst->BlockIo.Media->MediaId = 0;
+  HostInst->BlockIo.Media->RemovableMedia = FALSE;
+  HostInst->BlockIo.Media->MediaPresent = FALSE;
+  HostInst->BlockIo.Media->LogicalPartition = FALSE;
+  HostInst->BlockIo.Media->WriteCaching = FALSE;
+  HostInst->BlockIo.Media->BlockSize = 0;
+  HostInst->BlockIo.Media->IoAlign = 4;
+  HostInst->BlockIo.Media->LastBlock = 0;
+  HostInst->BlockIo.Media->LowestAlignedLba = 0;
+  HostInst->BlockIo.Media->LogicalBlocksPerPhysicalBlock = 0;
+  HostInst->BlockIo.Media->OptimalTransferLengthGranularity = 0;
+  HostInst->BlockIo.Media->ReadOnly = FALSE;
+
+  HostInst->RpmbIo.ReliableSectorCount = 0;
+  HostInst->RpmbIo.RpmbSizeMult = 0;
+  ZeroMem (HostInst->RpmbIo.Cid, sizeof (HostInst->RpmbIo.Cid));
+
+  HostInst->BlockIo.Media->MediaPresent = HostInst->HostExt->IsCardPresent (HostInst->HostExt);
+  if (!HostInst->BlockIo.Media->MediaPresent) {
+    // Even if the media is not present, we`d like to communicate that status up
+    // to the storage stack by means of installing the BlockIo protocol.
+    Status =
+      gBS->InstallMultipleProtocolInterfaces (
+        &HostInst->MmcHandle,
+        &gEfiBlockIoProtocolGuid,
+        &HostInst->BlockIo,
+        NULL);
+
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR (
+        "SoftReset(): Failed installing EFI_BLOCK_IO_PROTOCOL interface. %r",
+        Status);
+
+      goto Exit;
+    }
+
+    HostInst->BlockIoProtocolInstalled = TRUE;
+    LOG_INFO ("SDHC%d media not present, skipping device initialization", HostExt->SdhcId);
+    goto Exit;
+  } else {
+    HostInst->BlockIo.Media->ReadOnly = HostExt->IsReadOnly (HostExt);
+    if (HostInst->BlockIo.Media->ReadOnly) {
+      LOG_INFO ("SDHC%d media is read-only", HostExt->SdhcId);
+    }
+  }
+
+  Status = InitializeDevice (HostInst);
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR ("SoftReset(): InitializeDevice() failed. %r", Status);
+    goto Exit;
+  }
+
+  // Update SlotNode subtype based on the card type identified.
+  // Note that we only support 1 slot per SDHC, i.e It is assumed that no more
+  // than 1 SD/MMC card connected to the same SDHC block on the system.
+  DevicePath = &HostInst->DevicePath;
+  if (HostInst->CardInfo.CardFunction == CardFunctionSd) {
+    DevicePath->SlotNode.SD.Header.Type = MESSAGING_DEVICE_PATH;
+    DevicePath->SlotNode.SD.Header.SubType = MSG_SD_DP;
+    *((UINT16*) &DevicePath->SlotNode.SD.Header.Length) = sizeof (SD_DEVICE_PATH);
+    DevicePath->SlotNode.SD.SlotNumber = 0;
+  } else {
+    ASSERT (HostInst->CardInfo.CardFunction == CardFunctionMmc);
+    DevicePath->SlotNode.MMC.Header.Type = MESSAGING_DEVICE_PATH;
+    DevicePath->SlotNode.MMC.Header.SubType = MSG_EMMC_DP;
+    *((UINT16*) &DevicePath->SlotNode.MMC.Header.Length) = sizeof (EMMC_DEVICE_PATH);
+    DevicePath->SlotNode.MMC.SlotNumber = 0;
+  }
+
+  // Print a text representation of the Slot device path.
+  DevicePathText = ConvertDevicePathToText ((EFI_DEVICE_PATH_PROTOCOL*) DevicePath, FALSE, FALSE);
+
+  if (DevicePathText == NULL) {
+    LOG_ERROR ("SoftReset(): ConvertDevicePathToText() failed.");
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Exit;
+  }
+
+  LOG_INFO ("Recognized device: %s", DevicePathText);
+
+  Status =
+    gBS->InstallMultipleProtocolInterfaces (
+      &HostInst->MmcHandle,
+      &gEfiBlockIoProtocolGuid,
+      &HostInst->BlockIo,
+      NULL);
+
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR (
+      "SoftReset(): Failed installing EFI_BLOCK_IO_PROTOCOL interface. %r",
+      Status);
+
+    goto Exit;
+  }
+
+  HostInst->BlockIoProtocolInstalled = TRUE;
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+      &HostInst->MmcHandle,
+      &gEfiDevicePathProtocolGuid,
+      &HostInst->DevicePath,
+      NULL);
+
+  if (EFI_ERROR (Status)) {
+    LOG_ERROR (
+      "SoftReset(): Failed installing EFI_DEVICE_PATH_PROTOCOL interface. %r",
+      Status);
+
+    goto Exit;
+  }
+
+  HostInst->DevicePathProtocolInstalled = TRUE;
+
+  if (HostInst->CardInfo.CardFunction == CardFunctionMmc) {
+    if (!IsRpmbInstalledOnTheSystem ()) {
+      Status = gBS->InstallMultipleProtocolInterfaces (
+          &HostInst->MmcHandle,
+          &gEfiRpmbIoProtocolGuid,
+          &HostInst->RpmbIo,
+          NULL);
+
+      if (EFI_ERROR (Status)) {
+        LOG_ERROR (
+          "SoftReset(): Failed installing EFI_RPMB_IO_PROTOCOL interface. %r",
+          Status);
+
+        goto Exit;
+      }
+
+      LOG_INFO ("RpmbIo protocol installed for MMC: %s", DevicePathText);
+
+      HostInst->RpmbIoProtocolInstalled = TRUE;
+
+      // Track current partition in a separate variable to void having to
+      // ready the MMC EXT_CSD everytime we do partition switch to have
+      // an updated current partition. SdhostSwitchPartitionMmc will keep
+      // track of that variable.
+      PartConfig.AsUint8 = HostInst->CardInfo.Registers.Mmc.ExtCsd.PartitionConfig;
+      HostInst->CurrentMmcPartition =
+        (MMC_EXT_CSD_PARTITION_ACCESS) PartConfig.Fields.PARTITION_ACCESS;
+
+    } else {
+      LOG_ERROR (
+        "SoftReset(): RpmbIo protocol is already installed on the system. "
+        "The requirement is that only 1 MMC on the system should have RpmbIo "
+        "protocol installed. Skipping RpmbIo protocol installation for %s",
+        DevicePathText);
+    }
+  }
+
+  LOG_TRACE ("All required protocols installed successfully for %s", DevicePathText);
+
+Exit:
+
+  // On error we should uninstall all protocols except BlockIo, it should
+  // always be present since it represents the SDHC physical existance not
+  // the card, other protocols are card dependant and are pure representation
+  // of one or more card features
+  if (EFI_ERROR (Status)) {
+    if (HostInst->RpmbIoProtocolInstalled) {
+      Status = gBS->UninstallMultipleProtocolInterfaces (
+          HostInst->MmcHandle,
+          &gEfiRpmbIoProtocolGuid,
+          &HostInst->RpmbIo,
+          NULL);
+
+      if (EFI_ERROR (Status)) {
+        LOG_ERROR (
+          "SoftReset(): Failed to uninstall EFI_RPMB_IO_PROTOCOL interface. %r",
+          Status);
+      } else {
+        HostInst->RpmbIoProtocolInstalled = FALSE;
+      }
+    }
+
+    if (HostInst->DevicePathProtocolInstalled) {
+      Status = gBS->UninstallMultipleProtocolInterfaces (
+          HostInst->MmcHandle,
+          &gEfiDevicePathProtocolGuid,
+          &HostInst->DevicePath,
+          NULL);
+
+      if (EFI_ERROR (Status)) {
+        LOG_ERROR (
+          "SoftReset(): Failed to uninstall EFI_DEVICE_PATH_PROTOCOL interface. %r",
+          Status);
+      } else {
+        HostInst->DevicePathProtocolInstalled = FALSE;
+      }
+    }
+  }
+
+  if (DevicePathText != NULL) {
+    FreePool (DevicePathText);
+  }
+
+  return Status;
+}
+
+VOID
+EFIAPI
+CheckCardsCallback (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  LIST_ENTRY      *CurrentLink;
+  SDHC_INSTANCE   *HostInst;
+  BOOLEAN         CardInserted;
+  BOOLEAN         CardEjected;
+  BOOLEAN         IsCardPresent;
+  EFI_STATUS      Status;
+
+  // For each registered SDHC instance
+  CurrentLink = gSdhcInstancePool.ForwardLink;
+  while (CurrentLink != NULL && CurrentLink != &gSdhcInstancePool) {
+    HostInst = SDHC_INSTANCE_FROM_LINK (CurrentLink);
+    ASSERT (HostInst != NULL);
+
+    IsCardPresent = HostInst->HostExt->IsCardPresent (HostInst->HostExt);
+
+    // If card is present and not initialized or card no more present but was previously
+    // initialized, then reset the instance
+    //
+    // Present Initialized  Outcome
+    // T       T            No action
+    // T       F            Reset
+    // F       T            Reset
+    // F       F            No action
+    CardEjected = HostInst->BlockIo.Media->MediaPresent && !IsCardPresent;
+    if (CardEjected) {
+      LOG_INFO ("Card ejected from SDHC%d slot", HostInst->HostExt->SdhcId);
+    }
+
+    CardInserted = !HostInst->BlockIo.Media->MediaPresent && IsCardPresent;
+    if (CardInserted) {
+      LOG_INFO ("Card inserted into SDHC%d slot", HostInst->HostExt->SdhcId);
+    }
+
+    if (CardEjected || CardInserted) {
+      Status = SoftReset (HostInst);
+      if (EFI_ERROR (Status)) {
+        LOG_ERROR ("SoftReset() failed. %r", Status);
+      }
+    }
+
+    CurrentLink = CurrentLink->ForwardLink;
+  }
+}
+
+BOOLEAN
+EFIAPI
+IsRpmbInstalledOnTheSystem (
+  VOID
+  )
+{
+  EFI_RPMB_IO_PROTOCOL  RpmbIo;
+  EFI_STATUS            Status;
+
+  Status = gBS->LocateProtocol (
+    &gEfiRpmbIoProtocolGuid,
+    NULL,
+    (VOID **) &RpmbIo);
+  if (EFI_ERROR (Status)) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+EFI_STATUS
+EFIAPI
+UninstallAllProtocols (
+  IN SDHC_INSTANCE  *HostInst
+  )
+{
+  EFI_STATUS Status;
+
+  LOG_TRACE ("Uninstalling SDHC%d all protocols", HostInst->HostExt->SdhcId);
+
+  if (HostInst->BlockIoProtocolInstalled) {
+    Status =
+      gBS->UninstallMultipleProtocolInterfaces (
+        HostInst->MmcHandle,
+        &gEfiBlockIoProtocolGuid,
+        &HostInst->BlockIo,
+        NULL);
+
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR (
+        "UninstallAllProtocols(): Failed to uninstall EFI_BLOCK_IO_PROTOCOL. "
+        "(Status = %r)",
+        Status);
+
+      return Status;
+    }
+
+    HostInst->BlockIoProtocolInstalled = FALSE;
+  }
+
+  if (HostInst->RpmbIoProtocolInstalled) {
+    Status = gBS->UninstallMultipleProtocolInterfaces (
+        HostInst->MmcHandle,
+        &gEfiRpmbIoProtocolGuid,
+        &HostInst->RpmbIo,
+        NULL);
+
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR (
+        "UninstallAllProtocols(): Failed to uninstall EFI_RPMB_IO_PROTOCOL. "
+        "(Status = %r)",
+        Status);
+
+      return Status;
+    }
+
+    HostInst->RpmbIoProtocolInstalled = FALSE;
+  }
+
+  if (HostInst->DevicePathProtocolInstalled) {
+    Status = gBS->UninstallMultipleProtocolInterfaces (
+        HostInst->MmcHandle,
+        &gEfiDevicePathProtocolGuid,
+        &HostInst->DevicePath,
+        NULL);
+
+    if (EFI_ERROR (Status)) {
+      LOG_ERROR (
+        "UninstallAllProtocols(): Failed to uninstall EFI_DEVICE_PATH_PROTOCOL. "
+        "(Status = %r)",
+        Status);
+
+      return Status;
+    }
+
+    HostInst->DevicePathProtocolInstalled = FALSE;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_DRIVER_BINDING_PROTOCOL gSdMmcDriverBindingCallbacks = {
+  SdMmcDriverSupported,
+  SdMmcDriverStart,
+  SdMmcDriverStop,
+  0xa,
+  NULL,
+  NULL
+};
+
+EFI_STATUS
+EFIAPI
+SdMmcDxeInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_STATUS Status;
+
+  LOG_TRACE ("SdMmcDxeInitialize");
+
+  InitializeSdhcPool ();
+
+  // Install driver model protocols.
+  Status = EfiLibInstallDriverBindingComponentName2 (
+    ImageHandle,
+    SystemTable,
+    &gSdMmcDriverBindingCallbacks,
+    ImageHandle,
+    NULL,
+    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 * SDMMC_CHECK_CARD_INTERVAL_MS)); // 200 ms
+  ASSERT_EFI_ERROR (Status);
+
+  gHpcTicksPerSeconds = GetPerformanceCounterProperties (NULL, NULL);
+  ASSERT (gHpcTicksPerSeconds != 0);
+
+  return Status;
+}
diff --git a/Platform/Microsoft/Drivers/SdMmcDxe/SdMmc.h b/Platform/Microsoft/Drivers/SdMmcDxe/SdMmc.h
new file mode 100644
index 000000000000..44aa75a665e5
--- /dev/null
+++ b/Platform/Microsoft/Drivers/SdMmcDxe/SdMmc.h
@@ -0,0 +1,529 @@
+/** @file
+*
+*  Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+*  Copyright (c) 2011-2014, ARM Limited. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#ifndef __SDMMC_H__
+#define __SDMMC_H__
+
+// Define with non-zero to collect IO statistics and dump it to the terminal.
+#define SDMMC_COLLECT_STATISTICS  0
+
+// Define with non-zero to benchmark IO on the first MmcReadBlocks call and
+// dump to the terminal.
+#define SDMMC_BENCHMARK_IO        0
+
+// Lower bound of 2s poll wait time (200 x 10ms)
+#define SDMMC_POLL_WAIT_COUNT     200
+#define SDMMC_POLL_WAIT_TIME_US   10000
+
+// The period at which to check the presence state of each card on each
+// registered SDHC instance.
+#define SDMMC_CHECK_CARD_INTERVAL_MS 1000
+
+// The number of recursive error recoveries to reach before considering the
+// failure fatal, and not attempting more error recoveries.
+#define SDMMC_ERROR_RECOVERY_ATTEMPT_THRESHOLD    3
+
+// Logging Macros
+
+#define LOG_TRACE_FMT_HELPER(FMT, ...)  "SdMmc[T]:" FMT "%a\n", __VA_ARGS__
+
+#define LOG_INFO_FMT_HELPER(FMT, ...)   "SdMmc[I]:" FMT "%a\n", __VA_ARGS__
+
+#define LOG_ERROR_FMT_HELPER(FMT, ...) \
+  "SdMmc[E]:" FMT " (%a: %a, %d)\n", __VA_ARGS__
+
+#define LOG_INFO(...) \
+  DEBUG((DEBUG_INIT, LOG_INFO_FMT_HELPER(__VA_ARGS__, "")))
+
+#define LOG_VANILLA_TRACE(...) \
+  DEBUG((DEBUG_VERBOSE | DEBUG_BLKIO, __VA_ARGS__))
+
+#define LOG_TRACE(...) \
+  DEBUG((DEBUG_VERBOSE | DEBUG_BLKIO, LOG_TRACE_FMT_HELPER(__VA_ARGS__, "")))
+
+#define LOG_ERROR(...) \
+  DEBUG((DEBUG_ERROR, LOG_ERROR_FMT_HELPER(__VA_ARGS__, __FUNCTION__, __FILE__, __LINE__)))
+
+#define LOG_ASSERT(TXT) ASSERT(!"SdMmc[A]: " TXT "\n")
+
+#ifndef C_ASSERT
+#define C_ASSERT(e) _Static_assert(e, #e)
+#endif // C_ASSERT
+
+// Perform Integer division DIVIDEND/DIVISOR and return the result rounded up
+// or down to the nearest integer, where 3.5 and 3.75 are near 4, while 3.25
+// is near 3.
+#define INT_DIV_ROUND(DIVIDEND, DIVISOR) \
+  (((DIVIDEND) + ((DIVISOR) / 2)) / (DIVISOR))
+
+typedef struct {
+  UINT16  NumBlocks;
+  UINT16  Count;
+  UINT32  TotalTransferTimeUs;
+} IoReadStatsEntry;
+
+// Device Path Definitions
+//
+// eMMC and SD device paths got introduced in UEFI 2.6
+// Remove definitions below once code base migrates to UEFI 2.6
+
+#ifndef MSG_SD_DP
+// SD (Secure Digital) Device Path SubType.
+#define MSG_SD_DP   0x1A
+
+typedef struct {
+  EFI_DEVICE_PATH_PROTOCOL  Header;
+  UINT8                     SlotNumber;
+} SD_DEVICE_PATH;
+#endif // MSG_SD_DP
+
+#ifndef MSG_EMMC_DP
+// EMMC (Embedded MMC) Device Path SubType.
+#define MSG_EMMC_DP   0x1D
+
+typedef struct {
+  EFI_DEVICE_PATH_PROTOCOL  Header;
+  UINT8                     SlotNumber;
+} EMMC_DEVICE_PATH;
+#endif // MSG_EMMC_DP
+
+typedef struct {
+  VENDOR_DEVICE_PATH  SdhcNode;
+  UINT32              SdhcId;
+  union {
+    EMMC_DEVICE_PATH  MMC;
+    SD_DEVICE_PATH    SD;
+  } SlotNode;
+  EFI_DEVICE_PATH     EndNode;
+} SDHC_DEVICE_PATH;
+
+// GUID {AAFB8DAA-7340-43AC-8D49-0CCE14812489}
+#define SDHC_DEVICE_PATH_GUID \
+  { 0xaafb8daa, 0x7340, 0x43ac, { 0x8d, 0x49, 0xc, 0xce, 0x14, 0x81, 0x24, 0x89 } }
+
+// Size of SDHC node including the Sdhc ID field
+#define SDHC_NODE_PATH_LENGTH (sizeof(VENDOR_DEVICE_PATH) + sizeof(UINT32))
+
+typedef struct {
+  UINTN                         Signature;
+  LIST_ENTRY                    Link;
+  UINT32                        InstanceId;
+  EFI_HANDLE                    MmcHandle;
+  BOOLEAN                       SlotInitialized;
+  BOOLEAN                       Disabled;
+  SDHC_DEVICE_PATH              DevicePath;
+  EFI_BLOCK_IO_PROTOCOL         BlockIo;
+  EFI_RPMB_IO_PROTOCOL          RpmbIo;
+  EFI_SDHC_PROTOCOL             *HostExt;
+  SDHC_CAPABILITIES             HostCapabilities;
+  BOOLEAN                       DevicePathProtocolInstalled;
+  BOOLEAN                       BlockIoProtocolInstalled;
+  BOOLEAN                       RpmbIoProtocolInstalled;
+  MMC_EXT_CSD_PARTITION_ACCESS  CurrentMmcPartition;
+  CARD_INFO                     CardInfo;
+  UINT32                        BlockBuffer[SD_BLOCK_WORD_COUNT];
+  UINT32                        CmdResponse[4];
+  CONST SD_COMMAND              *PreLastSuccessfulCmd;
+  CONST SD_COMMAND              *LastSuccessfulCmd;
+  UINT32                        ErrorRecoveryAttemptCount;
+#ifdef MMC_COLLECT_STATISTICS
+  IoReadStatsEntry              IoReadStats[1024];
+  UINT32                        IoReadStatsNumEntries;
+#endif // COLLECT_IO_STATISTICS
+} SDHC_INSTANCE;
+
+#define SDHC_INSTANCE_SIGNATURE   SIGNATURE_32('s', 'd', 'h', 'c')
+#define SDHC_INSTANCE_FROM_BLOCK_IO_THIS(a) \
+  CR(a, SDHC_INSTANCE, BlockIo, SDHC_INSTANCE_SIGNATURE)
+#define SDHC_INSTANCE_FROM_LINK(a) \
+  CR(a, SDHC_INSTANCE, Link, SDHC_INSTANCE_SIGNATURE)
+#define SDHC_INSTANCE_FROM_RPMB_IO_THIS(a) \
+  CR (a, SDHC_INSTANCE, RpmbIo, SDHC_INSTANCE_SIGNATURE)
+
+// The ARM high-performance counter frequency
+extern UINT64 gHpcTicksPerSeconds;
+
+// EFI_BLOCK_IO Protocol Callbacks
+
+/**
+  Reset the block device.
+
+  This function implements EFI_BLOCK_IO_PROTOCOL.Reset().
+  It resets the block device hardware.
+  ExtendedVerification is ignored in this implementation.
+
+  @param  This                   Indicates a pointer to the calling context.
+  @param  ExtendedVerification   Indicates that the driver may perform a more exhaustive
+                                 verification operation of the device during reset.
+
+  @retval EFI_SUCCESS            The block device was reset.
+  @retval EFI_DEVICE_ERROR       The block device is not functioning correctly and could not be reset.
+
+**/
+EFI_STATUS
+EFIAPI
+BlockIoReset (
+  IN EFI_BLOCK_IO_PROTOCOL  *This,
+  IN BOOLEAN                ExtendedVerification
+  );
+
+/**
+  Reads the requested number of blocks from the device.
+
+  This function implements EFI_BLOCK_IO_PROTOCOL.ReadBlocks().
+  It reads the requested number of blocks from the device.
+  All the blocks are read, or an error is returned.
+
+  @param  This                   Indicates a pointer to the calling context.
+  @param  MediaId                The media ID that the read request is for.
+  @param  Lba                    The starting logical block address to read from on the device.
+  @param  BufferSize             The size of the Buffer in bytes.
+                                 This must be a multiple of the intrinsic block size of the device.
+  @param  Buffer                 A pointer to the destination buffer for the data. The caller is
+                                 responsible for either having implicit or explicit ownership of the buffer.
+
+  @retval EFI_SUCCESS            The data was read correctly from the device.
+  @retval EFI_DEVICE_ERROR       The device reported an error while attempting to perform the read operation.
+  @retval EFI_NO_MEDIA           There is no media in the device.
+  @retval EFI_MEDIA_CHANGED      The MediaId is not for the current media.
+  @retval EFI_BAD_BUFFER_SIZE    The BufferSize parameter is not a multiple of the intrinsic block size of the device.
+  @retval EFI_INVALID_PARAMETER  The read request contains LBAs that are not valid,
+                                 or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+BlockIoReadBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL  *This,
+  IN UINT32                 MediaId,
+  IN EFI_LBA                Lba,
+  IN UINTN                  BufferSize,
+  OUT VOID                  *Buffer
+  );
+
+/**
+  Writes a specified number of blocks to the device.
+
+  This function implements EFI_BLOCK_IO_PROTOCOL.WriteBlocks().
+  It writes a specified number of blocks to the device.
+  All blocks are written, or an error is returned.
+
+  @param  This                   Indicates a pointer to the calling context.
+  @param  MediaId                The media ID that the write request is for.
+  @param  Lba                    The starting logical block address to be written.
+  @param  BufferSize             The size of the Buffer in bytes.
+                                 This must be a multiple of the intrinsic block size of the device.
+  @param  Buffer                 Pointer to the source buffer for the data.
+
+  @retval EFI_SUCCESS            The data were written correctly to the device.
+  @retval EFI_WRITE_PROTECTED    The device cannot be written to.
+  @retval EFI_NO_MEDIA           There is no media in the device.
+  @retval EFI_MEDIA_CHANGED      The MediaId is not for the current media.
+  @retval EFI_DEVICE_ERROR       The device reported an error while attempting to perform the write operation.
+  @retval EFI_BAD_BUFFER_SIZE    The BufferSize parameter is not a multiple of the intrinsic
+                                 block size of the device.
+  @retval EFI_INVALID_PARAMETER  The write request contains LBAs that are not valid,
+                                 or the buffer is not on proper alignment.
+
+**/
+EFI_STATUS
+EFIAPI
+BlockIoWriteBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL  *This,
+  IN UINT32                 MediaId,
+  IN EFI_LBA                Lba,
+  IN UINTN                  BufferSize,
+  IN VOID                   *Buffer
+  );
+
+/**
+  Flushes all modified data to a physical block device.
+
+  @param  This                   Indicates a pointer to the calling context.
+
+  @retval EFI_SUCCESS            All outstanding data were written correctly to the device.
+  @retval EFI_DEVICE_ERROR       The device reported an error while attempting to write data.
+  @retval EFI_NO_MEDIA           There is no media in the device.
+
+**/
+EFI_STATUS
+EFIAPI
+BlockIoFlushBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL *This
+  );
+
+// EFI_RPMPB_IO Protocol Callbacks
+
+/** Authentication key programming request.
+
+  @param[in] This Indicates a pointer to the calling context.
+  @param[in] ProgrammingRequest A data packet describing a key programming request.
+  @param[out] ResultResponse A caller allocated data packet which will receive the
+  key programming result.
+
+  @retval EFI_SUCCESS RPMB communication sequence with the eMMC succeeded
+  according to specs, other values are returned in case of any protocol error.
+  Failure during key programming any other eMMC internal failure is reported
+  in the Result field of the returned response data packet.
+**/
+EFI_STATUS
+EFIAPI
+RpmbIoProgramKey (
+  IN EFI_RPMB_IO_PROTOCOL   *This,
+  IN EFI_RPMB_DATA_PACKET   *Request,
+  OUT EFI_RPMB_DATA_PACKET  *ResultResponse
+  );
+
+/** Reading of the write counter value request.
+
+  @param[in] This Indicates a pointer to the calling context.
+  @param[in] ReadRequest A data packet describing a read counter value request.
+  @param[out] ReadResponse A caller allocated data packet which will receive
+  the counter value read response. If counter has expired bit 7 is set to 1 in
+  returned Result.
+
+  @retval EFI_SUCCESS RPMB communication sequence with the eMMC succeeded
+  according to specs, other values are returned in case of any protocol error.
+  Failure during counter read or any other eMMC internal failure is reported
+  in the Result field of the returned response data packet.
+**/
+EFI_STATUS
+EFIAPI
+RpmbIoReadCounter (
+  IN EFI_RPMB_IO_PROTOCOL   *This,
+  IN EFI_RPMB_DATA_PACKET   *ReadRequest,
+  OUT EFI_RPMB_DATA_PACKET  *ReadResponse
+  );
+
+/** Authenticated data write request.
+
+  @param[in] This Indicates a pointer to the calling context.
+  @param[in] WriteRequest A sequence of data packets describing data write
+  requests and holds the data to be written.
+  @param[out] ResultResponse A caller allocated data packet which will receive
+  the data write programming result.
+
+  @retval EFI_SUCCESS RPMB communication sequence with the eMMC succeeded
+  according to specs, other values are returned in case of any protocol error.
+  Failure during data programming or any other eMMC internal failure is reported
+  in the Result field of the returned data packet.
+**/
+EFI_STATUS
+EFIAPI
+RpmbIoAuthenticatedWrite (
+  IN EFI_RPMB_IO_PROTOCOL   *This,
+  IN EFI_RPMB_DATA_BUFFER   *WriteRequest,
+  OUT EFI_RPMB_DATA_PACKET  *ResultResponse
+  );
+
+/** Authenticated data read request.
+
+  @param[in] This Indicates a pointer to the calling context.
+  @param[in] ReadRequest A data packet that describes a data read request.
+  @param[out] ReadResponse A caller allocated data packets which will receive
+  the data read.
+
+  @retval EFI_SUCCESS RPMB communication sequence with the eMMC succeeded
+  according to specs, other values are returned in case of any protocol error.
+  Failure during data fetch from the eMMC or any other eMMC internal failure
+  is reported in the Result field of the returned data packet.
+**/
+EFI_STATUS
+EFIAPI
+RpmbIoAuthenticatedRead (
+  IN EFI_RPMB_IO_PROTOCOL   *This,
+  IN EFI_RPMB_DATA_PACKET   *ReadRequest,
+  OUT EFI_RPMB_DATA_BUFFER  *ReadResponse
+  );
+
+// Helper Functions
+
+EFI_STATUS
+EFIAPI
+SoftReset (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+// Debugging Helpers
+
+VOID
+PrintCsd (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+VOID
+PrintCid (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+VOID
+PrintCardStatus (
+  IN SDHC_INSTANCE  *HostInst,
+  IN CARD_STATUS    CardStatus
+  );
+
+VOID
+GetAndPrintCardStatus (
+  IN SDHC_INSTANCE  *HostInst
+  );
+
+// Inlined Helper Functions
+
+__inline__
+static
+CONST CHAR8*
+MmcPartitionAccessToString (
+  IN MMC_EXT_CSD_PARTITION_ACCESS   Partition
+  )
+{
+  switch (Partition) {
+  case MmcExtCsdPartitionAccessUserArea:
+    return "UserArea";
+  case MmcExtCsdPartitionAccessBootPartition1:
+    return "Boot1";
+  case MmcExtCsdPartitionAccessBootPartition2:
+    return "Boot2";
+  case MmcExtCsdPartitionAccessRpmb:
+    return "RPMB";
+  case MmcExtCsdPartitionAccessGpp1:
+    return "GeneralPurpose1";
+  case MmcExtCsdPartitionAccessGpp2:
+    return "GeneralPurpose2";
+  case MmcExtCsdPartitionAccessGpp3:
+    return "GeneralPurpose3";
+  case MmcExtCsdPartitionAccessGpp4:
+    return "GeneralPurpose4";
+  default:
+    return "Unknown";
+  }
+}
+
+__inline__
+static
+CONST CHAR8*
+CardStateToString (
+  IN CARD_STATE   State
+  )
+{
+  switch (State) {
+  case CardStateIdle:
+    return "Idle";
+  case CardStateReady:
+    return "Ready";
+  case CardStateIdent:
+    return "Ident";
+  case CardStateStdby:
+    return "Stdby";
+  case CardStateTran:
+    return "Tran";
+  case CardStateData:
+    return "Data";
+  case CardStateRcv:
+    return "Rcv";
+  case CardStatePrg:
+    return "Prg";
+  case CardStateDis:
+    return "Dis";
+  case CardStateBtst:
+    return "Btst";
+  case CardStateSlp:
+    return "Slp";
+  default:
+    return "Reserved";
+  }
+}
+
+__inline__
+static
+BOOLEAN
+IsCardStatusError (
+  IN SDHC_INSTANCE  *HostInst,
+  IN CARD_STATUS    CardStatus
+  )
+{
+  ASSERT (HostInst != NULL);
+
+  switch (HostInst->CardInfo.CardFunction) {
+  case CardFunctionSd:
+    return CardStatus.AsUint32 & SD_CARD_STATUS_ERROR_MASK;
+  case CardFunctionMmc:
+    return CardStatus.AsUint32 & MMC_CARD_STATUS_ERROR_MASK;
+  default:
+    ASSERT (FALSE);
+    return FALSE;
+  }
+}
+
+__inline__
+static
+BOOLEAN
+CmdsAreEqual (
+  IN CONST SD_COMMAND   *Left,
+  IN CONST SD_COMMAND   *Right
+  )
+{
+  ASSERT (Left != NULL);
+  ASSERT (Right != NULL);
+
+  if (Left != Right) {
+    return (Left->Class == Right->Class) &&
+      (Left->Index == Right->Index) &&
+      (Left->ResponseType == Right->ResponseType) &&
+      (Left->TransferDirection == Right->TransferDirection) &&
+      (Left->TransferType == Right->TransferType) &&
+      (Left->Type == Right->Type);
+  }
+
+  return TRUE;
+}
+
+// Timing Helpers
+
+/** Gets the current high-performance counter tick count.
+
+  The returned tick count is considered as a time stamp and can used later in
+  elapsed time calculations.
+
+  @retval Current high-performance counter tick count.
+**/
+__inline__
+static
+UINT64
+HpcTimerStart (
+  VOID
+  )
+{
+  return GetPerformanceCounter ();
+}
+
+/** Calculates the elapsed milliseconds since a specific timer time stamp.
+
+  @param[in] TimerStartTimestamp The high-performance counter tick count to use
+  as the starting point in elapsed time calculation.
+
+  @retval The milliseconds elapsed.
+**/
+__inline__
+static
+UINT64
+HpcTimerElapsedMilliseconds (
+  IN UINT64   TimerStartTimestamp
+  )
+{
+  return (((GetPerformanceCounter () - TimerStartTimestamp) * 1000UL) /
+          gHpcTicksPerSeconds);
+}
+
+#endif // __SDMMC_H__
diff --git a/Platform/Microsoft/Drivers/SdMmcDxe/SdMmcDxe.inf b/Platform/Microsoft/Drivers/SdMmcDxe/SdMmcDxe.inf
new file mode 100644
index 000000000000..59dbb2662e8c
--- /dev/null
+++ b/Platform/Microsoft/Drivers/SdMmcDxe/SdMmcDxe.inf
@@ -0,0 +1,49 @@
+#
+#  Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+#
+#  This program and the accompanying materials
+#  are licensed and made available under the terms and conditions of the BSD License
+#  which accompanies this distribution.  The full text of the license may be found at
+#  http://opensource.org/licenses/bsd-license.php
+#
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+
+[Defines]
+  INF_VERSION                    = 0x0001001A
+  BASE_NAME                      = SdMmcDxe
+  FILE_GUID                      = 16738C4A-8044-4DD8-B68E-58A53CEFF5D9
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = SdMmcDxeInitialize
+
+[Sources.common]
+  BlockIo.c
+  Debug.c
+  RpmbIo.c
+  Protocol.c
+  SdMmc.c
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  Platform/Microsoft/MsPkg.dec
+  Platform/Microsoft/OpteeClientPkg/OpteeClientPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  TimerLib
+  UefiDriverEntryPoint
+  UefiLib
+
+[Protocols]
+  gEfiBlockIoProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiDiskIoProtocolGuid
+  gEfiRpmbIoProtocolGuid
+  gEfiSdhcProtocolGuid
+
+[Depex]
+  TRUE
diff --git a/Platform/Microsoft/Drivers/SdMmcDxe/SdMmcHw.h b/Platform/Microsoft/Drivers/SdMmcDxe/SdMmcHw.h
new file mode 100644
index 000000000000..fbfa49d8d3a1
--- /dev/null
+++ b/Platform/Microsoft/Drivers/SdMmcDxe/SdMmcHw.h
@@ -0,0 +1,505 @@
+/** @file
+*
+*  Copyright (c) Microsoft Corporation. All rights reserved.
+*  Copyright (c) 2011-2014, ARM Limited. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#ifndef __SDMMCHW_H__
+#define __SDMMCHW_H__
+
+#pragma pack(1)
+
+#define SD_BLOCK_LENGTH_BYTES               512
+#define SD_BLOCK_WORD_COUNT                 (SD_BLOCK_LENGTH_BYTES / sizeof (UINT32))
+#define SD_IDENT_MODE_CLOCK_FREQ_HZ         400000    // 400 KHz
+#define MMC_HIGH_SPEED_MODE_CLOCK_FREQ_HZ   52000000  // 52 MHz
+
+typedef enum {
+  CardSpeedModeUndefined = 0,
+  CardSpeedModeNormalSpeed,
+  CardSpeedModeHighSpeed
+} CARD_SPEED_MODE;
+
+typedef enum {
+  CardFunctionUnknown = 0,
+  CardFunctionSd,
+  CardFunctionSdio,
+  CardFunctionComboSdSdio,
+  CardFunctionMmc
+} CARD_FUNCTION;
+
+typedef enum {
+  CardStateIdle = 0,
+  CardStateReady,
+  CardStateIdent,
+  CardStateStdby,
+  CardStateTran,
+  CardStateData,
+  CardStateRcv,
+  CardStatePrg,
+  CardStateDis,
+  CardStateBtst,
+  CardStateSlp
+} CARD_STATE;
+
+typedef union {
+  UINT32 AsUint32;
+  struct {
+    UINT32 RESERVED_1 : 2;            // [1:0]
+    UINT32 RESERVED_2 : 1;            // [2]
+    UINT32 AKE_SEQ_ERROR : 1;         // [3] SD
+    UINT32 RESERVED_3 : 1;            // [4]
+    UINT32 APP_CMD : 1;               // [5]
+    UINT32 URGENT_BKOPS : 1;          // [6] MMC
+    UINT32 SWITCH_ERROR : 1;          // [7] MMC
+    UINT32 READY_FOR_DATA : 1;        // [8]
+    UINT32 CURRENT_STATE : 4;         // [12:9]
+    UINT32 ERASE_RESET : 1;           // [13]
+    UINT32 RESERVED_4 : 1;            // [14]
+    UINT32 WP_ERASE_SKIP : 1;         // [15]
+    UINT32 CID_CSD_OVERWRITE : 1;     // [16]
+    UINT32 OVERRUN : 1;               // [17] MMC
+    UINT32 UNDERRUN : 1;              // [18] MMC
+    UINT32 ERROR : 1;                 // [19]
+    UINT32 CC_ERROR : 1;              // [20]
+    UINT32 CARD_ECC_FAILED : 1;       // [21]
+    UINT32 ILLEGAL_COMMAND : 1;       // [22]
+    UINT32 COM_CRC_ERROR : 1;         // [23]
+    UINT32 LOCK_UNLOCK_FAILED : 1;    // [24]
+    UINT32 CARD_IS_LOCKED : 1;        // [25]
+    UINT32 WP_VIOLATION : 1;          // [26]
+    UINT32 ERASE_PARAM : 1;           // [27]
+    UINT32 ERASE_SEQ_ERROR : 1;       // [28]
+    UINT32 BLOCK_LEN_ERROR : 1;       // [29]
+    UINT32 ADDRESS_MISALIGN : 1;      // [30]
+    UINT32 ADDRESS_OUT_OF_RANGE : 1;  // [31]
+  } Fields;
+} CARD_STATUS;
+
+#define MMC_CARD_STATUS_ERROR_MASK \
+          (BIT7 | BIT13 | BIT15 | BIT16 | BIT17 | BIT18 | BIT19 | BIT20 | \
+          BIT21 | BIT22 | BIT23 | BIT24 | BIT26 | BIT27 | BIT28 | BIT29 | \
+          BIT30 | BIT31)
+
+#define SD_CARD_STATUS_ERROR_MASK \
+          (BIT3 | BIT13 | BIT15 | BIT16 | BIT19 | BIT20 | \
+          BIT21 | BIT22 | BIT23 | BIT24 | BIT26 | BIT27 | BIT28 | BIT29 | \
+          BIT30 | BIT31)
+
+typedef union {
+  UINT32 AsUint32;
+  struct {
+    UINT32 CheckPattern : 8;
+    UINT32 VoltageSupplied : 4;
+    UINT32 RESERVED_1 : 16;
+    UINT32 CommandIndex : 6;
+  } Fields;
+} SEND_IF_COND_ARG;
+
+typedef SEND_IF_COND_ARG SEND_IF_COND_CMD_RESPONSE;
+
+// definition for VoltageAccepted in SD_CMD8_STATUS
+
+#define SD_CMD8_VOLTAGE_27_36   0x01
+#define SD_CMD8_VOLTAGE_LOW     0x02
+
+typedef union {
+  UINT32 AsUint32;
+  struct {
+    UINT32 VoltageWindow : 24;  // [23:0] Maps to OCR[0:23]
+    UINT32 S18R : 1;            // [24] Switching to 1.8V Request
+                                // 0b: Use Current signal voltage, 1b: Switch to 1.8V signal voltage
+
+    UINT32 RESERVED_1 : 3;      // [27:25]
+    UINT32 XPC : 1;             // SDXC Power Control [28] 0b: Power Saving, 1b: Max Performance
+    UINT32 RESERVED_2 : 1;      // [29]
+    UINT32 HCS : 1;             // Host Capacity Support [30] 0b: SDSC, 1b: SDHC or SDXC
+    UINT32 RESERVED_3 : 1;      // [31]
+  } Fields;
+} SD_SEND_OP_COND_ARG;
+
+typedef union {
+  UINT32 AsUint32;
+  struct {
+    UINT32 VoltageWindow : 24;  // [23:0] Voltage profile
+    UINT32 RESERVED_1 : 5;      // Reserved
+    UINT32 AccessMode : 2;      // 00b (byte mode), 10b (sector mode)
+    UINT32 PowerUp : 1;         // This bit is set to LOW if the card has not finished the power up routine
+  } Fields;
+} SD_OCR;
+
+typedef union {
+  UINT32 AsUint32;
+  struct {
+    UINT32 VoltageWindow : 24;  // [23:0] Voltage profile
+    UINT32 S18A : 1;            // [24] Switching to 1.8V Accepted
+    UINT32 RESERVED_1 : 5;      // Reserved [29:25]
+    UINT32 CCS : 1;             // Card Capacity Status [30]
+    UINT32 PowerUp : 1;         // This bit is set to LOW if the card has not finished the power up routine [31]
+  } Fields;
+} SD_OCR_EX;
+
+typedef struct {
+  UINT16 MDT : 12;        // Manufacturing date [19:8]
+  UINT16 RESERVED_1 : 4;  // Reserved [23:20]
+  UINT32 PSN;             // Product serial number [55:24]
+  UINT8 PRV;              // Product revision [63:56]
+  UINT8 PNM[5];           // Product name [64:103]
+  UINT16 OID;             // OEM/Application ID [119:104]
+  UINT8 MID;              // Manufacturer ID [127:120]
+} SD_CID;
+
+typedef struct {
+  UINT16 MDT : 8;         // Manufacturing date [15:8]
+  UINT32 PSN;             // Product serial number [47:16]
+  UINT8 PRV;              // Product revision [55:48]
+  UINT8 PNM[6];           // Product name [103:56]
+  UINT8 OID;              // OEM/Application ID [111:104]
+  UINT8 CBX : 2;          // Card/BGA [113:112]
+  UINT8 RESERVED_1 : 6;   // [119:114]
+  UINT8 MID;              // Manufacturer ID [127:120]
+} MMC_CID;
+
+typedef struct {
+  UINT8 RESERVED_1 : 2;           // Reserved [9:8]
+  UINT8 FILE_FORMAT : 2;          // File format [11:10]
+  UINT8 TMP_WRITE_PROTECT : 1;    // Temporary write protection [12:12]
+  UINT8 PERM_WRITE_PROTECT : 1;   // Permanent write protection [13:13]
+  UINT8 COPY : 1;                 // Copy flag (OTP) [14:14]
+  UINT8 FILE_FORMAT_GRP : 1;      // File format group [15:15]
+  UINT16 RESERVED_2 : 5;          // Reserved [20:16]
+  UINT16 WRITE_BL_PARTIAL : 1;    // Partial blocks for write allowed [21:21]
+  UINT16 WRITE_BL_LEN : 4;        // Max. write data block length [25:22]
+  UINT16 R2W_FACTOR : 3;          // Write speed factor [28:26]
+  UINT16 RESERVED_3 : 2;          // Reserved [30:29]
+  UINT16 WP_GRP_ENABLE : 1;       // Write protect group enable [31:31]
+  UINT32 WP_GRP_SIZE : 7;         // Write protect group size [38:32]
+  UINT32 SECTOR_SIZE : 7;         // Erase sector size [45:39]
+  UINT32 ERASE_BLK_EN : 1;        // Erase single block enable [46:46]
+  UINT32 C_SIZE_MULT : 3;         // Device size multiplier [49:47]
+  UINT32 VDD_W_CURR_MAX : 3;      // Max. write current @ VDD max [52:50]
+  UINT32 VDD_W_CURR_MIN : 3;      // Max. write current @ VDD min [55:53]
+  UINT32 VDD_R_CURR_MAX : 3;      // Max. read current @ VDD max [58:56]
+  UINT32 VDD_R_CURR_MIN : 3;      // Max. read current @ VDD min [61:59]
+  UINT32 C_SIZELow2 : 2;          // Device size [63:62]
+  UINT32 C_SIZEHigh10 : 10;       // Device size [73:64]
+  UINT32 RESERVED_4 : 2;          // Reserved [75:74]
+  UINT32 DSR_IMP : 1;             // DSR implemented [76:76]
+  UINT32 READ_BLK_MISALIGN : 1;   // Read block misalignment [77:77]
+  UINT32 WRITE_BLK_MISALIGN : 1;  // Write block misalignment [78:78]
+  UINT32 READ_BL_PARTIAL : 1;     // Partial blocks for read allowed [79:79]
+  UINT32 READ_BL_LEN : 4;         // Max. read data block length [83:80]
+  UINT32 CCC : 12;                // Card command classes [95:84]
+  UINT8 TRAN_SPEED;               // Max. bus clock frequency [103:96]
+  UINT8 NSAC;                     // Data read access-time 2 in CLK cycles (NSAC*100) [111:104]
+  UINT8 TAAC;                     // Data read access-time 1 [119:112]
+  UINT8 RESERVED_5 : 6;           // Reserved [125:120]
+  UINT8 CSD_STRUCTURE : 2;        // CSD structure [127:126]
+} SD_CSD;
+
+typedef struct {
+  UINT8 RESERVED_1 : 2;           // Reserved [9:8]
+  UINT8 FILE_FORMAT : 2;          // File format [11:10]
+  UINT8 TMP_WRITE_PROTECT : 1;    // Temporary write protection [12:12]
+  UINT8 PERM_WRITE_PROTECT : 1;   // Permanent write protection [13:13]
+  UINT8 COPY : 1;                 // Copy flag (OTP) [14:14]
+  UINT8 FILE_FORMAT_GRP : 1;      // File format group [15:15]
+  UINT16 RESERVED_2 : 5;          // Reserved [20:16]
+  UINT16 WRITE_BL_PARTIAL : 1;    // Partial blocks for write allowed [21:21]
+  UINT16 WRITE_BL_LEN : 4;        // Max. write data block length [25:22]
+  UINT16 R2W_FACTOR : 3;          // Write speed factor [28:26]
+  UINT16 RESERVED_3 : 2;          // Reserved [30:29]
+  UINT16 WP_GRP_ENABLE : 1;       // Write protect group enable [31:31]
+  UINT16 WP_GRP_SIZE : 7;         // Write protect group size [38:32]
+  UINT16 SECTOR_SIZE : 7;         // Erase sector size [45:39]
+  UINT16 ERASE_BLK_EN : 1;        // Erase single block enable [46:46]
+  UINT16 RESERVED_4 : 1;          // Reserved [47:47]
+  UINT32 C_SIZE : 22;             // Device size [63:62]
+  UINT32 RESERVED_5 : 6;          // Reserved [75:74]
+  UINT32 DSR_IMP : 1;             // DSR implemented [76:76]
+  UINT32 READ_BLK_MISALIGN : 1;   // Read block misalignment [77:77]
+  UINT32 WRITE_BLK_MISALIGN : 1;  // Write block misalignment [78:78]
+  UINT32 READ_BL_PARTIAL : 1;     // Partial blocks for read allowed [79:79]
+  UINT16 READ_BL_LEN : 4;         // Max. read data block length [83:80]
+  UINT16 CCC : 12;                // Card command classes [95:84]
+  UINT8 TRAN_SPEED;               // Max. bus clock frequency [103:96]
+  UINT8 NSAC;                     // Data read access-time 2 in CLK cycles (NSAC*100) [111:104]
+  UINT8 TAAC;                     // Data read access-time 1 [119:112]
+  UINT8 RESERVED_6 : 6;           // Reserved [125:120]
+  UINT8 CSD_STRUCTURE : 2;        // CSD structure [127:126]
+} SD_CSD_2;
+
+typedef struct {
+  UINT8 ECC : 2;                  // ECC code [9:8]
+  UINT8 FILE_FORMAT : 2;          // File format [11:10]
+  UINT8 TMP_WRITE_PROTECT : 1;    // Temporary write protection [12:12]
+  UINT8 PERM_WRITE_PROTECT : 1;   // Permanent write protection [13:13]
+  UINT8 COPY : 1;                 // Copy flag (OTP) [14:14]
+  UINT8 FILE_FORMAT_GRP : 1;      // File format group [15:15]
+  UINT16 CONTENT_PROT_APP : 1;    // Content protection application [16:16]
+  UINT16 RESERVED_1 : 4;          // Reserved [20:17]
+  UINT16 WRITE_BL_PARTIAL : 1;    // Partial blocks for write allowed [21:21]
+  UINT16 WRITE_BL_LEN : 4;        // Max. write data block length [25:22]
+  UINT16 R2W_FACTOR : 3;          // Write speed factor [28:26]
+  UINT16 DEFAULT_ECC : 2;         // Manufacturer default ECC [30:29]
+  UINT16 WP_GRP_ENABLE : 1;       // Write protect group enable [31:31]
+  UINT32 WP_GRP_SIZE : 5;         // Write protect group size [36:32]
+  UINT32 ERASE_GRP_MULT : 5;      // Erase group size multiplier [41:37]
+  UINT32 ERASE_GRP_SIZE : 5;      // Erase sector size [46:42]
+  UINT32 C_SIZE_MULT : 3;         // Device size multiplier [49:47]
+  UINT32 VDD_W_CURR_MAX : 3;      // Max. write current @ VDD max [52:50]
+  UINT32 VDD_W_CURR_MIN : 3;      // Max. write current @ VDD min [55:53]
+  UINT32 VDD_R_CURR_MAX : 3;      // Max. read current @ VDD max [58:56]
+  UINT32 VDD_R_CURR_MIN : 3;      // Max. read current @ VDD min [61:59]
+  UINT32 C_SIZELow2 : 2;          // Device size [63:62]
+  UINT32 C_SIZEHigh10 : 10;       // Device size [73:64]
+  UINT32 RESERVED_4 : 2;          // Reserved [75:74]
+  UINT32 DSR_IMP : 1;             // DSR implemented [76:76]
+  UINT32 READ_BLK_MISALIGN : 1;   // Read block misalignment [77:77]
+  UINT32 WRITE_BLK_MISALIGN : 1;  // Write block misalignment [78:78]
+  UINT32 READ_BL_PARTIAL : 1;     // Partial blocks for read allowed [79:79]
+  UINT32 READ_BL_LEN : 4;         // Max. read data block length [83:80]
+  UINT32 CCC : 12;                // Card command classes [95:84]
+  UINT8 TRAN_SPEED;               // Max. bus clock frequency [103:96]
+  UINT8 NSAC;                     // Data read access-time 2 in CLK cycles (NSAC*100) [111:104]
+  UINT8 TAAC;                     // Data read access-time 1 [119:112]
+  UINT8 RESERVED_5 : 2;           // Reserved [121:120]
+  UINT8 SPEC_VERS : 4;            // System specification version [125:122]
+  UINT8 CSD_STRUCTURE : 2;        // CSD structure [127:126]
+} MMC_CSD;
+
+// We support spec version 4.X and higher
+// If CSD.SPEC_VERS indicates a version 4.0 or higher, the card is a high speed card and supports
+// SWITCH and SEND_EXT_CSD commands. Otherwise the card is an old MMC card.
+#define MMC_MIN_SUPPORTED_SPEC_VERS     4
+
+typedef struct {
+
+  // Host modifiable modes
+
+  UINT8 Reserved26[134];
+  UINT8 BadBlockManagement;
+  UINT8 Reserved25;
+  UINT32 EnhancedUserDataStartAddress;
+  UINT8 EnhancedUserDataAreaSize[3];
+  UINT8 GpPartSizeMult[12];
+  UINT8 PartitioningSetting;
+  UINT8 PartitionsAttribute;
+  UINT8 MaxEnhancedAreaSize[3];
+  UINT8 PartitioningSupport;
+  UINT8 Reserved24;
+  UINT8 HwResetFunc;
+  UINT8 Reserved23[5];
+  UINT8 RpmbSizeMult;
+  UINT8 FwConfig;
+  UINT8 Reserved22;
+  UINT8 UserWriteProt;
+  UINT8 Reserved21;
+  UINT8 BootWriteProt;
+  UINT8 Reserved20;
+  UINT8 EraseGroupDef;
+  UINT8 Reserved19;
+  UINT8 BootBusWidth;
+  UINT8 BootConfigProt;
+  UINT8 PartitionConfig;
+  UINT8 Reserved18;
+  UINT8 ErasedMemContent;
+  UINT8 Reserved17;
+  UINT8 BusWidth;
+  UINT8 Reserved16;
+  UINT8 HighSpeedTiming;
+  UINT8 Reserved15;
+  UINT8 PowerClass;
+  UINT8 Reserved14;
+  UINT8 CmdSetRevision;
+  UINT8 Reserved13;
+  UINT8 CmdSet;
+
+  // Non-modifiable properties
+
+  UINT8 ExtendedCsdRevision;
+  UINT8 Reserved12;
+  UINT8 CsdStructureVersion;
+  UINT8 Reserved11;
+  UINT8 CardType;
+  UINT8 Reserved10[3];
+  UINT8 PowerClass52Mhz195V;
+  UINT8 PowerClass26Mhz195V;
+  UINT8 PowerClass52Mhz36V;
+  UINT8 PowerClass26Mhz36V;
+  UINT8 Reserved9;
+  UINT8 MinReadPerf4bit26Mhz;
+  UINT8 MinWritePerf4bit26Mhz;
+  UINT8 MinReadPerf8bit26Mhz;
+  UINT8 MinWritePerf8bit26Mhz;
+  UINT8 MinReadPerf8bit52Mhz;
+  UINT8 MinWritePerf8bit52Mhz;
+  UINT8 Reserved8;
+  UINT32 SectorCount;
+  UINT8 Reserved7;
+  UINT8 SleepAwakeTimeout;
+  UINT8 Reserved6;
+  UINT8 SleepCurrentVccq;
+  UINT8 SleepCurrentVcc;
+  UINT8 HighCapacityWriteProtectSize;
+  UINT8 ReliableWriteSectorCount;
+  UINT8 HighCapacityEraseTimeout;
+  UINT8 HighCapacityEraseSize;
+  UINT8 AccessSize;
+  UINT8 BootPartitionSize;
+  UINT8 Reserved5;
+  UINT8 BootInfo;
+  UINT8 SecureTrimMultiplier;
+  UINT8 SecureEraseMultiplier;
+  UINT8 SecureFeatureSupport;
+  UINT8 TrimMultiplier;
+  UINT8 Reserved4;
+  UINT8 MinReadPerf8bit52MhzDdr;
+  UINT8 MinWritePerf8bit52MhzDdr;
+  UINT16 Reserved3;
+  UINT8 PowerClass52MhzDdr36V;
+  UINT8 PowerClass52MhzDdr195V;
+  UINT8 Reserved2;
+  UINT8 InitTimeoutAfterPartitioning;
+  UINT8 Reserved1[262];
+  UINT8 SupportedCmdSets;
+  UINT8 Reserved[7];
+} MMC_EXT_CSD;
+
+typedef enum {
+  MmcExtCsdCardTypeNormalSpeed = 0x01,
+  MmcExtCsdCardTypeHighSpeed = 0x02,
+  MmcExtCsdCardTypeDdr1v8 = 0x04,
+  MmcExtCsdCardTypeDdr1v2 = 0x08
+} MmcExtCsdCardType;
+
+typedef enum {
+  MmcExtCsdBusWidth1Bit = 0,
+  MmcExtCsdBusWidth4Bit = 1,
+  MmcExtCsdBusWidth8Bit = 2
+} MmcExtCsdBusWidth;
+
+typedef enum {
+  MmcExtCsdPartitionAccessUserArea,
+  MmcExtCsdPartitionAccessBootPartition1,
+  MmcExtCsdPartitionAccessBootPartition2,
+  MmcExtCsdPartitionAccessRpmb,
+  MmcExtCsdPartitionAccessGpp1,
+  MmcExtCsdPartitionAccessGpp2,
+  MmcExtCsdPartitionAccessGpp3,
+  MmcExtCsdPartitionAccessGpp4,
+} MMC_EXT_CSD_PARTITION_ACCESS;
+
+typedef enum {
+  MmcExtCsdBootPartitionEnableNotBootEnabled,
+  MmcExtCsdBootPartitionBootPartition1BootEnabled,
+  MmcExtCsdBootPartitionBootPartition2BootEnabled,
+  MmcExtCsdBootPartitionUserAreaBootEnabled = 7,
+} MMC_EXT_CSD_BOOT_PARTITION_ENABLE;
+
+typedef union {
+  UINT8 AsUint8;
+  struct {
+    UINT8 PARTITION_ACCESS : 3;       // [2:0]
+    UINT8 BOOT_PARTITION_ENABLE : 3;  // [5:3]
+    UINT8 BOOT_ACK : 1;               // [6]
+    UINT8 RESERVED_1 : 1;             // [7]
+  } Fields;
+} MMC_EXT_CSD_PARTITION_CONFIG;
+
+typedef enum {
+  MmcExtCsdBitIndexPartitionConfig = 179,
+  MmcExtCsdBitIndexBusWidth = 183,
+  MmcExtCsdBitIndexHsTiming = 185
+} MMC_EXT_CSD_BIT_INDEX;
+
+typedef enum {
+  MmcSwitchCmdAccessTypeCommandSet,
+  MmcSwitchCmdAccessTypeSetBits,
+  MmcSwitchCmdAccessTypeClearBits,
+  MmcSwitchCmdAccessTypeWriteByte
+} MMC_SWITCH_CMD_ACCESS_TYPE;
+
+typedef union {
+  UINT32 AsUint32;
+  struct {
+    UINT32 CmdSet : 3;      // [2:0]
+    UINT32 RESERVED_1 : 5;  // Set to 0 [7:3]
+    UINT32 Value : 8;       // [15:8]
+    UINT32 Index : 8;       // [23:16]
+    UINT32 Access : 2;      // A value from MMC_SWITCH_CMD_ACCESS_TYPE [25:24]
+    UINT32 RESERVED_2 : 6;  // Set to 0 [31:26]
+  } Fields;
+} MMC_SWITCH_CMD_ARG;
+
+// Bits [3:0] code the current consumption for the 4 bit bus configuration
+#define MMC_EXT_CSD_POWER_CLASS_4BIT(X)         ((X) & 0xF)
+
+// Bits [7:4] code the current consumption for the 8 bit bus configuration
+#define MMC_EXT_CSD_POWER_CLASS_8BIT(X)         ((X) >> 4)
+
+typedef struct {
+  UINT32 RESERVED_1;
+  UINT32 CMD_SUPPORT : 2;
+  UINT32 RESERVED_2 : 9;
+  UINT32 EX_SECURITY : 4;
+  UINT32 SD_SPEC3 : 1;
+  UINT32 SD_BUS_WIDTH : 4;
+  UINT32 SD_SECURITY : 3;
+  UINT32 DATA_STAT_AFTER_ERASE : 1;
+  UINT32 SD_SPEC : 4;
+  UINT32 SCR_STRUCTURE : 4;
+} SD_SCR;
+
+typedef SD_OCR MMC_OCR;
+typedef SD_OCR MMC_SEND_OP_COND_ARG;
+
+typedef enum {
+  SdOcrAccessByteMode = 0,
+  SdOcrAccessSectorMode = 2
+} SD_OCR_ACCESS;
+
+// Voltage window that covers from 2.8V and up to 3.6V
+#define SD_OCR_HIGH_VOLTAGE_WINDOW        0x00FF8000
+
+typedef struct {
+  SD_OCR Ocr;
+  SD_CID Cid;
+  SD_CSD Csd;
+  SD_SCR Scr;
+} SD_REGISTERS;
+
+typedef struct {
+  MMC_OCR Ocr;
+  MMC_CID Cid;
+  MMC_CSD Csd;
+  MMC_EXT_CSD ExtCsd;
+} MMC_REGISTERS;
+
+typedef struct {
+  UINT32              RCA;
+  CARD_FUNCTION       CardFunction;
+  BOOLEAN             HasExtendedOcr;
+  BOOLEAN             HighCapacity;
+  UINT64              ByteCapacity;
+  CARD_SPEED_MODE     CurrentSpeedMode;
+
+  union {
+    SD_REGISTERS Sd;
+    MMC_REGISTERS Mmc;
+  } Registers;
+
+} CARD_INFO;
+
+#pragma pack()
+
+#endif // __SDMMCHW_H__
diff --git a/Platform/Microsoft/Include/Protocol/RpmbIo.h b/Platform/Microsoft/Include/Protocol/RpmbIo.h
new file mode 100644
index 000000000000..6087f99615d7
--- /dev/null
+++ b/Platform/Microsoft/Include/Protocol/RpmbIo.h
@@ -0,0 +1,268 @@
+/** @file
+*
+*  RPMB IO protocol is an interface implementation for the Replay Protected Memory
+*  Block (RPMB) as defined by JEDEC Standard for MultiMediaCard specs 4.41.
+*
+*  This protocol abstracts the RPMB operations to allow EFI boot services environment
+*  to perform eMMC RPMB operations without specific knowledge about the card type or
+*  the host controller.
+*
+*  Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#ifndef __RPMB_IO_H__
+#define __RPMB_IO_H__
+
+// Global ID for the RPMB IO Protocol {FBAEE5B2-08B0-41B8-B0B0-86B72EED1BB6}
+#define EFI_RPMB_IO_PROTOCOL_GUID \
+  { 0xfbaee5b2, 0x8b0, 0x41b8, { 0xb0, 0xb0, 0x86, 0xb7, 0x2e, 0xed, 0x1b, 0xb6 } };
+
+#define EFI_RPMB_IO_PROTOCOL_REVISION  0x00010000
+
+// RPMB Request Message Types
+
+#define EFI_RPMB_REQUEST_PROGRAM_KEY     0x0001  // Authentication key programming request
+#define EFI_RPMB_REQUEST_COUNTER_VALUE   0x0002  // Reading of the Write Counter value -request
+#define EFI_RPMB_REQUEST_AUTH_WRITE      0x0003  // Authenticated data write request
+#define EFI_RPMB_REQUEST_AUTH_READ       0x0004  // Authenticated data read request
+#define EFI_RPMB_REQUEST_RESULT_REQUEST  0x0005  // Result read request
+
+// RPMB Response Message Types
+
+#define EFI_RPMB_RESPONSE_PROGRAM_KEY    0x0100  // Authentication key programming response
+#define EFI_RPMB_RESPONSE_COUNTER_VALUE  0x0200  // Reading of the Write Counter value -response
+#define EFI_RPMB_RESPONSE_AUTH_WRITE     0x0300  // Authenticated data write response
+#define EFI_RPMB_RESPONSE_AUTH_READ      0x0400  // Authenticated data read response
+
+// RPMB Operation Results
+
+#define EFI_RPMB_OK                     0   // Operation OK
+#define EFI_RPMB_ERROR_GENERAL          1   // General failure
+#define EFI_RPMB_ERROR_AUTH             2   // Authentication failure (MAC comparison
+                                            //   not matching, MAC calculation failure)
+#define EFI_RPMB_ERROR_COUNTER          3   // Counter failure (counters not matching
+                                            //   in comparison, counter incrementing failure)
+#define EFI_RPMB_ERROR_ADDRESS          4   // Address failure (address out of range,
+                                            //   wrong address alignment)
+#define EFI_RPMB_ERROR_WRITE            5   // Write failure (data/counter/result write failure)
+#define EFI_RPMB_ERROR_READ             6   // Read failure (data/counter/result read failure)
+#define EFI_RPMB_ERROR_KEY              7   // Authentication Key not yet programmed
+
+#define EFI_RPMB_ERROR_MASK             0x7
+#define EFI_RPMB_ERROR_CNT_EXPIRED_BIT  0x80
+
+// RPMB Data Frame fields size in bytes.
+
+#define EFI_RPMB_PACKET_STUFF_SIZE       196
+#define EFI_RPMB_PACKET_KEY_MAC_SIZE     32
+#define EFI_RPMB_PACKET_DATA_SIZE        256
+#define EFI_RPMB_PACKET_NONCE_SIZE       16
+#define EFI_RPMB_PACKET_WCOUNTER_SIZE    4
+#define EFI_RPMB_PACKET_ADDRESS_SIZE     2
+#define EFI_RPMB_PACKET_BLOCKCOUNT_SIZE  2
+#define EFI_RPMB_PACKET_RESULT_SIZE      2
+#define EFI_RPMB_PACKET_TYPE_SIZE        2
+
+// Everything in the RPMB Data Frame is hashed except the Stuff and the MAC itself.
+#define EFI_RPMB_PACKET_DATA_HASH_SIZE ( \
+  sizeof(EFI_RPMB_DATA_PACKET) - \
+  offsetof(EFI_RPMB_DATA_PACKET, PacketData) \
+  )
+
+// CID register is 16 byte
+#define EFI_RPMB_CID_SIZE 16
+
+/** The data frame to access the replay protected memory area.
+
+  NOTE: Byte order of the RPMB data frame is MSB first, e.g. Write Counter MSB
+  [11] is storing the upmost byte of the counter value.
+**/
+#pragma pack(1)
+typedef struct {
+  UINT8  Stuff[EFI_RPMB_PACKET_STUFF_SIZE];
+  UINT8  KeyOrMAC[EFI_RPMB_PACKET_KEY_MAC_SIZE];  // The authentication key or
+                                                  // the message authentication code (MAC)
+                                                  // depending on the request/response
+                                                  // type. The MAC will be delivered
+                                                  // in the last (or the only) block of data
+  UINT8  PacketData[EFI_RPMB_PACKET_DATA_SIZE];   // Data to be written or read by signed access.
+  UINT8  Nonce[EFI_RPMB_PACKET_NONCE_SIZE];       // Random number generated by the host
+                                                  // for the Requests and copied to the
+                                                  // Response by the eMMC Replay Protected
+                                                  // Memory Block engine
+  UINT8  WriteCounter[EFI_RPMB_PACKET_WCOUNTER_SIZE];   // Counter value for the total amount
+                                                        // of the successful authenticated data
+                                                        // write requests made by the host
+  UINT8  Address[EFI_RPMB_PACKET_ADDRESS_SIZE];   // Address of the data to be programmed to
+                                                  // or read from the Replay Protected
+                                                  // Memory Block. Address is the serial number
+                                                  // of the accessed half sector (256B).
+                                                  // Address argument in CMD 18 and CMD 25
+                                                  // will be ignored
+  UINT8  BlockCount[EFI_RPMB_PACKET_BLOCKCOUNT_SIZE];   // Number of blocks (half sectors, 256B)
+                                                        // requested to be read/programmed. This
+                                                        // value is equal to the count value in
+                                                        // CMD23 argument
+  UINT8  OperationResult[EFI_RPMB_PACKET_RESULT_SIZE];  // Includes information about the status
+                                                        // of the write counter (valid, expired)
+                                                        // and successfulness of the access made
+                                                        // to the Replay Protected Memory Block
+  UINT8  RequestOrResponseType[EFI_RPMB_PACKET_TYPE_SIZE];  // Defines the type of request and
+                                                            // response to/from the memory.
+} EFI_RPMB_DATA_PACKET;
+#pragma pack()
+
+typedef struct {
+  EFI_RPMB_DATA_PACKET  *Packets;
+  UINTN                 PacketCount;  // The number of RPMB Frames/Packets which is
+                                      // also the number of half sectors (256Byte) to
+                                      // be read/programmed since each RPBM frame
+                                      // holds 256Bytes of data.
+} EFI_RPMB_DATA_BUFFER;
+
+typedef struct _EFI_RPMB_IO_PROTOCOL EFI_RPMB_IO_PROTOCOL;
+
+/** Authentication key programming request.
+
+  @param[in] This Indicates a pointer to the calling context.
+  @param[in] ProgrammingRequest A data packet describing a key programming request.
+  @param[out] ResultResponse A caller allocated data packet which will receive the
+  key programming result.
+
+  @retval EFI_SUCCESS RPMB communication sequence with the eMMC succeeded
+  according to specs, other values are returned in case of any protocol error.
+  Failure during key programming any other eMMC internal failure is reported
+  in the Result field of the returned response data packet.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_RPMB_PROGRAM_KEY) (
+  IN EFI_RPMB_IO_PROTOCOL   *This,
+  IN EFI_RPMB_DATA_PACKET   *ProgrammingRequest,
+  OUT EFI_RPMB_DATA_PACKET  *ResultResponse
+  );
+
+/** Reading of the write counter value request.
+
+  @param[in] This Indicates a pointer to the calling context.
+  @param[in] ReadRequest A data packet describing a read counter value request.
+  @param[out] ReadResponse A caller allocated data packet which will receive
+  the counter value read response. If counter has expired bit 7 is set to 1 in
+  returned Result.
+
+  @retval EFI_SUCCESS RPMB communication sequence with the eMMC succeeded
+  according to specs, other values are returned in case of any protocol error.
+  Failure during counter read or any other eMMC internal failure is reported
+  in the Result field of the returned response data packet.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_RPMB_READ_COUNTER) (
+  IN EFI_RPMB_IO_PROTOCOL   *This,
+  IN EFI_RPMB_DATA_PACKET   *ReadRequest,
+  OUT EFI_RPMB_DATA_PACKET  *ReadResponse
+  );
+
+/** Authenticated data write request.
+
+  @param[in] This Indicates a pointer to the calling context.
+  @param[in] WriteRequest A sequence of data packets describing data write
+  requests and holds the data to be written.
+  @param[out] ResultResponse A caller allocated data packet which will receive
+  the data write programming result.
+
+  @retval EFI_SUCCESS RPMB communication sequence with the eMMC succeeded
+  according to specs, other values are returned in case of any protocol error.
+  Failure during data programming or any other eMMC internal failure is reported
+  in the Result field of the returned data packet.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_RPMB_AUTHENTICATED_WRITE) (
+  IN EFI_RPMB_IO_PROTOCOL   *This,
+  IN EFI_RPMB_DATA_BUFFER   *WriteRequest,
+  OUT EFI_RPMB_DATA_PACKET  *ResultResponse
+  );
+
+/** Authenticated data read request.
+
+  @param[in] This Indicates a pointer to the calling context.
+  @param[in] ReadRequest A data packet that describes a data read request.
+  @param[out] ReadResponse A caller allocated data packets which will receive
+  the data read.
+
+  @retval EFI_SUCCESS RPMB communication sequence with the eMMC succeeded
+  according to specs, other values are returned in case of any protocol error.
+  Failure during data fetch from the eMMC or any other eMMC internal failure
+  is reported in the Result field of the returned data packet.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_RPMB_AUTHENTICATED_READ) (
+  IN EFI_RPMB_IO_PROTOCOL   *This,
+  IN EFI_RPMB_DATA_PACKET   *ReadRequest,
+  OUT EFI_RPMB_DATA_BUFFER  *ReadResponse
+  );
+
+struct _EFI_RPMB_IO_PROTOCOL {
+  UINT64  Revision;
+  UINT8   Cid[EFI_RPMB_CID_SIZE];   // MMC Card IDentification (CID) register.
+  UINT8   ReliableSectorCount;      // Reliable write sector count as defined by
+                                    // the REL_WR_SEC_C field of the
+                                    // MMC EXT_CSD register.
+  UINT8   RpmbSizeMult;             // RPMB sector size multiplier as defined by
+                                    // the RPMB_SIZE_MULT field of the
+                                    // MMC EXT_CSD register.
+                                    // The RPMB partition size is calculated from
+                                    // the register by using the
+                                    // following equation:
+                                    // RPMB partition size = 128kB x RPMB_SIZE_MULT
+
+  // Protocol Callbacks
+  EFI_RPMB_PROGRAM_KEY          ProgramKey;
+  EFI_RPMB_READ_COUNTER         ReadCounter;
+  EFI_RPMB_AUTHENTICATED_WRITE  AuthenticatedWrite;
+  EFI_RPMB_AUTHENTICATED_READ   AuthenticatedRead;
+};
+
+__inline__
+static
+CONST CHAR16*
+RpmbOperationResultToString (
+  UINT16 OperationResult
+  )
+{
+  switch (OperationResult) {
+    case EFI_RPMB_OK:
+      return L"Operation OK";
+    case EFI_RPMB_ERROR_GENERAL:
+      return L"General failure";
+    case EFI_RPMB_ERROR_AUTH:
+      return L"Authentication failure";
+    case EFI_RPMB_ERROR_COUNTER:
+      return L"Counter failure";
+    case EFI_RPMB_ERROR_ADDRESS:
+      return L"Address failure";
+    case EFI_RPMB_ERROR_WRITE:
+      return L"Write failure";
+    case EFI_RPMB_ERROR_READ:
+      return L"Read failure";
+    case EFI_RPMB_ERROR_KEY:
+      return L"Authentication key not yet programmed";
+    default:
+      return L"Undefined operation result";
+  }
+}
+
+extern EFI_GUID gEfiRpmbIoProtocolGuid;
+
+#endif // __RPMB_IO_H__
diff --git a/Platform/Microsoft/Include/Protocol/Sdhc.h b/Platform/Microsoft/Include/Protocol/Sdhc.h
new file mode 100644
index 000000000000..334036c6dc88
--- /dev/null
+++ b/Platform/Microsoft/Include/Protocol/Sdhc.h
@@ -0,0 +1,197 @@
+/** @file
+*
+*  Copyright (c) 2018 Microsoft Corporation. All rights reserved.
+*  Copyright (c) 2011-2014, ARM Limited. All rights reserved.
+*
+*  This program and the accompanying materials
+*  are licensed and made available under the terms and conditions of the BSD License
+*  which accompanies this distribution.  The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+*
+**/
+
+#ifndef __SDHC_H__
+#define __SDHC_H__
+
+//
+// Global ID for the SDHC Protocol {46055B0F-992A-4AD7-8F81-148186FFDF72}
+//
+#define EFI_SDHC_PROTOCOL_GUID \
+  { 0x46055b0f, 0x992a, 0x4ad7, { 0x8f, 0x81, 0x14, 0x81, 0x86, 0xff, 0xdf, 0x72 } };
+
+typedef UINT16 SD_COMMAND_INDEX;
+
+typedef enum {
+    SdCommandTypeUndefined = 0,
+    SdCommandTypeSuspend,
+    SdCommandTypeResume,
+    SdCommandTypeAbort
+} SD_COMMAND_TYPE;
+
+typedef enum {
+    SdCommandClassUndefined = 0,
+    SdCommandClassStandard,
+    SdCommandClassApp
+} SD_COMMAND_CLASS;
+
+typedef enum {
+    SdResponseTypeUndefined = 0,
+    SdResponseTypeNone,
+    SdResponseTypeR1,
+    SdResponseTypeR1B,
+    SdResponseTypeR2,
+    SdResponseTypeR3,
+    SdResponseTypeR4,
+    SdResponseTypeR5,
+    SdResponseTypeR5B,
+    SdResponseTypeR6
+} SD_RESPONSE_TYPE;
+
+typedef enum {
+    SdTransferTypeUndefined = 0,
+    SdTransferTypeNone,
+    SdTransferTypeSingleBlock,
+    SdTransferTypeMultiBlock,
+    SdTransferTypeMultiBlockNoStop
+} SD_TRANSFER_TYPE;
+
+typedef enum {
+    SdTransferDirectionUndefined = 0,
+    SdTransferDirectionRead,
+    SdTransferDirectionWrite
+} SD_TRANSFER_DIRECTION;
+
+typedef struct {
+    SD_COMMAND_INDEX Index;
+    SD_COMMAND_TYPE Type;
+    SD_COMMAND_CLASS Class;
+    SD_RESPONSE_TYPE ResponseType;
+    SD_TRANSFER_TYPE TransferType;
+    SD_TRANSFER_DIRECTION TransferDirection;
+} SD_COMMAND;
+
+typedef struct {
+    UINT32 BlockSize;
+    UINT32 BlockCount;
+    VOID* Buffer;
+} SD_COMMAND_XFR_INFO;
+
+typedef enum {
+    SdBusWidthUndefined = 0,
+    SdBusWidth1Bit = 1,
+    SdBusWidth4Bit = 4,
+    SdBusWidth8Bit = 8
+} SD_BUS_WIDTH;
+
+typedef enum {
+    SdhcResetTypeUndefined = 0,
+    SdhcResetTypeAll,
+    SdhcResetTypeCmd,
+    SdhcResetTypeData
+} SDHC_RESET_TYPE;
+
+typedef struct {
+  UINT32 MaximumBlockSize;
+  UINT32 MaximumBlockCount;
+} SDHC_CAPABILITIES;
+//
+// Forward declaration for EFI_SDHC_PROTOCOL
+//
+typedef struct _EFI_SDHC_PROTOCOL EFI_SDHC_PROTOCOL;
+
+typedef VOID (EFIAPI *SDHC_GET_CAPABILITIES) (
+  IN EFI_SDHC_PROTOCOL *This,
+  OUT SDHC_CAPABILITIES *Capabilities
+  );
+
+typedef EFI_STATUS (EFIAPI *SDHC_SOFTWARERESET) (
+  IN EFI_SDHC_PROTOCOL *This,
+  IN SDHC_RESET_TYPE ResetType
+  );
+
+typedef EFI_STATUS (EFIAPI *SDHC_SETCLOCK) (
+  IN EFI_SDHC_PROTOCOL *This,
+  IN UINT32 TargetFreqHz
+  );
+
+typedef EFI_STATUS (EFIAPI *SDHC_SETBUSWIDTH) (
+  IN EFI_SDHC_PROTOCOL *This,
+  IN SD_BUS_WIDTH BusWidth
+  );
+
+typedef BOOLEAN (EFIAPI *SDHC_ISCARDPRESENT) (
+  IN EFI_SDHC_PROTOCOL *This
+  );
+
+typedef BOOLEAN (EFIAPI *SDHC_ISREADONLY) (
+  IN EFI_SDHC_PROTOCOL *This
+  );
+
+typedef EFI_STATUS (EFIAPI *SDHC_SENDCOMMAND) (
+  IN EFI_SDHC_PROTOCOL *This,
+  IN const SD_COMMAND *Cmd,
+  IN UINT32 Argument,
+  IN OPTIONAL const SD_COMMAND_XFR_INFO *XfrInfo
+  );
+
+typedef EFI_STATUS (EFIAPI *SDHC_RECEIVERESPONSE) (
+  IN EFI_SDHC_PROTOCOL *This,
+  IN const SD_COMMAND *Cmd,
+  OUT UINT32 *Buffer
+  );
+
+typedef EFI_STATUS (EFIAPI *SDHC_READBLOCKDATA) (
+  IN EFI_SDHC_PROTOCOL *This,
+  IN UINTN LengthInBytes,
+  OUT UINT32 *Buffer
+  );
+
+typedef EFI_STATUS (EFIAPI *SDHC_WRITEBLOCKDATA) (
+  IN EFI_SDHC_PROTOCOL *This,
+  IN UINTN LengthInBytes,
+  IN const UINT32 *Buffer
+  );
+
+typedef VOID (EFIAPI *SDHC_CLEANUP) (
+  IN EFI_SDHC_PROTOCOL *This
+  );
+
+struct _EFI_SDHC_PROTOCOL {
+  UINT32                   Revision;
+
+  //
+  // A unique ID that identify the SDHC device among other
+  // SDHC devices on the system
+  //
+  UINT32                   SdhcId;
+
+  //
+  // Context area allocated by the SDHC driver
+  //
+  VOID                     *PrivateContext;
+
+  //
+  // SDHHC Callbacks
+  //
+  SDHC_GET_CAPABILITIES    GetCapabilities;
+  SDHC_SOFTWARERESET       SoftwareReset;
+  SDHC_SETCLOCK            SetClock;
+  SDHC_SETBUSWIDTH         SetBusWidth;
+  SDHC_ISCARDPRESENT       IsCardPresent;
+  SDHC_ISREADONLY          IsReadOnly;
+  SDHC_SENDCOMMAND         SendCommand;
+  SDHC_RECEIVERESPONSE     ReceiveResponse;
+  SDHC_READBLOCKDATA       ReadBlockData;
+  SDHC_WRITEBLOCKDATA      WriteBlockData;
+  SDHC_CLEANUP             Cleanup;
+};
+
+#define SDHC_PROTOCOL_INTERFACE_REVISION    0x00010000    // 1.0
+
+extern EFI_GUID gEfiSdhcProtocolGuid;
+
+#endif // __SDHC_H__
+
-- 
2.16.2.gvfs.1.33.gf5370f1



  parent reply	other threads:[~2018-09-21  8:25 UTC|newest]

Thread overview: 75+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-09-21  8:25 [PATCH edk2-platforms 00/27] Import Hummingboard Edge platform for Windows IoT Core Chris Co
2018-09-21  8:25 ` [PATCH edk2-platforms 01/27] Platform/Microsoft: Add OpteeClientPkg dec Chris Co
2018-10-31 20:43   ` Leif Lindholm
2018-11-01 10:55     ` Sumit Garg
2018-11-02  0:41       ` Chris Co
2018-11-02  5:24         ` Sumit Garg
2018-11-02 23:55           ` Chris Co
2018-11-05 10:07             ` Sumit Garg
2018-11-06  1:53               ` Chris Co
2018-11-06 11:09                 ` Sumit Garg
2018-09-21  8:25 ` Chris Co [this message]
2018-09-21  8:25 ` [PATCH edk2-platforms 03/27] Platform/Microsoft: Add MsPkg Chris Co
2018-10-31 21:00   ` Leif Lindholm
2018-09-21  8:25 ` [PATCH edk2-platforms 04/27] Silicon/NXP: Add iMXPlatformPkg dec Chris Co
2018-09-21  8:25 ` [PATCH edk2-platforms 05/27] Silicon/NXP: Add UART library support for i.MX platforms Chris Co
2018-11-01  8:59   ` Leif Lindholm
2018-11-02  1:46     ` Chris Co
2018-09-21  8:25 ` [PATCH edk2-platforms 06/27] Silicon/NXP: Add I2C " Chris Co
2018-11-01 17:53   ` Leif Lindholm
2018-09-21  8:25 ` [PATCH edk2-platforms 07/27] Silicon/NXP: Add i.MX display library support Chris Co
2018-11-01 18:05   ` Leif Lindholm
2018-11-29  0:55     ` Chris Co
2018-09-21  8:25 ` [PATCH edk2-platforms 08/27] Silicon/NXP: Add Virtual RTC support for i.MX platform Chris Co
2018-12-15 13:26   ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 09/27] Silicon/NXP: Add headers for SoC-specific i.MX packages to use Chris Co
2018-11-01 18:20   ` Leif Lindholm
2018-12-01  0:22     ` Chris Co
2018-12-03  9:42       ` Leif Lindholm
2018-12-04  1:44         ` Chris Co
2018-12-04  9:33           ` Ard Biesheuvel
2018-12-04 12:22             ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 10/27] Silicon/NXP: Add iMX6Pkg dec Chris Co
2018-11-01 18:25   ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 11/27] Silicon/NXP: Add i.MX6 SoC header files Chris Co
2018-12-13 17:11   ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 12/27] Silicon/NXP: Add i.MX6 I/O MUX library Chris Co
2018-11-08 18:00   ` Leif Lindholm
2018-12-04  1:41     ` Chris Co
2018-09-21  8:26 ` [PATCH edk2-platforms 13/27] Silicon/NXP: Add support for iMX SDHC Chris Co
2018-12-05 10:31   ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 14/27] Silicon/NXP: Add i.MX6 GPT and EPIT timer headers Chris Co
2018-11-08 18:14   ` Leif Lindholm
2018-12-04  2:06     ` Chris Co
2018-12-04 12:58       ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 15/27] Silicon/NXP: Add i.MX6 GPT Timer library Chris Co
2018-12-13 17:26   ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 16/27] Silicon/NXP: Add i.MX6 Timer DXE driver Chris Co
2018-12-13 17:33   ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 17/27] Silicon/NXP: Add i.MX6 USB Phy Library Chris Co
2018-12-14 17:10   ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 18/27] Silicon/NXP: Add i.MX6 Clock Library Chris Co
2018-12-14 18:12   ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 20/27] Silicon/NXP: Add i.MX6 Board init library Chris Co
2018-12-14 20:12   ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 19/27] Silicon/NXP: Add i.MX6 ACPI tables Chris Co
2018-12-14 19:53   ` Leif Lindholm
2018-12-17 11:14   ` Ard Biesheuvel
2019-01-08 21:43     ` Chris Co
2019-01-29 14:09       ` Ard Biesheuvel
2018-09-21  8:26 ` [PATCH edk2-platforms 21/27] Silicon/NXP: Add i.MX6 PCIe DXE driver Chris Co
2018-12-14 21:59   ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 23/27] Silicon/NXP: Add i.MX6 Smbios Driver Chris Co
2018-12-14 23:07   ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 22/27] Silicon/NXP: Add i.MX6 GOP driver Chris Co
2018-12-14 22:37   ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 24/27] Silicon/NXP: Add i.MX6 common dsc and fdf files Chris Co
2018-12-14 23:36   ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 25/27] Platform/Solidrun: Add Hummingboard Peripheral Initialization Chris Co
2018-12-15 12:12   ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 26/27] Platform/SolidRun: Add i.MX 6Quad Hummingboard Edge ACPI tables Chris Co
2018-12-15 12:19   ` Leif Lindholm
2018-09-21  8:26 ` [PATCH edk2-platforms 27/27] Platform/Solidrun: Add i.MX 6Quad Hummingboard Edge dsc and fdf files Chris Co
2018-12-15 12:28   ` Leif Lindholm
2018-12-15 13:32 ` [PATCH edk2-platforms 00/27] Import Hummingboard Edge platform for Windows IoT Core Leif Lindholm
2018-12-19 18:28   ` Chris Co

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180921082542.35768-3-christopher.co@microsoft.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox