[edk2] [PATCH v1 edk2-platfoms 1/2] Platform/Broadcom: Add Raspberry Pi 3 support

Pete Batard pete at akeo.ie
Fri Dec 7 04:05:10 PST 2018


This commit contains the edk2-platform components that introduce
support for the Raspberry Pi 3 as a platform, based on the original
work by Ard Biesheuvel and Andrei Warkentin.

Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Pete Batard <pete at akeo.ie>
---
 Platform/Broadcom/Bcm283x/AcpiTables/AcpiTables.inf                                   |   51 +
 Platform/Broadcom/Bcm283x/AcpiTables/Csrt.aslc                                        |  337 ++++
 Platform/Broadcom/Bcm283x/AcpiTables/Dbg2.aslc                                        |   32 +
 Platform/Broadcom/Bcm283x/AcpiTables/Dsdt.asl                                         |  523 ++++++
 Platform/Broadcom/Bcm283x/AcpiTables/Fadt.aslc                                        |   50 +
 Platform/Broadcom/Bcm283x/AcpiTables/Gtdt.aslc                                        |   31 +
 Platform/Broadcom/Bcm283x/AcpiTables/Madt.aslc                                        |   60 +
 Platform/Broadcom/Bcm283x/AcpiTables/Pep.asl                                          |   92 +
 Platform/Broadcom/Bcm283x/AcpiTables/Pep.c                                            |   84 +
 Platform/Broadcom/Bcm283x/AcpiTables/Pep.h                                            |  126 ++
 Platform/Broadcom/Bcm283x/AcpiTables/Platform.h                                       |   82 +
 Platform/Broadcom/Bcm283x/AcpiTables/Rhpx.asl                                         |  201 +++
 Platform/Broadcom/Bcm283x/AcpiTables/Sdhc.asl                                         |  105 ++
 Platform/Broadcom/Bcm283x/AcpiTables/Spcr.asl                                         |   53 +
 Platform/Broadcom/Bcm283x/AcpiTables/Uart.asl                                         |  155 ++
 Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c                 |  730 ++++++++
 Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.h                 |   50 +
 Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf               |   53 +
 Platform/Broadcom/Bcm283x/Drivers/Bcm2836InterruptDxe/Bcm2836InterruptDxe.c           |  367 ++++
 Platform/Broadcom/Bcm283x/Drivers/Bcm2836InterruptDxe/Bcm2836InterruptDxe.inf         |   48 +
 Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxe.c                               |  356 ++++
 Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxe.inf                             |   81 +
 Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxeFormSetGuid.h                    |   23 +
 Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxeHii.uni                          |  100 ++
 Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxeHii.vfr                          |  306 ++++
 Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/ComponentName.c                          |  222 +++
 Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.c                             |  606 +++++++
 Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.h                             |   43 +
 Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.inf                           |   71 +
 Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/Screenshot.c                             |  379 ++++
 Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/ComponentName.c                  |  183 ++
 Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsole.c                | 1836 ++++++++++++++++++++
 Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsole.h                |  591 +++++++
 Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsoleDxe.inf           |   74 +
 Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsoleDxe.uni           |   19 +
 Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsoleDxeExtra.uni      |   20 +
 Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/NewFont.c                        |  288 +++
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/ComponentName.c                            |  163 ++
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Diagnostics.c                              |  256 +++
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.c                                      |  458 +++++
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.h                                      |  533 ++++++
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcBlockIo.c                               |  473 +++++
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDebug.c                                 |  169 ++
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDxe.inf                                 |   58 +
 Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcIdentification.c                        |  993 +++++++++++
 Platform/Broadcom/Bcm283x/Drivers/PlatformSmbiosDxe/PlatformSmbiosDxe.c               |  915 ++++++++++
 Platform/Broadcom/Bcm283x/Drivers/PlatformSmbiosDxe/PlatformSmbiosDxe.inf             |   56 +
 Platform/Broadcom/Bcm283x/Drivers/RpiFdtDxe/RpiFdtDxe.c                               |  370 ++++
 Platform/Broadcom/Bcm283x/Drivers/RpiFdtDxe/RpiFdtDxe.inf                             |   53 +
 Platform/Broadcom/Bcm283x/Drivers/RpiFirmwareDxe/RpiFirmwareDxe.c                     | 1085 ++++++++++++
 Platform/Broadcom/Bcm283x/Drivers/RpiFirmwareDxe/RpiFirmwareDxe.inf                   |   49 +
 Platform/Broadcom/Bcm283x/Drivers/SdHostDxe/SdHostDxe.c                               |  830 +++++++++
 Platform/Broadcom/Bcm283x/Drivers/SdHostDxe/SdHostDxe.inf                             |   54 +
 Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/FileIo.c                         |  196 +++
 Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/FvbInfo.c                        |  118 ++
 Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockService.c                |  984 +++++++++++
 Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockService.h                |  217 +++
 Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockServiceDxe.c             |  334 ++++
 Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockServiceDxe.inf           |   93 +
 Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836.h                          |   70 +
 Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836MmcHs.h                     |  199 +++
 Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836SdHost.h                    |   92 +
 Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2837Gpio.h                      |   50 +
 Platform/Broadcom/Bcm283x/Include/IndustryStandard/RpiFirmware.h                      |   93 +
 Platform/Broadcom/Bcm283x/Include/Library/GpioLib.h                                   |   33 +
 Platform/Broadcom/Bcm283x/Include/Protocol/DwUsb.h                                    |   53 +
 Platform/Broadcom/Bcm283x/Include/Protocol/ExtendedTextOut.h                          |   36 +
 Platform/Broadcom/Bcm283x/Include/Protocol/PiMmcHost.h                                |  187 ++
 Platform/Broadcom/Bcm283x/Include/Protocol/RaspberryPiFirmware.h                      |  131 ++
 Platform/Broadcom/Bcm283x/Include/Utils.h                                             |   33 +
 Platform/Broadcom/Bcm283x/Library/GpioLib/GpioLib.c                                   |   79 +
 Platform/Broadcom/Bcm283x/Library/GpioLib/GpioLib.inf                                 |   39 +
 Platform/Broadcom/Bcm283x/Library/MemoryInitPeiLib/MemoryInitPeiLib.c                 |  183 ++
 Platform/Broadcom/Bcm283x/Library/MemoryInitPeiLib/MemoryInitPeiLib.inf               |   51 +
 Platform/Broadcom/Bcm283x/Library/PlatformBootManagerLib/PlatformBm.c                 |  831 +++++++++
 Platform/Broadcom/Bcm283x/Library/PlatformBootManagerLib/PlatformBm.h                 |   60 +
 Platform/Broadcom/Bcm283x/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf   |   90 +
 Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/AArch64/RaspberryPiHelper.S  |  107 ++
 Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/RaspberryPi.c                |   99 ++
 Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/RaspberryPiMem.c             |  160 ++
 Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/RaspberryPiPlatformLib.inf   |   64 +
 Platform/Broadcom/Bcm283x/Library/ResetLib/ResetLib.c                                 |  104 ++
 Platform/Broadcom/Bcm283x/Library/ResetLib/ResetLib.inf                               |   46 +
 Platform/Broadcom/Bcm283x/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c   |  222 +++
 Platform/Broadcom/Bcm283x/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf |   43 +
 Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec                                          |   63 +
 Platform/Broadcom/Bcm283x/RaspberryPiPkg.dsc                                          |  636 +++++++
 Platform/Broadcom/Bcm283x/RaspberryPiPkg.fdf                                          |  450 +++++
 Platform/Broadcom/Bcm283x/Readme.md                                                   |  263 +++
 89 files changed, 21452 insertions(+)

diff --git a/Platform/Broadcom/Bcm283x/AcpiTables/AcpiTables.inf b/Platform/Broadcom/Bcm283x/AcpiTables/AcpiTables.inf
new file mode 100644
index 000000000000..db0270270cf9
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/AcpiTables/AcpiTables.inf
@@ -0,0 +1,51 @@
+#/** @file
+#
+#  ACPI table data and ASL sources required to boot the platform.
+#
+#  Copyright (c) 2017, Andrey Warkentin <andrey.warkentin at gmail.com>
+#  Copyright (c) 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                    = 0x00010005
+  BASE_NAME                      = AcpiTables
+  FILE_GUID                      = 7E374E25-8E01-4FEE-87F2-390C23C606CD
+  MODULE_TYPE                    = USER_DEFINED
+  VERSION_STRING                 = 1.0
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = AARCH64
+#
+
+[Sources]
+  Platform.h
+  Madt.aslc
+  Fadt.aslc
+  Dbg2.aslc
+  Gtdt.aslc
+  Dsdt.asl
+  Csrt.aslc
+  Spcr.asl
+
+[Packages]
+  MdePkg/MdePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+
+[FixedPcd]
+  gEmbeddedTokenSpaceGuid.PcdInterruptBaseAddress
+
+[BuildOptions]
+  # The default '-mcmodel=small' used with DEBUG produces a GenFw error when compiling CSRT.acpi:
+  # "AARCH64 small code model requires identical ELF and PE/COFF section offsets modulo 4 KB."
+  GCC:DEBUG_*_AARCH64_CC_FLAGS = -mcmodel=tiny
diff --git a/Platform/Broadcom/Bcm283x/AcpiTables/Csrt.aslc b/Platform/Broadcom/Bcm283x/AcpiTables/Csrt.aslc
new file mode 100644
index 000000000000..926942ec8da3
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/AcpiTables/Csrt.aslc
@@ -0,0 +1,337 @@
+/** @file
+ *
+ *  Core System Resource Table (CSRT)
+ *
+ *  Copyright (c) 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 "Platform.h"
+
+#define DMA_MAX_REQ_LINES 32
+
+#pragma pack(push, 1)
+
+//------------------------------------------------------------------------
+// DMA Controller Vendor Data for RPi3
+//------------------------------------------------------------------------
+typedef struct
+{
+    UINT32 Length;
+    UINT32 Type;
+    UINT64 ChannelsBaseAddress;
+    UINT32 ChannelsBaseSize;
+    UINT64 ControllerBaseAddress;
+    UINT32 ControllerBaseSize;
+    UINT32 ChannelCount;
+    UINT32 ControllerInterrupt;
+    UINT32 MinimumRequestLine;
+    UINT32 MaximumRequestLine;
+    BOOLEAN CacheCoherent;
+} DMA_CONTROLLER_VENDOR_DATA;
+
+//------------------------------------------------------------------------
+// DMA Controller on RPi3
+//------------------------------------------------------------------------
+typedef struct
+{
+    EFI_ACPI_5_0_CSRT_RESOURCE_DESCRIPTOR_HEADER DmaControllerHeader;
+    DMA_CONTROLLER_VENDOR_DATA ControllerVendorData;
+} RD_DMA_CONTROLLER;
+
+//------------------------------------------------------------------------
+// DMA Channel Vendor Data for RPi3
+//------------------------------------------------------------------------
+typedef struct
+{
+    UINT32 ChannelNumber;
+    UINT32 ChannelInterrupt;
+    UINT16 IsReservedChannel;
+    UINT16 NoSrcNoDestAddrIncr;
+} DMA_CHANNEL_VENDOR_DATA;
+
+//------------------------------------------------------------------------
+// DMA Channel on RPi3
+//------------------------------------------------------------------------
+typedef struct
+{
+    EFI_ACPI_5_0_CSRT_RESOURCE_DESCRIPTOR_HEADER DmaChannelHeader;
+    DMA_CHANNEL_VENDOR_DATA ChannelVendorData;
+} RD_DMA_CHANNEL;
+
+//------------------------------------------------------------------------
+// DMA Resource Group on RPi3
+//------------------------------------------------------------------------
+
+typedef struct
+{
+    EFI_ACPI_5_0_CSRT_RESOURCE_GROUP_HEADER ResGroupHeader;
+    RD_DMA_CONTROLLER DmaController;
+    RD_DMA_CHANNEL DmaChannels[RPI3_DMA_CHANNEL_COUNT];
+} RG_DMA;
+
+//----------------------------------------------------------------------------
+// CSRT table structure for RPi3 platform - current revision only includes DMA
+//----------------------------------------------------------------------------
+typedef struct
+{
+// Standard ACPI Header
+    EFI_ACPI_DESCRIPTION_HEADER CsrtHeader;
+
+// DMA Resource Group
+    RG_DMA DmaResourceGroup;
+
+} EFI_ACPI_5_0_CSRT_TABLE;
+
+
+
+EFI_ACPI_5_0_CSRT_TABLE Csrt =
+{
+    //------------------------------------------------------------------------
+    // ACPI Table Header
+    //------------------------------------------------------------------------
+    {
+        EFI_ACPI_5_0_CORE_SYSTEM_RESOURCE_TABLE_SIGNATURE,    // Signature "CSRT"
+        sizeof(EFI_ACPI_DESCRIPTION_HEADER) + sizeof(RG_DMA), // Length
+        EFI_ACPI_5_0_CSRT_REVISION,     // Revision
+        0x00,                           // Checksum calculated at runtime.
+        EFI_ACPI_OEM_ID,                // OEMID is a 6 bytes long field "BC2836"
+        EFI_ACPI_OEM_TABLE_ID,          // OEM table identification(8 bytes long) "RPI3EDK2"
+        EFI_ACPI_OEM_REVISION,          // OEM revision number.
+        EFI_ACPI_CREATOR_ID,            // ASL compiler vendor ID.
+        EFI_ACPI_CREATOR_REVISION       // ASL compiler revision number.
+    },
+
+    //------------------------------------------------------------------------
+    // DMA Resource Group
+    //------------------------------------------------------------------------
+    {
+
+        //------------------------------------------------------------------------
+        // DMA Resource Group Header
+        //------------------------------------------------------------------------
+        {
+            sizeof(RG_DMA),                 // Resource Group Length
+            EFI_ACPI_VENDOR_ID,             // VendorId
+            0,                              // SubvendorId
+            EFI_ACPI_CSRT_DEVICE_ID_DMA,    // DeviceId 9
+            0,                              // SubdeviceId
+            0,                              // Revision
+            0,                              // Reserved
+            0                               // SharedInfoLength
+        },
+
+
+        //-------------------------------------------------------------------------------
+        // Resource Descriptor - DMA Controller
+        //-------------------------------------------------------------------------------
+        {
+            {
+                sizeof(RD_DMA_CONTROLLER),                        // Length of this Resource Descriptor
+                EFI_ACPI_CSRT_RESOURCE_TYPE_DMA,                  // Type for this resource 3=DMA
+                EFI_ACPI_CSRT_RESOURCE_SUBTYPE_DMA_CONTROLLER,    // Subtype for this resource 1=DMA Controller
+                EFI_ACPI_CSRT_RESOURCE_ID_IN_DMA_GRP+0,           // ResourceId - 1st DMA controller
+            },
+            {
+                sizeof(DMA_CONTROLLER_VENDOR_DATA),               // controller vendor data here
+                1,
+                0x3F007000,                   // Base address for channels
+                RPI3_DMA_CHANNEL_COUNT*0x100, // Base size = Number of channels x 0x100 size for each channel
+                0x3F007FE0,                   // Base address for controller
+                8,                            // Base size = two registers
+                RPI3_DMA_USED_CHANNEL_COUNT,
+                0,                            // cannot use controller interrupt
+                0,                            // Minimum Request Line
+                DMA_MAX_REQ_LINES-1,          // Maximum Request Line
+                FALSE,
+            },
+        },
+
+        //------------------------------------------------------------------------
+        // Resource Descriptor(s) - DMA Channels 0 to n-1
+        //------------------------------------------------------------------------
+        {
+
+            // channel 0
+            {
+                {
+                    sizeof(RD_DMA_CHANNEL),                     // Length of this Resource Descriptor
+                    EFI_ACPI_CSRT_RESOURCE_TYPE_DMA,            // Type for this resource 3=DMA
+                    EFI_ACPI_CSRT_RESOURCE_SUBTYPE_DMA_CHANNEL, // Subtype for this resource 0=DMA Channel
+                    EFI_ACPI_CSRT_RESOURCE_ID_IN_DMA_GRP+1,     // ResourceId
+                },
+                {
+                    0,        // channel vendor data here
+                    0x30,     // 16+32 dma_int[0]
+                    0,
+                    0
+                },
+            },
+
+            // channel 1 reserved
+            {
+                {
+                    sizeof(RD_DMA_CHANNEL),
+                    EFI_ACPI_CSRT_RESOURCE_TYPE_DMA,
+                    EFI_ACPI_CSRT_RESOURCE_SUBTYPE_DMA_CHANNEL,
+                    EFI_ACPI_CSRT_RESOURCE_ID_IN_DMA_GRP+2,            // ResourceId
+                },
+                {
+                    1,        // channel vendor data here
+                    0x31,     // 17+32 dma_int[1]
+                    1,
+                    0
+                },
+            },
+
+            // channel 2 - VC4 use only
+            {
+                {
+                    sizeof(RD_DMA_CHANNEL),
+                    EFI_ACPI_CSRT_RESOURCE_TYPE_DMA,
+                    EFI_ACPI_CSRT_RESOURCE_SUBTYPE_DMA_CHANNEL,
+                    EFI_ACPI_CSRT_RESOURCE_ID_IN_DMA_GRP+3,        // ResourceId
+                },
+                {
+                    2,        // channel vendor data here
+                    0x32,     // 18+32 dma_int[2]
+                    1,
+                    0
+                },
+            },
+
+            // channel 3 - VC4 use only
+            {
+                {
+                    sizeof(RD_DMA_CHANNEL),
+                    EFI_ACPI_CSRT_RESOURCE_TYPE_DMA,
+                    EFI_ACPI_CSRT_RESOURCE_SUBTYPE_DMA_CHANNEL,
+                    EFI_ACPI_CSRT_RESOURCE_ID_IN_DMA_GRP+4,
+                },
+                {
+                    3,        // channel vendor data here
+                    0x33,     // 19+32 dma_int[3]
+                    1,
+                    0
+                },
+            },
+
+            // channel 4
+            {
+                {
+                    sizeof(RD_DMA_CHANNEL),
+                    EFI_ACPI_CSRT_RESOURCE_TYPE_DMA,
+                    EFI_ACPI_CSRT_RESOURCE_SUBTYPE_DMA_CHANNEL,
+                    EFI_ACPI_CSRT_RESOURCE_ID_IN_DMA_GRP+5,
+                },
+                {
+                    4,        // channel vendor data here
+                    0x34,     // 20+32 dma_int[4]
+                    0,
+                    1         // SD host controller candidate
+                },
+            },
+
+            // channel 5
+            {
+                {
+                    sizeof(RD_DMA_CHANNEL),
+                    EFI_ACPI_CSRT_RESOURCE_TYPE_DMA,
+                    EFI_ACPI_CSRT_RESOURCE_SUBTYPE_DMA_CHANNEL,
+                    EFI_ACPI_CSRT_RESOURCE_ID_IN_DMA_GRP+6,
+                },
+                {
+                    5,        // channel vendor data here
+                    0x35,     // 21+32 dma_int[5]
+                    0,
+                    0
+                },
+            },
+
+            // channel 6 is reserved
+            {
+                {
+                    sizeof(RD_DMA_CHANNEL),
+                    EFI_ACPI_CSRT_RESOURCE_TYPE_DMA,
+                    EFI_ACPI_CSRT_RESOURCE_SUBTYPE_DMA_CHANNEL,
+                    EFI_ACPI_CSRT_RESOURCE_ID_IN_DMA_GRP+7,
+                },
+                {
+                    6,        // channel vendor data here
+                    0x36,     // 22+32 dma_int[6]
+                    1,
+                    0
+                },
+            },
+
+            // channel 7 is reserved
+            {
+                {
+                    sizeof(RD_DMA_CHANNEL),
+                    EFI_ACPI_CSRT_RESOURCE_TYPE_DMA,
+                    EFI_ACPI_CSRT_RESOURCE_SUBTYPE_DMA_CHANNEL,
+                    EFI_ACPI_CSRT_RESOURCE_ID_IN_DMA_GRP+8,
+                },
+                {
+                    7,        // channel vendor data here
+                    0x37,     // 23+32 dma_int[7]
+                    1,
+                    0
+                },
+            },
+
+            // channel 8
+            {
+                {
+                    sizeof(RD_DMA_CHANNEL),
+                    EFI_ACPI_CSRT_RESOURCE_TYPE_DMA,
+                    EFI_ACPI_CSRT_RESOURCE_SUBTYPE_DMA_CHANNEL,
+                    EFI_ACPI_CSRT_RESOURCE_ID_IN_DMA_GRP+9,
+                },
+                {
+                    8,        // channel vendor data here
+                    0x38,     // 24+32 dma_int[8]
+                    0,
+                    0
+                },
+            },
+
+            // channel 9
+            {
+                {
+                    sizeof(RD_DMA_CHANNEL),
+                    EFI_ACPI_CSRT_RESOURCE_TYPE_DMA,
+                    EFI_ACPI_CSRT_RESOURCE_SUBTYPE_DMA_CHANNEL,
+                    EFI_ACPI_CSRT_RESOURCE_ID_IN_DMA_GRP+10,
+                },
+                {
+                    9,        // channel vendor data here
+                    0x39,     // 25+32 dma_int[9]
+                    0,
+                    0
+                },
+            }
+
+        } // end DMA Channels 0 to 14
+
+    } // end DMA Resource group
+};
+
+#pragma pack(pop)
+
+VOID* ReferenceAcpiTable(VOID)
+{
+//
+// Reference the table being generated to prevent the optimizer from removing the
+// data structure from the exeutable
+//
+  return (VOID*)&Csrt;
+}
diff --git a/Platform/Broadcom/Bcm283x/AcpiTables/Dbg2.aslc b/Platform/Broadcom/Bcm283x/AcpiTables/Dbg2.aslc
new file mode 100644
index 000000000000..b728a5f00cfb
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/AcpiTables/Dbg2.aslc
@@ -0,0 +1,32 @@
+/** @file
+ *
+ *  Debug Port Table (DBG2)
+ *
+ *  Copyright (c) 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.
+ *
+ **/
+
+char DBG2[92] = {
+    0x44, 0x42, 0x47, 0x32, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x4D, 0x53, 0x46, 0x54, 0x20, 0x20, 0x45, 0x44, 0x4B, 0x32,
+    0x20, 0x20, 0x20, 0x20, 0x01, 0x00, 0x00, 0x00, 0x4D, 0x53,
+    0x46, 0x54, 0x01, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x01, 0x0A, 0x00,
+    0x26, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x80, 0x10, 0x00,
+    0x00, 0x00, 0x16, 0x00, 0x22, 0x00, 0x00, 0x20, 0x00, 0x10,
+    0x00, 0x50, 0x21, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x00,
+    0x00, 0x00, '\\',  '_',  'S',  'B',  '.',  'U',  'R',  'T',
+    'M', 0x00,
+};
+
+void * ReferenceAcpiTable(void) {
+    return (void *) &DBG2;
+}
diff --git a/Platform/Broadcom/Bcm283x/AcpiTables/Dsdt.asl b/Platform/Broadcom/Bcm283x/AcpiTables/Dsdt.asl
new file mode 100644
index 000000000000..69897cb921f7
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/AcpiTables/Dsdt.asl
@@ -0,0 +1,523 @@
+/** @file
+ *
+ *  Differentiated System Definition Table (DSDT)
+ *
+ *  Copyright (c) 2018, Andrey Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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.
+ *
+ **/
+
+#define BCM_ALT0 0x4
+#define BCM_ALT1 0x5
+#define BCM_ALT2 0x6
+#define BCM_ALT3 0x7
+#define BCM_ALT4 0x3
+#define BCM_ALT5 0x2
+
+DefinitionBlock ("Dsdt.aml", "DSDT", 5, "MSFT", "EDK2", 2)
+{
+    Scope (\_SB_)
+    {
+        include("Sdhc.asl")
+        include("Pep.asl")
+
+        //
+        // Description: This is a Processor Device
+        //
+
+        Device (CPU0)
+        {
+            Name (_HID, "ACPI0007")
+            Name (_UID, 0x0)
+            Method (_STA)
+            {
+                Return(0xf)
+            }
+        }
+
+        //
+        // Description: This is a Processor Device
+        //
+
+        Device (CPU1)
+        {
+            Name (_HID, "ACPI0007")
+            Name (_UID, 0x1)
+            Method (_STA)
+            {
+                Return(0xf)
+            }
+        }
+
+        //
+        // Description: This is a Processor Device
+        //
+
+        Device (CPU2)
+        {
+            Name (_HID, "ACPI0007")
+            Name (_UID, 0x2)
+            Method (_STA)
+            {
+                Return(0xf)
+            }
+        }
+
+        //
+        // Description: This is a Processor Device
+        //
+
+        Device (CPU3)
+        {
+            Name (_HID, "ACPI0007")
+            Name (_UID, 0x3)
+            Method (_STA)
+            {
+                Return(0xf)
+            }
+        }
+
+        //
+        // Description: DWC OTG Controller
+        //
+
+        Device (USB0)
+        {
+            Name (_HID, "BCM2848")
+            Name (_CID, Package() { "DWC_OTG", "DWC2_OTG"})
+            Name (_UID, 0x0)
+            Method (_STA)
+            {
+                Return(0xf)
+            }
+            Method (_CRS, 0x0, Serialized) {
+                Name (RBUF, ResourceTemplate () {
+                    MEMORY32FIXED(ReadWrite, 0x3F980000, 0x10000, )
+                    Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) { 0x29 }
+                })
+                Return(RBUF)
+            }
+        }
+
+        //
+        // Description: Video Core 4 GPU
+        //
+
+        Device (GPU0)
+        {
+            Name (_HID, "BCM2850")
+            Name (_CID, "VC4")
+            Name (_UID, 0x0)
+            Method (_STA)
+            {
+                Return(0xf)
+            }
+            Method (_CRS, 0x0, Serialized) {
+                Name (RBUF, ResourceTemplate () {
+                    // Memory and interrupt for the GPU
+                    MEMORY32FIXED(ReadWrite, 0x3FC00000, 0x1000, )
+                    Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) { 0x2A }
+
+                    // HVS - Hardware Video Scalar
+                    MEMORY32FIXED(ReadWrite, 0x3F400000, 0x6000, )
+                    // The HVS interrupt is reserved by the VPU
+                    // Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) { 0x41 }
+
+                    // PixelValve0 - DSI0 or DPI
+                    // MEMORY32FIXED(ReadWrite, 0x3F206000, 0x100, )
+                    // Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) { 0x4D }
+
+                    // PixelValve1 - DS1 or SMI
+                    // MEMORY32FIXED(ReadWrite, 0x73F207000, 0x100, )
+                    // Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) { 0x4E }
+
+                    // PixelValve2 - HDMI output - connected to HVS display FIFO 1
+                    MEMORY32FIXED(ReadWrite, 0x3F807000, 0x100, )
+                    Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) { 0x4A }
+
+                    // HDMI registers
+                    MEMORY32FIXED(ReadWrite, 0x3F902000, 0x600, )   // HDMI registers
+                    MEMORY32FIXED(ReadWrite, 0x3F808000, 0x100, )   // HD registers
+                    // hdmi_int[0]
+                    // Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) { 0x48 }
+                    // hdmi_int[1]
+                    // Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) { 0x49 }
+
+                    // HDMI DDC connection
+                    I2CSerialBus(0x50,, 100000,, "\\_SB.I2C2",,,,)  // EDID
+                    I2CSerialBus(0x30,, 100000,, "\\_SB.I2C2",,,,)  // E-DDC Segment Pointer
+                })
+                Return(RBUF)
+            }
+
+            // GPU Power Management Component Data
+            // Reference : https://github.com/Microsoft/graphics-driver-samples/wiki/Install-Driver-in-a-Windows-VM
+            Method(PMCD, 0, Serialized) {
+
+              Name(RBUF, Package()  {
+                1,                  // Version
+                1,                  // Number of graphics power components
+
+                Package() {         // Power components package
+
+                  Package() {       // GPU component package
+                    0,              // Component Index
+                    0,              // DXGK_POWER_COMPONENT_MAPPING.ComponentType (0 = DXGK_POWER_COMPONENT_ENGINE)
+                    0,              // DXGK_POWER_COMPONENT_MAPPING.NodeIndex
+
+                    Buffer() {      // DXGK_POWER_RUNTIME_COMPONENT.ComponentGuid
+                                    // 9B2D1E26-1575-4747-8FC0-B9EB4BAA2D2B
+                      0x26, 0x1E, 0x2D, 0x9B, 0x75, 0x15, 0x47, 0x47,
+                      0x8f, 0xc0, 0xb9, 0xeb, 0x4b, 0xaa, 0x2d, 0x2b
+                    },
+
+                    "VC4_Engine_00",// DXGK_POWER_RUNTIME_COMPONENT.ComponentName
+                    2,              // DXGK_POWER_RUNTIME_COMPONENT.StateCount
+
+                    Package() {     // DXGK_POWER_RUNTIME_COMPONENT.States[] package
+
+                      Package() {   // F0
+                         0,         // DXGK_POWER_RUNTIME_STATE.TransitionLatency
+                         0,         // DXGK_POWER_RUNTIME_STATE.ResidencyRequirement
+                         1210000,   // DXGK_POWER_RUNTIME_STATE.NominalPower (microwatt)
+                      },
+
+                      Package() {   // F1 - Placeholder
+                         10000,     // DXGK_POWER_RUNTIME_STATE.TransitionLatency
+                         10000,     // DXGK_POWER_RUNTIME_STATE.ResidencyRequirement
+                         4,         // DXGK_POWER_RUNTIME_STATE.NominalPower
+                      },
+                    }
+                  }
+                }
+              })
+              Return(RBUF)
+            }
+        }
+
+        //
+        // Description: PiQ Mailbox Driver
+        //
+
+        Device (RPIQ)
+        {
+            Name (_HID, "BCM2849")
+            Name (_CID, "RPIQ")
+            Name (_UID, 0)
+            Method (_STA)
+            {
+                Return(0xf)
+            }
+            Method (_CRS, 0x0, Serialized) {
+                Name (RBUF, ResourceTemplate () {
+                    Memory32Fixed (ReadWrite, 0x3F00B880, 0x00000024, )
+                    Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) { 0x61 }
+                })
+                Return (RBUF)
+            }
+        }
+
+        //
+        // Description: VCHIQ Driver
+        //
+
+        Device (VCIQ)
+        {
+            Name (_HID, "BCM2835")
+            Name (_CID, "VCIQ")
+            Name (_UID, 0)
+            Name (_DEP, Package() { \_SB.RPIQ })
+            Method (_STA)
+            {
+                Return(0xf)
+            }
+            Method (_CRS, 0x0, Serialized) {
+                Name (RBUF, ResourceTemplate () {
+                    Memory32Fixed (ReadWrite, 0x3F00B840, 0x00000010, )
+                    Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) { 0x62 }
+                })
+                Return (RBUF)
+            }
+        }
+
+        //
+        // Description: VC Shared Memory Driver
+        //
+
+        Device (VCSM)
+        {
+            Name (_HID, "BCM2856")
+            Name (_CID, "VCSM")
+            Name (_UID, 0)
+            Name (_DEP, Package() { \_SB.VCIQ })
+            Method (_STA)
+            {
+                Return(0xf)
+            }
+        }
+
+        //
+        // Description: GPIO
+        //
+        Device (GPI0)
+        {
+            Name (_HID, "BCM2845")
+            Name (_CID, "BCMGPIO")
+            Name (_UID, 0x0)
+            Method (_STA)
+            {
+                Return(0xf)
+            }
+            Method (_CRS, 0x0, Serialized) {
+                Name (RBUF, ResourceTemplate () {
+                    MEMORY32FIXED(ReadWrite, 0x3F200000, 0xB4, )
+                    Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) { 0x51 }
+                    Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) { 0x53 }
+                })
+                Return(RBUF)
+            }
+        }
+
+        //
+        // Description: I2C
+        //
+
+        Device (I2C1)
+        {
+            Name (_HID, "BCM2841")
+            Name (_CID, "BCMI2C")
+            Name (_UID, 0x1)
+            Method (_STA)
+            {
+                Return(0xf)
+            }
+            Method (_CRS, 0x0, Serialized)
+            {
+                Name (RBUF, ResourceTemplate()
+                {
+                    Memory32Fixed(ReadWrite, 0x3F804000, 0x20)
+                    Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) {0x55}
+                    //
+                    // MsftFunctionConfig is encoded as the VendorLong.
+                    //
+                    // MsftFunctionConfig(Exclusive, PullUp, BCM_ALT0, "\\_SB.GPI0", 0, ResourceConsumer, ) {2, 3}
+                    //
+                    VendorLong  ()      // Length = 0x31
+                    {
+                        /* 0000 */  0x00, 0x60, 0x44, 0xD5, 0xF3, 0x1F, 0x11, 0x60,  // .`D....`
+                        /* 0008 */  0x4A, 0xB8, 0xB0, 0x9C, 0x2D, 0x23, 0x30, 0xDD,  // J...-#0.
+                        /* 0010 */  0x2F, 0x8D, 0x1D, 0x00, 0x01, 0x10, 0x00, 0x01,  // /.......
+                        /* 0018 */  0x04, 0x00, 0x12, 0x00, 0x00, 0x16, 0x00, 0x20,  // .......
+                        /* 0020 */  0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x5C,  // .......\
+                        /* 0028 */  0x5F, 0x53, 0x42, 0x2E, 0x47, 0x50, 0x49, 0x30,  // _SB.GPI0
+                        /* 0030 */  0x00                                             // .
+                    }
+
+                })
+                Return(RBUF)
+            }
+        }
+
+        //
+        // I2C2 is the HDMI DDC connection
+        //
+
+        Device (I2C2)
+        {
+            Name (_HID, "BCM2841")
+            Name (_CID, "BCMI2C")
+            Name (_UID, 0x2)
+            Method (_STA)
+            {
+                Return(0xf)
+            }
+            Method (_CRS, 0x0, Serialized)
+            {
+                Name (RBUF, ResourceTemplate()
+                {
+                    Memory32Fixed(ReadWrite, 0x3F805000, 0x20)
+                    Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) {0x55}
+                })
+                Return(RBUF)
+            }
+        }
+
+        //
+        // Description: SPI
+        //
+
+        Device (SPI0)
+        {
+            Name (_HID, "BCM2838")
+            Name (_CID, "BCMSPI0")
+            Name (_UID, 0x0)
+            Method (_STA)
+            {
+                Return(0xf)
+            }
+            Method (_CRS, 0x0, Serialized) {
+                Name (RBUF, ResourceTemplate () {
+                    MEMORY32FIXED(ReadWrite, 0x3F204000, 0x20, )
+                    Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) {0x56}
+
+                    //
+                    // MsftFunctionConfig is encoded as the VendorLong.
+                    //
+                    // MsftFunctionConfig(Exclusive, PullDown, BCM_ALT0, "\\_SB.GPI0", 0, ResourceConsumer, ) {9, 10, 11} // MISO, MOSI, SCLK
+                    VendorLong  ()      // Length = 0x33
+                    {
+                        /* 0000 */  0x00, 0x60, 0x44, 0xD5, 0xF3, 0x1F, 0x11, 0x60,  // .`D....`
+                        /* 0008 */  0x4A, 0xB8, 0xB0, 0x9C, 0x2D, 0x23, 0x30, 0xDD,  // J...-#0.
+                        /* 0010 */  0x2F, 0x8D, 0x1F, 0x00, 0x01, 0x10, 0x00, 0x02,  // /.......
+                        /* 0018 */  0x04, 0x00, 0x12, 0x00, 0x00, 0x18, 0x00, 0x22,  // ......."
+                        /* 0020 */  0x00, 0x00, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B,  // ........
+                        /* 0028 */  0x00, 0x5C, 0x5F, 0x53, 0x42, 0x2E, 0x47, 0x50,  // .\_SB.GP
+                        /* 0030 */  0x49, 0x30, 0x00                                 // I0.
+                    }
+
+                    //
+                    // MsftFunctionConfig is encoded as the VendorLong.
+                    //
+                    // MsftFunctionConfig(Exclusive, PullUp, BCM_ALT0, "\\_SB.GPI0", 0, ResourceConsumer, ) {8}     // CE0
+                    VendorLong  ()      // Length = 0x2F
+                    {
+                        /* 0000 */  0x00, 0x60, 0x44, 0xD5, 0xF3, 0x1F, 0x11, 0x60,  // .`D....`
+                        /* 0008 */  0x4A, 0xB8, 0xB0, 0x9C, 0x2D, 0x23, 0x30, 0xDD,  // J...-#0.
+                        /* 0010 */  0x2F, 0x8D, 0x1B, 0x00, 0x01, 0x10, 0x00, 0x01,  // /.......
+                        /* 0018 */  0x04, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x1E,  // ........
+                        /* 0020 */  0x00, 0x00, 0x00, 0x08, 0x00, 0x5C, 0x5F, 0x53,  // .....\_S
+                        /* 0028 */  0x42, 0x2E, 0x47, 0x50, 0x49, 0x30, 0x00         // B.GPI0.
+                    }
+
+                    //
+                    // MsftFunctionConfig is encoded as the VendorLong.
+                    //
+                    // MsftFunctionConfig(Exclusive, PullUp, BCM_ALT0, "\\_SB.GPI0", 0, ResourceConsumer, ) {7}     // CE1
+                    VendorLong  ()      // Length = 0x2F
+                    {
+                        /* 0000 */  0x00, 0x60, 0x44, 0xD5, 0xF3, 0x1F, 0x11, 0x60,  // .`D....`
+                        /* 0008 */  0x4A, 0xB8, 0xB0, 0x9C, 0x2D, 0x23, 0x30, 0xDD,  // J...-#0.
+                        /* 0010 */  0x2F, 0x8D, 0x1B, 0x00, 0x01, 0x10, 0x00, 0x01,  // /.......
+                        /* 0018 */  0x04, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x1E,  // ........
+                        /* 0020 */  0x00, 0x00, 0x00, 0x07, 0x00, 0x5C, 0x5F, 0x53,  // .....\_S
+                        /* 0028 */  0x42, 0x2E, 0x47, 0x50, 0x49, 0x30, 0x00         // B.GPI0.
+                    }
+                })
+                Return(RBUF)
+            }
+        }
+
+        Device (SPI1)
+        {
+            Name (_HID, "BCM2839")
+            Name (_CID, "BCMAUXSPI")
+            Name (_UID, 0x1)
+            Name (_DEP, Package() { \_SB.RPIQ })
+            Method (_STA)
+            {
+                Return(0xf)
+            }
+            Method (_CRS, 0x0, Serialized) {
+                Name (RBUF, ResourceTemplate () {
+                    MEMORY32FIXED(ReadWrite, 0x3F215080, 0x40,)
+                    Interrupt(ResourceConsumer, Level, ActiveHigh, Shared,) {0x3D}
+
+                    //
+                    // MsftFunctionConfig is encoded as the VendorLong.
+                    //
+                    // MsftFunctionConfig(Exclusive, PullDown, BCM_ALT4, "\\_SB.GPI0", 0, ResourceConsumer, ) {19, 20, 21} // MISO, MOSI, SCLK
+                    VendorLong  ()      // Length = 0x33
+                    {
+                        /* 0000 */  0x00, 0x60, 0x44, 0xD5, 0xF3, 0x1F, 0x11, 0x60,  // .`D....`
+                        /* 0008 */  0x4A, 0xB8, 0xB0, 0x9C, 0x2D, 0x23, 0x30, 0xDD,  // J...-#0.
+                        /* 0010 */  0x2F, 0x8D, 0x1F, 0x00, 0x01, 0x10, 0x00, 0x02,  // /.......
+                        /* 0018 */  0x03, 0x00, 0x12, 0x00, 0x00, 0x18, 0x00, 0x22,  // ......."
+                        /* 0020 */  0x00, 0x00, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15,  // ........
+                        /* 0028 */  0x00, 0x5C, 0x5F, 0x53, 0x42, 0x2E, 0x47, 0x50,  // .\_SB.GP
+                        /* 0030 */  0x49, 0x30, 0x00                                 // I0.
+                    }
+
+                    //
+                    // MsftFunctionConfig is encoded as the VendorLong.
+                    //
+                    // MsftFunctionConfig(Exclusive, PullDown, BCM_ALT4, "\\_SB.GPI0", 0, ResourceConsumer, ) {16} // CE2
+                    VendorLong  ()      // Length = 0x2F
+                    {
+                        /* 0000 */  0x00, 0x60, 0x44, 0xD5, 0xF3, 0x1F, 0x11, 0x60,  // .`D....`
+                        /* 0008 */  0x4A, 0xB8, 0xB0, 0x9C, 0x2D, 0x23, 0x30, 0xDD,  // J...-#0.
+                        /* 0010 */  0x2F, 0x8D, 0x1B, 0x00, 0x01, 0x10, 0x00, 0x02,  // /.......
+                        /* 0018 */  0x03, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x1E,  // ........
+                        /* 0020 */  0x00, 0x00, 0x00, 0x10, 0x00, 0x5C, 0x5F, 0x53,  // .....\_S
+                        /* 0028 */  0x42, 0x2E, 0x47, 0x50, 0x49, 0x30, 0x00         // B.GPI0.
+                    }
+                })
+                Return(RBUF)
+            }
+        }
+
+        // SPI2 has no pins on GPIO header
+        // Device (SPI2)
+        // {
+            // Name (_HID, "BCM2839")
+            // Name (_CID, "BCMAUXSPI")
+            // Name (_UID, 0x2)
+            // Name (_DEP, Package() { \_SB.RPIQ })
+            // Method (_STA)
+           // {
+                // Return(0xf)     // Disabled
+            // }
+            // Method (_CRS, 0x0, Serialized) {
+                // Name (RBUF, ResourceTemplate () {
+                    // MEMORY32FIXED(ReadWrite, 0x3F2150C0, 0x40,)
+                    // Interrupt(ResourceConsumer, Level, ActiveHigh, Shared,) {0x3D}
+                // })
+                // Return(RBUF)
+            // }
+        // }
+
+        //
+        // Description: PWM Driver
+        //
+
+        Device (PWM0)
+        {
+            Name (_HID, "BCM2844")
+            Name (_CID, "BCM2844")
+            Name (_UID, 0)
+            Method (_STA)
+            {
+                Return(0xf)
+            }
+            Method (_CRS, 0x0, Serialized) {
+                Name (RBUF, ResourceTemplate () {
+                    // DMA channel 11 control
+                    Memory32Fixed (ReadWrite, 0x3F007B00, 0x00000100, )
+                    // PWM control
+                    Memory32Fixed (ReadWrite, 0x3F20C000, 0x00000028, )
+                    // PWM control bus
+                    Memory32Fixed (ReadWrite, 0x7E20C000, 0x00000028, )
+                    // PWM control uncached
+                    Memory32Fixed (ReadWrite, 0xFF20C000, 0x00000028, )
+                    // PWM clock control
+                    Memory32Fixed (ReadWrite, 0x3F1010A0, 0x00000008, )
+                    // Interrupt DMA channel 11
+                    Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) { 0x3B }
+                    // DMA channel 11, DREQ 5 for PWM
+                    FixedDMA(5, 11, Width32Bit, )
+                })
+                Return (RBUF)
+            }
+        }
+
+        include("Uart.asl")
+        include("Rhpx.asl")
+    }
+}
diff --git a/Platform/Broadcom/Bcm283x/AcpiTables/Fadt.aslc b/Platform/Broadcom/Bcm283x/AcpiTables/Fadt.aslc
new file mode 100644
index 000000000000..493a888d3699
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/AcpiTables/Fadt.aslc
@@ -0,0 +1,50 @@
+/** @file
+ *
+ *  Fixed ACPI Description Table (FADT)
+ *
+ *  Copyright (c) 2018, Andrey Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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.
+ *
+ **/
+
+char FACP[268] = {
+    0x46, 0x41, 0x43, 0x50, 0x0C, 0x01, 0x00, 0x00, 0x05, 0x00, /*   0 */
+    0x42, 0x43, 0x32, 0x38, 0x33, 0x36, 0x45, 0x44, 0x4B, 0x32, /*  10 */
+    0x20, 0x20, 0x20, 0x20, 0x01, 0x00, 0x00, 0x00, 0x4D, 0x53, /*  20 */
+    0x46, 0x54, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*  30 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, /*  40 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*  50 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*  60 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*  70 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*  80 */
+    0x00, 0x04, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0x00, /*  90 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, /* 100 */
+    0x00, 0x00, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, /* 110 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* 120 */
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 130 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 140 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 150 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 160 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 170 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 180 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 190 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 200 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 210 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 220 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 230 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 240 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 250 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00              /* 268 */
+};
+
+void * ReferenceAcpiTable(void) {
+    return (void *) &FACP;
+}
diff --git a/Platform/Broadcom/Bcm283x/AcpiTables/Gtdt.aslc b/Platform/Broadcom/Bcm283x/AcpiTables/Gtdt.aslc
new file mode 100644
index 000000000000..764cc6c09fe7
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/AcpiTables/Gtdt.aslc
@@ -0,0 +1,31 @@
+/** @file
+ *
+ *  Generic Timer Description Table (GTDT)
+ *  Automatically generated by AutoAcpi
+ *
+ *  Copyright (c) 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.
+ *
+ **/
+
+char GTDT[80] = {
+    0x47, 0x54, 0x44, 0x54, 0x50, 0x00, 0x00, 0x00, 0x01, 0x00,
+    0x4D, 0x53, 0x46, 0x54, 0x20, 0x20, 0x45, 0x44, 0x4B, 0x32,
+    0x20, 0x20, 0x20, 0x20, 0x01, 0x00, 0x00, 0x00, 0x4D, 0x53,
+    0x46, 0x54, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x40,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+void * ReferenceAcpiTable(void) {
+    return (void *) >DT;
+}
diff --git a/Platform/Broadcom/Bcm283x/AcpiTables/Madt.aslc b/Platform/Broadcom/Bcm283x/AcpiTables/Madt.aslc
new file mode 100644
index 000000000000..96f3ee469324
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/AcpiTables/Madt.aslc
@@ -0,0 +1,60 @@
+/** @file
+ *
+ *  Multiple APIC Description Table (MADT)
+ *
+ *  Copyright (c) 2018, Andrey Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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.
+ *
+ **/
+
+/*
+ * Even though the BCM2836 doesn't contain a GIC, these
+ * GICC definitions enable multi-core support (with PSCI).
+ *
+ * Mind the signatures in the header, they must be kept.
+ */
+char APIC[] = {
+  0x41, 0x50, 0x49, 0x43, 0x6c, 0x01, 0x00, 0x00, 0x03, 0xaf, 0x42, 0x43,
+  0x32, 0x38, 0x33, 0x36, 0x45, 0x44, 0x4b, 0x32, 0x20, 0x20, 0x20, 0x20,
+  0x01, 0x00, 0x00, 0x00, 0x4d, 0x53, 0x46, 0x54, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x50, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x0b, 0x50, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x0b, 0x50, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x50, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00
+};
+
+void * ReferenceAcpiTable(void) {
+    return (void *) &APIC;
+}
diff --git a/Platform/Broadcom/Bcm283x/AcpiTables/Pep.asl b/Platform/Broadcom/Bcm283x/AcpiTables/Pep.asl
new file mode 100644
index 000000000000..c0e07c6eaf42
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/AcpiTables/Pep.asl
@@ -0,0 +1,92 @@
+/** @file
+ *
+ *  Platform Extension Plugin (PEP).
+ *
+ *  Copyright (c) 2018, Andrey Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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.
+ *
+ **/
+
+Device(PEPD)
+{
+    //
+    // RPI3 PEP virtual device.
+    //
+    Name(_HID, "BCM2854") // note: since pep on rpi3 is virtual device,
+    Name(_CID, "BCM2854") // its device id needs to be generated by Microsoft
+    Name(_UID, 0x0)
+    Name(_CRS, ResourceTemplate () {
+        // No hardware resources for PEP driver are needed.
+    })
+
+    //
+    // Processor info. PEP proprietary method to return
+    // PEP_PROCESSOR_TABLE_PLAT structure.
+    //
+    // See Pep.h and Pep.c.
+    //
+    Name(_GPI, Buffer() {
+        0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x5C,0x00,0x5F,0x00,0x53,
+        0x00,0x42,0x00,0x2E,0x00,0x43,0x00,0x50,0x00,0x55,0x00,0x30,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+    })
+
+    //
+    // Coordinated state info. PEP proprietary method to return
+    // PEP_COORDINATED_STATE_TABLE_PLAT structure.
+    //
+    // See Pep.h and Pep.c.
+    //
+    Name(_GCI, Buffer() {
+        0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
+        0x00,0x00,0x00,0x01,0x00,0x00,0x00
+    })
+
+    //
+    // Device info. PEP proprietary method to return
+    // PEP_DEVICE_TABLE_PLAT structure.
+    //
+    // See Pep.h and Pep.c.
+    //
+
+    Name(_GDI, Buffer() {
+        0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x5C,0x00,0x5F,0x00,0x53,
+        0x00,0x42,0x00,0x2E,0x00,0x49,0x00,0x32,0x00,0x43,0x00,0x30,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,
+        0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00
+    })
+}
diff --git a/Platform/Broadcom/Bcm283x/AcpiTables/Pep.c b/Platform/Broadcom/Bcm283x/AcpiTables/Pep.c
new file mode 100644
index 000000000000..de457daaa547
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/AcpiTables/Pep.c
@@ -0,0 +1,84 @@
+/** @file
+ *
+ *  PEP device tables
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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 "Pep.h"
+
+PEP_PROCESSOR_TABLE_PLAT RPI3Processors = {
+  1, //Version
+  1, //NumberProcessors
+  { //ProcessorInfo
+    { //[0]
+      L"\\_SB.CPU0", //DevicePath , wchar_t[16]
+      0, //FeedbackCounterCount
+      0x00000000, //Flags
+      0, //NumberIdleStates
+      0, //NumberPerfStates
+      { //IdleInfo
+      },
+      { // perfinfo
+      }
+    }
+  }
+};
+
+PEP_COORDINATED_STATE_TABLE_PLAT RPI3CoordinatedStates = {
+  1, //Version
+  1, //CoordinatedStateCount
+  { //CordinatedStates[]
+    { //[0]
+      { // DependencyInfo
+        { //[0]
+          1, //ExpectedState
+          0, //TargetProcessor
+          0x0 | 0x2 | 0x4, //LooseDependency = FALSE, InitialState = TRUE, DependentState = TRUE
+        }
+      },
+      SOC_STATE_TYPE, //StateType
+      0x1, //Flags
+      0, //Latency
+      0, //BreakEvenDuration
+      1, //DependencyCount
+      1, //MaximumDependencySize
+    }
+  }
+};
+
+PEP_DEVICE_TABLE_PLAT RPI3Devices = {
+  1, //Version
+  1, //NumberDevices
+  { //DeviceInfo
+    { //[1]
+      L"\\_SB.I2C0", //DevicePath , wchar_t[16]
+      0x1 | (1 << 3), //DStateSupportMask (D0 and D3)
+      1, //NumberCompoenents
+      { //DStateRequirement
+        { //[0]
+          PowerDeviceD3 //DState
+        }
+      },
+      { // FStateRequirement
+        { //[0]
+            { //FState
+              { //[0]
+                0
+              }
+            }
+          }
+      }
+    }
+  }
+};
diff --git a/Platform/Broadcom/Bcm283x/AcpiTables/Pep.h b/Platform/Broadcom/Bcm283x/AcpiTables/Pep.h
new file mode 100644
index 000000000000..16d8c460832e
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/AcpiTables/Pep.h
@@ -0,0 +1,126 @@
+/** @file
+ *
+ *  PEP device defines
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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.
+ *
+ **/
+
+/*
+ * Note: Not everything is here. At least SOC_STATE_TYPE is missing.
+ */
+
+#ifndef _RPI3PEP_H_INCLUDED_
+#define _RPI3PEP_H_INCLUDED_
+
+#include <IndustryStandard/Acpi50.h>
+
+#define PEP_MAX_DEPENDENCIES_PER_STATE 16
+#define MAX_PROCESSOR_PATH_LENGTH 16
+#define MAX_DEVICE_PATH_LENGTH 32
+#define MAX_COMPONENT_COUNT 8
+#define P_NUMBER_PROCESSORS 1
+#define P_NUMBER_IDLE_STATES 1
+#define P_NUMBER_PERF_STATES 0
+#define P_NUMBER_DEVICES 1
+#define P_NUMBER_COORDINATED_STATS 1
+
+typedef struct _PEP_PROCESSOR_IDLE_STATE_INFO {
+    UINT32 Ulong;
+    UINT32 Latency;
+    UINT32 BreakEvenDuration;
+} PEP_PROCESSOR_IDLE_STATE_INFO, *PEP_PROCESSOR_IDLE_STATE_INFO;
+
+typedef struct _PEP_PROCESSOR_IDLE_INFO_PLAT {
+  //
+  // Processor idle states.
+  //
+  PEP_PROCESSOR_IDLE_STATE_INFO IdleStates[P_NUMBER_IDLE_STATES];
+} PEP_PROCESSOR_IDLE_INFO_PLAT, *PPEP_PROCESSOR_IDLE_INFO_PLAT;
+
+typedef struct COORDINATED_DEPENDENCY_INFO {
+    UINT32 ExpectedState;
+    UINT32 TargetProcessor;
+    UINT32 Ulong;
+} COORDINATED_DEPENDENCY_INFO, *PCOORDINATED_DEPENDENCY_INFO;
+
+typedef struct {
+  COORDINATED_DEPENDENCY_INFO DependencyInfo[PEP_MAX_DEPENDENCIES_PER_STATE];
+  UINT32 StateType;
+  UINT32 Ulong;
+  UINT32 Latency;
+  UINT32 BreakEvenDuration;
+  UINT32 DependencyCount;
+  UINT32 MaximumDependencySize;
+} COORDINATED_STATE_INFO;
+
+typedef struct {
+  UINT32 Unused;
+} PEP_PROCESSOR_PERF_INFO;
+
+typedef struct {
+  UINT32 FState[MAX_COMPONENT_COUNT];
+} COORDINATED_FSTATE_REQUIREMENT;
+
+typedef struct {
+  UINT32 DState;
+} COORDINATED_DSTATE_REQUIREMENT;
+
+//
+// Top level device table
+// *N.B. The exact length of the structure is determined by the NumberIdleStates/NumberPerfStates variables.
+//
+
+typedef struct _PEP_PROCESSOR_INFO_PLAT {
+  WCHAR DevicePath[MAX_PROCESSOR_PATH_LENGTH]; // Null-terminated ACPI name
+  ULONG FeedbackCounterCount;
+  ULONG Flags;
+
+  //
+  // We are putting the idle/perf state count here (instead
+  // of the PEP_PROCESSOR_xxx_INFO structure for the ease of parsing.
+  //
+  ULONG NumberIdleStates;
+  ULONG NumberPerfStates;
+
+  PEP_PROCESSOR_IDLE_INFO_PLAT IdleInfo;
+  PEP_PROCESSOR_PERF_INFO PerfInfo;
+} PEP_PROCESSOR_INFO_PLAT, *PPEP_PROCESSOR_INFO_PLAT;
+
+typedef struct _PEP_PROCESSOR_TABLE_PLAT {
+  UINT32 Version;
+  UINT32 NumberProcessors;
+  PEP_PROCESSOR_INFO_PLAT ProcessorInfo[P_NUMBER_PROCESSORS];
+} PEP_PROCESSOR_TABLE_PLAT;
+
+typedef struct _PEP_COORDINATED_STATE_TABLE_PLAT {
+  ULONG Version;
+  ULONG CoordinatedStateCount;
+  COORDINATED_STATE_INFO CoordinatedStates[P_NUMBER_COORDINATED_STATS];
+} PEP_COORDINATED_STATE_TABLE_PLAT, *PPEP_COORDINATED_STATE_TABLE_PLAT;
+
+typedef struct _PEP_DEVICE_INFO_PLAT {
+  WCHAR DevicePath[MAX_DEVICE_PATH_LENGTH]; // Null-terminated ACPI name
+  ULONG DStateSupportMask;
+  ULONG NumberComponents;
+
+  COORDINATED_DSTATE_REQUIREMENT DStateRequirement[P_NUMBER_COORDINATED_STATS];
+  COORDINATED_FSTATE_REQUIREMENT FStateRequirement[P_NUMBER_COORDINATED_STATS];
+} PEP_DEVICE_INFO_PLAT, *PPEP_DEVICE_INFO_PLAT;
+
+typedef struct _PEP_DEVICE_TABLE_PLAT {
+  ULONG Version;
+  ULONG NumberDevices;
+  PEP_DEVICE_INFO_PLAT DeviceInfo[P_NUMBER_DEVICES];
+} PEP_DEVICE_TABLE_PLAT, *PPEP_DEVICE_TABLE_PLAT;
+
+#endif
diff --git a/Platform/Broadcom/Bcm283x/AcpiTables/Platform.h b/Platform/Broadcom/Bcm283x/AcpiTables/Platform.h
new file mode 100644
index 000000000000..826182698fd0
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/AcpiTables/Platform.h
@@ -0,0 +1,82 @@
+/** @file
+ *
+ *  RPi3 Platform specific defines for constructing ACPI tables
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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 _Platform_H_INCLUDED_
+#define _Platform_H_INCLUDED_
+
+#include <IndustryStandard/Acpi50.h>
+
+#define EFI_ACPI_OEM_ID                       {'M','C','R','S','F','T'} // OEMID 6 bytes long
+#define EFI_ACPI_OEM_TABLE_ID                 SIGNATURE_64('R','P','I','3','E','D','K','2') // OEM table id 8 bytes long
+#define EFI_ACPI_OEM_REVISION                 0x02000820
+#define EFI_ACPI_CREATOR_ID                   SIGNATURE_32('R','P','I','3')
+#define EFI_ACPI_CREATOR_REVISION             0x00000097
+
+#define EFI_ACPI_VENDOR_ID                    SIGNATURE_32('M','S','F','T')
+#define EFI_ACPI_CSRT_REVISION                0x00000005
+#define EFI_ACPI_CSRT_DEVICE_ID_DMA           0x00000009 // fixed id
+#define EFI_ACPI_CSRT_RESOURCE_ID_IN_DMA_GRP  0x0 // count up from 0
+
+#define RPI3_DMA_CHANNEL_COUNT                10 // all 10 DMA channels are listed, including the reserved ones
+#define RPI3_DMA_USED_CHANNEL_COUNT           5  // use 5 DMA channels
+
+#define EFI_ACPI_5_0_CSRT_REVISION            0x00000000
+
+typedef enum
+{
+        EFI_ACPI_CSRT_RESOURCE_TYPE_RESERVED,           // 0
+        EFI_ACPI_CSRT_RESOURCE_TYPE_INTERRUPT,          // 1
+        EFI_ACPI_CSRT_RESOURCE_TYPE_TIMER,              // 2
+        EFI_ACPI_CSRT_RESOURCE_TYPE_DMA,                // 3
+        EFI_ACPI_CSRT_RESOURCE_TYPE_CACHE,              // 4
+}
+CSRT_RESOURCE_TYPE;
+
+typedef enum
+{
+        EFI_ACPI_CSRT_RESOURCE_SUBTYPE_DMA_CHANNEL,     // 0
+        EFI_ACPI_CSRT_RESOURCE_SUBTYPE_DMA_CONTROLLER   // 1
+}
+CSRT_DMA_SUBTYPE;
+
+//------------------------------------------------------------------------
+// CSRT Resource Group header 24 bytes long
+//------------------------------------------------------------------------
+typedef struct
+{
+        UINT32 Length;                  // Length
+        UINT32 VendorID;                // 4 bytes
+        UINT32 SubVendorId;             // 4 bytes
+        UINT16 DeviceId;                // 2 bytes
+        UINT16 SubdeviceId;             // 2 bytes
+        UINT16 Revision;                // 2 bytes
+        UINT16 Reserved;                // 2 bytes
+        UINT32 SharedInfoLength;        // 4 bytes
+} EFI_ACPI_5_0_CSRT_RESOURCE_GROUP_HEADER;
+
+//------------------------------------------------------------------------
+// CSRT Resource Descriptor 12 bytes total
+//------------------------------------------------------------------------
+typedef struct
+{
+        UINT32 Length;                  // 4 bytes
+        UINT16 ResourceType;            // 2 bytes
+        UINT16 ResourceSubType;         // 2 bytes
+        UINT32 UID;                     // 4 bytes
+} EFI_ACPI_5_0_CSRT_RESOURCE_DESCRIPTOR_HEADER;
+
+#endif // of _Platform_H_INCLUDED_
diff --git a/Platform/Broadcom/Bcm283x/AcpiTables/Rhpx.asl b/Platform/Broadcom/Bcm283x/AcpiTables/Rhpx.asl
new file mode 100644
index 000000000000..b197aa53fed4
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/AcpiTables/Rhpx.asl
@@ -0,0 +1,201 @@
+/** @file
+ *
+ *  [DSDT] RHProxy device to enable WinRT API (RHPX)
+ *
+ *  Copyright (c) 2018, Andrey Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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.
+ *
+ **/
+
+Device(RHPX)
+{
+    Name(_HID, "MSFT8000")
+    Name(_CID, "MSFT8000")
+    Name(_UID, 1)
+
+    Name(_CRS, ResourceTemplate()
+    {
+        // Index 0
+        SPISerialBus(              // SCKL - GPIO 11 - Pin 23
+                                   // MOSI - GPIO 10 - Pin 19
+                                   // MISO - GPIO 9  - Pin 21
+                                   // CE0  - GPIO 8  - Pin 24
+            0,                     // Device selection (CE0)
+            PolarityLow,           // Device selection polarity
+            FourWireMode,          // wiremode
+            8,                     // databit len
+            ControllerInitiated,   // slave mode
+            4000000,               // connection speed
+            ClockPolarityLow,      // clock polarity
+            ClockPhaseFirst,       // clock phase
+            "\\_SB.SPI0",          // ResourceSource: SPI bus controller name
+            0,                     // ResourceSourceIndex
+                                   // Resource usage
+                                   // DescriptorName: creates name for offset of resource descriptor
+            )                      // Vendor Data
+
+        // Index 1
+        SPISerialBus(              // SCKL - GPIO 11 - Pin 23
+                                   // MOSI - GPIO 10 - Pin 19
+                                   // MISO - GPIO 9  - Pin 21
+                                   // CE1  - GPIO 7  - Pin 26
+            1,                     // Device selection (CE1)
+            PolarityLow,           // Device selection polarity
+            FourWireMode,          // wiremode
+            8,                     // databit len
+            ControllerInitiated,   // slave mode
+            4000000,               // connection speed
+            ClockPolarityLow,      // clock polarity
+            ClockPhaseFirst,       // clock phase
+            "\\_SB.SPI0",          // ResourceSource: SPI bus controller name
+            0,                     // ResourceSourceIndex
+                                   // Resource usage
+                                   // DescriptorName: creates name for offset of resource descriptor
+            )                      // Vendor Data
+
+        // Index 2
+        I2CSerialBus(              // Pin 3 (GPIO2, SDA1), 5 (GPIO3, SCL1)
+            0xFFFF,                // SlaveAddress: placeholder
+            ,                      // SlaveMode: default to ControllerInitiated
+            0,                     // ConnectionSpeed: placeholder
+            ,                      // Addressing Mode: default to 7 bit
+            "\\_SB.I2C1",          // ResourceSource: I2C bus controller name
+            ,
+            ,
+            ,                      // Descriptor Name: creates name for offset of resource descriptor
+            )                      // VendorData
+
+        // Index 3
+        SPISerialBus(              // SPI1_SCLK - GPIO21
+                                   // SPI1_MOSI - GPIO20
+                                   // SPI1_MISO - GPIO19
+                                   // SPI1_CE2_N - GPIO16
+            2,                     // Device selection (CE2)
+            PolarityLow,           // Device selection polarity
+            FourWireMode,          // wiremode
+            8,                     // databit len
+            ControllerInitiated,   // slave mode
+            4000000,               // connection speed
+            ClockPolarityLow,      // clock polarity
+            ClockPhaseFirst,       // clock phase
+            "\\_SB.SPI1",          // ResourceSource: SPI bus controller name
+            0,                     // ResourceSourceIndex
+                                   // Resource usage
+                                   // DescriptorName: creates name for offset of resource descriptor
+            )                      // Vendor Data
+
+        // GPIO 2
+        GpioIO(Shared, PullUp, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 2 }
+        GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",)                            { 2 }
+        // GPIO 3
+        GpioIO(Shared, PullUp, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 3 }
+        GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",)                            { 3 }
+        // GPIO 4
+        GpioIO(Shared, PullUp, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 4 }
+        GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",)                            { 4 }
+        // GPIO 5
+        GpioIO(Shared, PullUp, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 5 }
+        GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",)                            { 5 }
+        // GPIO 6
+        GpioIO(Shared, PullUp, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 6 }
+        GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",)                            { 6 }
+        // GPIO 7
+        GpioIO(Shared, PullUp, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 7 }
+        GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",)                            { 7 }
+        // GPIO 8
+        GpioIO(Shared, PullUp, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 8 }
+        GpioInt(Edge, ActiveBoth, Shared, PullUp, 0, "\\_SB.GPI0",)                            { 8 }
+        // GPIO 9
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 9 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 9 }
+        // GPIO 10
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 10 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 10 }
+        // GPIO 11
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 11 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 11 }
+        // GPIO 12
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 12 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 12 }
+        // GPIO 13
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 13 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 13 }
+        // NTRAID#MSFT-7141401-2016/04/7-jordanrh - disable UART muxing
+        // until a proper solution can be created for the dmap conflict
+        // GPIO 14 - UART TX
+        // GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 14 }
+        // GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 14 }
+        // GPIO 15 - UART RX
+        // GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 15 }
+        // GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 15 }
+        // GPIO 16
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 16 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 16 }
+        // GPIO 17
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 17 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 17 }
+        // GPIO 18
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 18 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 18 }
+        // GPIO 19
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 19 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 19 }
+        // GPIO 20
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 20 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 20 }
+        // GPIO 21
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 21 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 21 }
+        // GPIO 22
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 22 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 22 }
+        // GPIO 23
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 23 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 23 }
+        // GPIO 24
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 24 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 24 }
+        // GPIO 25
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 25 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 25 }
+        // GPIO 26
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 26 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 26 }
+        // GPIO 27
+        GpioIO(Shared, PullDown, 0, 0, IoRestrictionNone, "\\_SB.GPI0", 0, ResourceConsumer, , ) { 27 }
+        GpioInt(Edge, ActiveBoth, Shared, PullDown, 0, "\\_SB.GPI0",)                            { 27 }
+    })
+
+    Name(_DSD, Package()
+    {
+        ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+        Package()
+        {
+            // Reference http://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md
+            // SPI 0
+            Package(2) { "bus-SPI-SPI0", Package() { 0, 1 }},                       // Index 0 & 1
+            Package(2) { "SPI0-MinClockInHz", 7629 },                               // 7629 Hz
+            Package(2) { "SPI0-MaxClockInHz", 125000000 },                          // 125 MHz
+            Package(2) { "SPI0-SupportedDataBitLengths", Package() { 8 }},          // Data Bit Length
+            // I2C1
+            Package(2) { "bus-I2C-I2C1", Package() { 2 }},
+            // GPIO Pin Count and supported drive modes
+            Package (2) { "GPIO-PinCount", 54 },
+            Package (2) { "GPIO-UseDescriptorPinNumbers", 1 },
+            Package (2) { "GPIO-SupportedDriveModes", 0xf },                        // InputHighImpedance, InputPullUp, InputPullDown, OutputCmos
+            // SPI 1
+            Package(2) { "bus-SPI-SPI1", Package() { 3 }},                          // Index 3
+            Package(2) { "SPI1-MinClockInHz", 30511 },                              // 30.5 kHz
+            Package(2) { "SPI1-MaxClockInHz", 20000000 },                           // 20 MHz
+            Package(2) { "SPI1-SupportedDataBitLengths", Package() { 8 }},          // Data Bit Length
+        }
+    })
+}
diff --git a/Platform/Broadcom/Bcm283x/AcpiTables/Sdhc.asl b/Platform/Broadcom/Bcm283x/AcpiTables/Sdhc.asl
new file mode 100644
index 000000000000..a39138ce5c5c
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/AcpiTables/Sdhc.asl
@@ -0,0 +1,105 @@
+/** @file
+ *
+ *  [DSDT] SD controller/card definition (SDHC)
+ *
+ *  Copyright (c) 2018, Andrey Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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.
+ *
+ **/
+
+//
+// Note: UEFI can use either SDHost or Arasan. We expose both to the OS.
+//
+
+//
+// Description: This is ArasanSD 3.0 SD Host Controller.
+//
+
+Device (SDC1)
+{
+    Name (_HID, "BCM2847")
+    Name (_CID, "ARASAN")
+    Name (_UID, 0x0)
+    Name (_S1D, 0x1)
+    Name (_S2D, 0x1)
+    Name (_S3D, 0x1)
+    Name (_S4D, 0x1)
+    Method (_STA)
+    {
+        Return(0xf)
+    }
+    Method (_CRS, 0x0, Serialized) {
+        Name (RBUF, ResourceTemplate () {
+            MEMORY32FIXED(ReadWrite, 0x3F300000, 0x100, )
+            Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) { 0x5E }
+        })
+        Return(RBUF)
+    }
+
+    //
+    // A child device that represents the
+    // sd card, which is marked as non-removable.
+    //
+    Device (SDMM)
+    {
+        Method (_ADR)
+        {
+            Return (0)
+        }
+        Method (_RMV) // Is removable
+        {
+            Return (0) // 0 - fixed
+        }
+    }
+}
+
+
+//
+// Description: This is Broadcom SDHost 2.0 SD Host Controller
+//
+
+Device (SDC2)
+{
+    Name (_HID, "BCM2855")
+    Name (_CID, "SDHST")
+    Name (_UID, 0x0)
+    Name (_S1D, 0x1)
+    Name (_S2D, 0x1)
+    Name (_S3D, 0x1)
+    Name (_S4D, 0x1)
+    Method (_STA)
+    {
+        Return(0xf)
+    }
+    Method (_CRS, 0x0, Serialized) {
+        Name (RBUF, ResourceTemplate () {
+            MEMORY32FIXED(ReadWrite, 0x3F202000, 0x100, )
+            Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) { 0x58 }
+        })
+        Return(RBUF)
+    }
+
+    //
+    // A child device that represents the
+    // sd card, which is marked as non-removable.
+    //
+    Device (SDMM)
+    {
+        Method (_ADR)
+        {
+            Return (0)
+        }
+        Method (_RMV) // Is removable
+        {
+            Return (0) // 0 - fixed
+        }
+    }
+}
diff --git a/Platform/Broadcom/Bcm283x/AcpiTables/Spcr.asl b/Platform/Broadcom/Bcm283x/AcpiTables/Spcr.asl
new file mode 100644
index 000000000000..a6fbfd08e021
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/AcpiTables/Spcr.asl
@@ -0,0 +1,53 @@
+/** @file
+ *
+ *  Serial Port Console Redirection Table (SPCR)
+ *
+ *  Copyright (c) 2017-2018, Andrey Warkentin <andrey.warkentin at gmail.com>
+ *
+ *  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.
+ *
+ **/
+
+[000h 0000   4]                    Signature : "SPCR"    [Serial Port Console Redirection table]
+[004h 0004   4]                 Table Length : 00000050
+[008h 0008   1]                     Revision : 01
+[009h 0009   1]                     Checksum : 00
+[00Ah 0010   6]                       Oem ID : "RPiEFI"
+[010h 0016   8]                 Oem Table ID : "RPi3UEFI"
+[018h 0024   4]                 Oem Revision : 00000001
+[01Ch 0028   4]              Asl Compiler ID : "----"
+[020h 0032   4]        Asl Compiler Revision : 00000000
+
+[024h 0036   1]               Interface Type : 10
+[025h 0037   3]                     Reserved : 000000
+
+[028h 0040  12]         Serial Port Register : [Generic Address Structure]
+[028h 0040   1]                     Space ID : 00 [SystemMemory]
+[029h 0041   1]                    Bit Width : 20
+[02Ah 0042   1]                   Bit Offset : 00
+[02Bh 0043   1]         Encoded Access Width : 02 [DWord Access:32]
+[02Ch 0044   8]                      Address : 000000003f215000
+
+[034h 0052   1]               Interrupt Type : 0E
+[035h 0053   1]          PCAT-compatible IRQ : 00
+[036h 0054   4]                    Interrupt : 3D
+[03Ah 0058   1]                    Baud Rate : 07
+[03Bh 0059   1]                       Parity : 00
+[03Ch 0060   1]                    Stop Bits : 01
+[03Dh 0061   1]                 Flow Control : 00
+[03Eh 0062   1]                Terminal Type : 00
+[04Ch 0076   1]                     Reserved : 00
+[040h 0064   2]                PCI Device ID : FFFF
+[042h 0066   2]                PCI Vendor ID : FFFF
+[044h 0068   1]                      PCI Bus : 00
+[045h 0069   1]                   PCI Device : 00
+[046h 0070   1]                 PCI Function : 00
+[047h 0071   4]                    PCI Flags : 00000000
+[04Bh 0075   1]                  PCI Segment : 00
+[04Ch 0076   4]                     Reserved : 00000000
diff --git a/Platform/Broadcom/Bcm283x/AcpiTables/Uart.asl b/Platform/Broadcom/Bcm283x/AcpiTables/Uart.asl
new file mode 100644
index 000000000000..fa92cf24db91
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/AcpiTables/Uart.asl
@@ -0,0 +1,155 @@
+/** @file
+ *
+ *  [DSDT] Serial devices (UART).
+ *
+ *  Copyright (c) 2018, Andrey Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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.
+ *
+ **/
+
+//
+// Description: This is the PL011 based UART.
+//
+
+Device (URT0)
+{
+    Name (_HID, "BCM2837")
+    Name (_CID, "HID3123")
+    Name (_UID, 0x4)
+    Method (_STA)
+    {
+        Return(0xf)
+    }
+    Method (_CRS, 0x0, Serialized) {
+        Name (RBUF, ResourceTemplate () {
+            MEMORY32FIXED(ReadWrite, 0x3F201000, 0x1000, )
+            Interrupt(ResourceConsumer, Level, ActiveHigh, Exclusive) { 0x59 }
+        })
+        Return(RBUF)
+    }
+
+    Name (CLCK, 3000000)
+
+    Name (_DSD, Package () {
+        ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+            Package () {
+                Package (2) { "clock-frequency", CLCK },
+            }
+        })
+}
+
+//
+// Description: UART Mini.
+//
+// This device is referenced in the DBG2 table, which will cause the system to
+// not start the driver when the debugger is enabled and to mark the device
+// with problem code 53 (CM_PROB_USED_BY_DEBUGGER).
+//
+
+Device (URTM)
+{
+    Name (_HID, "BCM2836")
+    Name (_CID, "MINIUART")
+    Name (_UID, 0x0)
+    Method (_STA)
+    {
+        Return(0xf)
+    }
+    Method (_CRS, 0x0, Serialized) {
+        Name (RBUF, ResourceTemplate () {
+            MEMORY32FIXED(ReadWrite, 0x3F215000, 0x70, )
+            Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) {0x3D}
+
+            // NTRAID#MSFT-7141401-2016/04/7-jordanrh - disable UART muxing
+            // until a proper solution can be created for the dmap conflict.
+            // When muxing is enabled, must consider DBG2 table conflict.
+            // The alternate function resource needs to be reserved when
+            // the kernel debugger is enabled to prevent another client
+            // from muxing the pins away.
+
+            //
+            // MsftFunctionConfig is encoded as the VendorLong.
+            //
+            // MsftFunctionConfig(Exclusive, PullDown, BCM_ALT5, "\\_SB.GPI0", 0, ResourceConsumer, ) {14, 15}
+            // VendorLong  ()      // Length = 0x31
+            // {
+            //    /* 0000 */  0x00, 0x60, 0x44, 0xD5, 0xF3, 0x1F, 0x11, 0x60,  // .`D....`
+            //    /* 0008 */  0x4A, 0xB8, 0xB0, 0x9C, 0x2D, 0x23, 0x30, 0xDD,  // J...-#0.
+            //    /* 0010 */  0x2F, 0x8D, 0x1D, 0x00, 0x01, 0x10, 0x00, 0x02,  // /.......
+            //    /* 0018 */  0x02, 0x00, 0x12, 0x00, 0x00, 0x16, 0x00, 0x20,  // .......
+            //    /* 0020 */  0x00, 0x00, 0x00, 0x0E, 0x00, 0x0F, 0x00, 0x5C,  // .......\
+            //    /* 0028 */  0x5F, 0x53, 0x42, 0x2E, 0x47, 0x50, 0x49, 0x30,  // _SB.GPI0
+            //    /* 0030 */  0x00                                             // .
+            //}
+
+        })
+        Return(RBUF)
+    }
+}
+
+//
+// Multifunction serial bus device to support Bluetooth function.
+//
+
+Device(BTH0)
+{
+    Name (_HID, "BCM2EA6")
+    Name (_CID, "BCM2EA6")
+    Method (_STA)
+    {
+        Return(0xf)
+    }
+    Method (_CRS, 0x0, Serialized) {
+        Name (RBUF, ResourceTemplate () {
+            //
+            // BT UART: UART0 (PL011)
+            //
+            UARTSerialBus(
+                115200,        // InitialBaudRate: in BPS
+                ,              // BitsPerByte: default to 8 bits
+                ,              // StopBits: Defaults to one bit
+                0x00,          // LinesInUse: 8 1-bit flags to
+                               //   declare enabled control lines.
+                               //   Raspberry Pi does not exposed
+                               //   HW control signals -> not supported.
+                               //   Optional bits:
+                               //   - Bit 7 (0x80) Request To Send (RTS)
+                               //   - Bit 6 (0x40) Clear To Send (CTS)
+                               //   - Bit 5 (0x20) Data Terminal Ready (DTR)
+                               //   - Bit 4 (0x10) Data Set Ready (DSR)
+                               //   - Bit 3 (0x08) Ring Indicator (RI)
+                               //   - Bit 2 (0x04) Data Carrier Detect (DTD)
+                               //   - Bit 1 (0x02) Reserved. Must be 0.
+                               //   - Bit 0 (0x01) Reserved. Must be 0.
+                ,              // IsBigEndian:
+                               //   default to LittleEndian.
+                ,              // Parity: Defaults to no parity
+                ,              // FlowControl: Defaults to
+                               //   no flow control.
+                16,            // ReceiveBufferSize
+                16,            // TransmitBufferSize
+                "\\_SB.URT0",  // ResourceSource:
+                               //   UART bus controller name
+                ,              // ResourceSourceIndex: assumed to be 0
+                ,              // ResourceUsage: assumed to be
+                               //   ResourceConsumer
+                UAR0,          // DescriptorName: creates name
+                               //   for offset of resource descriptor
+                )              // Vendor data
+
+               //
+               // RPIQ connection for BT_ON/OFF
+               //
+               GpioIO(Shared, PullUp, 0, 0, IoRestrictionNone, "\\_SB.RPIQ", 0, ResourceConsumer, , ) { 128 }
+        })
+        Return(RBUF)
+    }
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c b/Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
new file mode 100644
index 000000000000..a070b6d5d8d9
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
@@ -0,0 +1,730 @@
+/** @file
+ *
+ *  Copyright (c) 2017, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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 "ArasanMmcHostDxe.h"
+
+#define DEBUG_MMCHOST_SD DEBUG_VERBOSE
+
+BOOLEAN PreviousIsCardPresent = FALSE;
+UINT32 LastExecutedCommand = (UINT32) -1;
+
+STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol;
+
+/**
+   These SD commands are optional, according to the SD Spec
+**/
+BOOLEAN
+IgnoreCommand(
+              UINT32 Command
+              )
+{
+  switch (Command) {
+  case MMC_CMD20:
+    return TRUE;
+  default:
+    return FALSE;
+  }
+}
+
+/**
+   Translates a generic SD command into the format used by the Arasan SD Host Controller
+**/
+UINT32
+TranslateCommand(
+  UINT32 Command,
+  UINT32 Argument
+  )
+{
+  UINT32 Translation = 0xffffffff;
+
+  if (LastExecutedCommand == CMD55) {
+    switch (Command) {
+    case MMC_CMD6:
+      Translation = ACMD6;
+      DEBUG((DEBUG_MMCHOST_SD, "ACMD6\n"));
+      break;
+    case MMC_ACMD22:
+      Translation = ACMD22;
+      DEBUG((DEBUG_MMCHOST_SD, "ACMD22\n"));
+      break;
+    case MMC_ACMD41:
+      Translation = ACMD41;
+      DEBUG((DEBUG_MMCHOST_SD, "ACMD41\n"));
+      break;
+    case MMC_ACMD51:
+      Translation = ACMD51;
+      DEBUG((DEBUG_MMCHOST_SD, "ACMD51\n"));
+      break;
+    default:
+      DEBUG((DEBUG_ERROR, "ArasanMMCHost: TranslateCommand(): Unrecognized App command: %d\n", Command));
+    }
+  } else {
+    switch (Command) {
+    case MMC_CMD0:
+      Translation = CMD0;
+      break;
+    case MMC_CMD1:
+      Translation = CMD1;
+      break;
+    case MMC_CMD2:
+      Translation = CMD2;
+      break;
+    case MMC_CMD3:
+      Translation = CMD3;
+      break;
+    case MMC_CMD5:
+      Translation = CMD5;
+      break;
+    case MMC_CMD6:
+      Translation = CMD6;
+      break;
+    case MMC_CMD7:
+      Translation = CMD7;
+      break;
+    case MMC_CMD8: {
+      if (Argument == CMD8_SD_ARG) {
+        Translation = CMD8_SD;
+        DEBUG((DEBUG_MMCHOST_SD, "Sending SD CMD8 variant\n"));
+      } else {
+        ASSERT (Argument == CMD8_MMC_ARG);
+        Translation = CMD8_MMC;
+        DEBUG((DEBUG_MMCHOST_SD, "Sending MMC CMD8 variant\n"));
+      }
+      break;
+    }
+    case MMC_CMD9:
+      Translation = CMD9;
+      break;
+    case MMC_CMD11:
+      Translation = CMD11;
+      break;
+    case MMC_CMD12:
+      Translation = CMD12;
+      break;
+    case MMC_CMD13:
+      Translation = CMD13;
+      break;
+    case MMC_CMD16:
+      Translation = CMD16;
+      break;
+    case MMC_CMD17:
+      Translation = CMD17;
+      break;
+    case MMC_CMD18:
+      Translation = CMD18;
+      break;
+    case MMC_CMD23:
+      Translation = CMD23;
+      break;
+    case MMC_CMD24:
+      Translation = CMD24;
+      break;
+    case MMC_CMD25:
+      Translation = CMD25;
+      break;
+    case MMC_CMD55:
+      Translation = CMD55;
+      break;
+    default:
+      DEBUG((DEBUG_ERROR, "ArasanMMCHost: TranslateCommand(): Unrecognized Command: %d\n", Command));
+    }
+  }
+
+  return Translation;
+}
+
+/**
+   Repeatedly polls a register until its value becomes correct, or until MAX_RETRY_COUNT polls is reached
+**/
+EFI_STATUS
+PollRegisterWithMask(
+                     IN UINTN Register,
+                     IN UINTN Mask,
+                     IN UINTN ExpectedValue
+                     )
+{
+  UINTN RetryCount = 0;
+
+  while (RetryCount < MAX_RETRY_COUNT) {
+    if ((MmioRead32(Register) & Mask) != ExpectedValue) {
+      RetryCount++;
+      gBS->Stall(STALL_AFTER_RETRY_US);
+    } else {
+      break;
+    }
+  }
+
+  if (RetryCount == MAX_RETRY_COUNT) {
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+SoftReset(
+  IN UINT32 Mask
+  )
+{
+  MmioOr32(MMCHS_SYSCTL, Mask);
+  if (PollRegisterWithMask(MMCHS_SYSCTL, Mask, 0) == EFI_TIMEOUT) {
+    DEBUG((DEBUG_ERROR, "Failed to SoftReset with mask 0x%x\n", Mask));
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+   Calculate the clock divisor
+**/
+EFI_STATUS
+CalculateClockFrequencyDivisor(
+                               IN UINTN TargetFrequency,
+                               OUT UINT32 *DivisorValue,
+                               OUT UINTN *ActualFrequency
+                               )
+{
+  EFI_STATUS Status;
+  UINT32 Divisor;
+  UINT32 BaseFrequency = 0;
+
+  Status = mFwProtocol->GetClockRate(RPI_FW_CLOCK_RATE_EMMC, &BaseFrequency);
+  if (EFI_ERROR(Status)) {
+    DEBUG((DEBUG_ERROR, "Couldn't get RPI_FW_CLOCK_RATE_EMMC\n"));
+    return Status;
+  }
+
+  ASSERT (BaseFrequency != 0);
+  Divisor = BaseFrequency / TargetFrequency;
+
+  // Arasan controller is based on 3.0 spec so the div is multiple of 2
+  // Actual Frequency = BaseFequency/(Div*2)
+  Divisor /= 2;
+
+  if ((TargetFrequency < BaseFrequency) &&
+      (TargetFrequency * 2 * Divisor != BaseFrequency)) {
+    Divisor += 1;
+  }
+
+  if (Divisor > MAX_DIVISOR_VALUE) {
+    Divisor = MAX_DIVISOR_VALUE;
+  }
+
+  DEBUG((DEBUG_MMCHOST_SD, "ArasanMMCHost: BaseFrequency 0x%x Divisor 0x%x\n", BaseFrequency, Divisor));
+
+  *DivisorValue = (Divisor & 0xFF) << 8;
+  Divisor >>= 8;
+  *DivisorValue |= (Divisor & 0x03) << 6;
+
+  if (ActualFrequency) {
+    if (Divisor == 0) {
+      *ActualFrequency = BaseFrequency;
+    } else {
+      *ActualFrequency = BaseFrequency / Divisor;
+      *ActualFrequency >>= 1;
+    }
+    DEBUG((DEBUG_MMCHOST_SD, "ArasanMMCHost: *ActualFrequency 0x%x\n", *ActualFrequency));
+  }
+
+  DEBUG((DEBUG_MMCHOST_SD, "ArasanMMCHost: *DivisorValue 0x%x\n", *DivisorValue));
+
+  return EFI_SUCCESS;
+}
+
+BOOLEAN
+MMCIsCardPresent(
+                 IN EFI_MMC_HOST_PROTOCOL *This
+                 )
+{
+  return TRUE;
+}
+
+BOOLEAN
+MMCIsReadOnly(
+              IN EFI_MMC_HOST_PROTOCOL *This
+              )
+{
+  BOOLEAN IsReadOnly = !((MmioRead32(MMCHS_PRES_STATE) & WRITE_PROTECT_OFF) == WRITE_PROTECT_OFF);
+  DEBUG((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCIsReadOnly(): %d\n", IsReadOnly));
+  return IsReadOnly;
+}
+
+EFI_STATUS
+MMCBuildDevicePath(
+                   IN EFI_MMC_HOST_PROTOCOL       *This,
+                   IN EFI_DEVICE_PATH_PROTOCOL    **DevicePath
+                   )
+{
+  EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;
+  EFI_GUID DevicePathGuid = EFI_CALLER_ID_GUID;
+
+  DEBUG((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCBuildDevicePath()\n"));
+
+  NewDevicePathNode = CreateDeviceNode(HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof(VENDOR_DEVICE_PATH));
+  CopyGuid(&((VENDOR_DEVICE_PATH*)NewDevicePathNode)->Guid, &DevicePathGuid);
+  *DevicePath = NewDevicePathNode;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+MMCSendCommand(
+               IN EFI_MMC_HOST_PROTOCOL    *This,
+               IN MMC_CMD                  MmcCmd,
+               IN UINT32                   Argument
+               )
+{
+  UINTN MmcStatus;
+  UINTN RetryCount = 0;
+  UINTN CmdSendOKMask;
+  EFI_STATUS Status = EFI_SUCCESS;
+  BOOLEAN IsAppCmd = (LastExecutedCommand == CMD55);
+  BOOLEAN IsDATCmd = FALSE;
+  BOOLEAN IsADTCCmd = FALSE;
+
+  DEBUG((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCSendCommand(MmcCmd: %08x, Argument: %08x)\n", MmcCmd, Argument));
+
+  if (IgnoreCommand(MmcCmd)) {
+    return EFI_SUCCESS;
+  }
+
+  MmcCmd = TranslateCommand(MmcCmd, Argument);
+  if (MmcCmd == 0xffffffff) {
+    return EFI_UNSUPPORTED;
+  }
+
+  if ((MmcCmd & CMD_R1_ADTC) == CMD_R1_ADTC) {
+    IsADTCCmd = TRUE;
+  }
+  if (((MmcCmd & CMD_R1B) == CMD_R1B &&
+       /*
+        * Abort commands don't get inhibited by DAT.
+        */
+       (MmcCmd & TYPE(CMD_TYPE_ABORT)) != TYPE(CMD_TYPE_ABORT)) ||
+      IsADTCCmd ||
+      /*
+       * We want to detect BRR/BWR change.
+       */
+      MmcCmd == CMD_SEND_STATUS) {
+    IsDATCmd = TRUE;
+  }
+
+  CmdSendOKMask = CMDI_MASK;
+  if (IsDATCmd) {
+    CmdSendOKMask |= DATI_MASK;
+  }
+
+  if (PollRegisterWithMask(MMCHS_PRES_STATE,
+                           CmdSendOKMask, 0) == EFI_TIMEOUT) {
+    DEBUG((DEBUG_ERROR, "%a(%u): not ready for MMC_CMD%u PresState 0x%x MmcStatus 0x%x\n",
+           __FUNCTION__, __LINE__, MMC_CMD_NUM(MmcCmd),
+           MmioRead32(MMCHS_PRES_STATE),
+           MmioRead32(MMCHS_INT_STAT)));
+    Status = EFI_TIMEOUT;
+    goto out;
+  }
+
+  if (IsAppCmd && MmcCmd == ACMD22) {
+    MmioWrite32(MMCHS_BLK, 4);
+  } else if (IsAppCmd && MmcCmd == ACMD51) {
+    MmioWrite32(MMCHS_BLK, 8);
+  } else if (!IsAppCmd && MmcCmd == CMD6) {
+    MmioWrite32(MMCHS_BLK, 64);
+  } else if (IsADTCCmd) {
+    MmioWrite32(MMCHS_BLK, BLEN_512BYTES);
+  }
+
+  // Set Data timeout counter value to max value.
+  MmioAndThenOr32(MMCHS_SYSCTL, (UINT32) ~DTO_MASK, DTO_VAL);
+
+  //
+  // Clear Interrupt Status Register, but not the Card Inserted bit
+  // to avoid messing with card detection logic.
+  //
+  MmioWrite32(MMCHS_INT_STAT, ALL_EN & ~(CARD_INS));
+
+  // Set command argument register
+  MmioWrite32(MMCHS_ARG, Argument);
+
+  // Send the command
+  MmioWrite32(MMCHS_CMD, MmcCmd);
+
+  // Check for the command status.
+  while (RetryCount < MAX_RETRY_COUNT) {
+    MmcStatus = MmioRead32(MMCHS_INT_STAT);
+
+    // Read status of command response
+    if ((MmcStatus & ERRI) != 0) {
+      //
+      // CMD5 (CMD_IO_SEND_OP_COND) is only valid for SDIO
+      // cards and thus expected to fail.
+      //
+      if (MmcCmd != CMD_IO_SEND_OP_COND) {
+        DEBUG((DEBUG_ERROR, "%a(%u): MMC_CMD%u ERRI MmcStatus 0x%x\n",
+               __FUNCTION__, __LINE__, MMC_CMD_NUM(MmcCmd), MmcStatus));
+      }
+
+      SoftReset(SRC);
+
+      Status = EFI_DEVICE_ERROR;
+      goto out;
+    }
+
+    // Check if command is completed.
+    if ((MmcStatus & CC) == CC) {
+      MmioWrite32(MMCHS_INT_STAT, CC);
+      break;
+    }
+
+    RetryCount++;
+    gBS->Stall(STALL_AFTER_RETRY_US);
+  }
+
+  gBS->Stall(STALL_AFTER_SEND_CMD_US);
+
+  if (RetryCount == MAX_RETRY_COUNT) {
+    DEBUG((DEBUG_ERROR, "%a(%u): MMC_CMD%u completion TIMEOUT PresState 0x%x MmcStatus 0x%x\n",
+           __FUNCTION__, __LINE__, MMC_CMD_NUM(MmcCmd),
+           MmioRead32(MMCHS_PRES_STATE),
+           MmcStatus));
+    Status = EFI_TIMEOUT;
+    goto out;
+  }
+
+ out:
+  if (EFI_ERROR(Status)) {
+    LastExecutedCommand = (UINT32) -1;
+  } else {
+    LastExecutedCommand = MmcCmd;
+  }
+  return Status;
+}
+
+EFI_STATUS
+MMCNotifyState(
+               IN EFI_MMC_HOST_PROTOCOL    *This,
+               IN MMC_STATE                State
+               )
+{
+  DEBUG((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCNotifyState(State: %d)\n", State));
+
+  switch (State) {
+  case MmcHwInitializationState:
+    {
+      EFI_STATUS Status;
+      UINT32 Divisor;
+
+      Status = SoftReset(SRA);
+      if (EFI_ERROR(Status)) {
+        return Status;
+      }
+
+      // Attempt to set the clock to 400Khz which is the expected initialization speed
+      Status = CalculateClockFrequencyDivisor(400000, &Divisor, NULL);
+      if (EFI_ERROR(Status)) {
+        DEBUG((DEBUG_ERROR, "ArasanMMCHost: MMCNotifyState(): Fail to initialize SD clock\n"));
+        return Status;
+      }
+
+      // Set Data Timeout Counter value, set clock frequency, enable internal clock
+      MmioOr32(MMCHS_SYSCTL, DTO_VAL | Divisor | CEN | ICS | ICE);
+
+      // Enable interrupts
+      MmioWrite32(MMCHS_IE, ALL_EN);
+    }
+    break;
+  case MmcIdleState:
+    break;
+  case MmcReadyState:
+    break;
+  case MmcIdentificationState:
+    break;
+  case MmcStandByState: {
+    EFI_STATUS Status;
+    UINTN ClockFrequency = 25000000;
+    UINT32 Divisor;
+
+    // First turn off the clock
+    MmioAnd32(MMCHS_SYSCTL, ~CEN);
+
+    Status = CalculateClockFrequencyDivisor(ClockFrequency, &Divisor, NULL);
+    if (EFI_ERROR(Status)) {
+      DEBUG((DEBUG_ERROR, "ArasanMMCHost: MmcStandByState(): Fail to initialize SD clock to %u Hz\n",
+             ClockFrequency));
+      return Status;
+    }
+
+    // Setup new divisor
+    MmioAndThenOr32(MMCHS_SYSCTL, (UINT32) ~CLKD_MASK, Divisor);
+
+    // Wait for the clock to stabilise
+    while ((MmioRead32(MMCHS_SYSCTL) & ICS_MASK) != ICS);
+
+    // Set Data Timeout Counter value, set clock frequency, enable internal clock
+    MmioOr32(MMCHS_SYSCTL, CEN);
+  }
+    break;
+  case MmcTransferState:
+    break;
+  case MmcSendingDataState:
+    break;
+  case MmcReceiveDataState:
+    break;
+  case MmcProgrammingState:
+    break;
+  case MmcDisconnectState:
+  case MmcInvalidState:
+  default:
+    DEBUG((DEBUG_ERROR, "ArasanMMCHost: MMCNotifyState(): Invalid State: %d\n", State));
+    ASSERT(0);
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+MMCReceiveResponse(
+                   IN EFI_MMC_HOST_PROTOCOL    *This,
+                   IN MMC_RESPONSE_TYPE        Type,
+                   IN UINT32*                  Buffer
+                   )
+{
+  ASSERT (Buffer != NULL);
+
+  if (Type == MMC_RESPONSE_TYPE_R2) {
+
+    // 16-byte response
+    Buffer[0] = MmioRead32(MMCHS_RSP10);
+    Buffer[1] = MmioRead32(MMCHS_RSP32);
+    Buffer[2] = MmioRead32(MMCHS_RSP54);
+    Buffer[3] = MmioRead32(MMCHS_RSP76);
+
+    Buffer[3] <<= 8;
+    Buffer[3] |= (Buffer[2] >> 24) & 0xFF;
+    Buffer[2] <<= 8;
+    Buffer[2] |= (Buffer[1] >> 24) & 0xFF;
+    Buffer[1] <<= 8;
+    Buffer[1] |= (Buffer[0] >> 24) & 0xFF;
+    Buffer[0] <<= 8;
+
+    DEBUG((
+           DEBUG_MMCHOST_SD,
+           "ArasanMMCHost: MMCReceiveResponse(Type: %x), Buffer[0-3]: %08x, %08x, %08x, %08x\n",
+           Type, Buffer[0], Buffer[1], Buffer[2], Buffer[3]));
+  } else {
+    // 4-byte response
+    Buffer[0] = MmioRead32(MMCHS_RSP10);
+    DEBUG((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCReceiveResponse(Type: %08x), Buffer[0]: %08x\n", Type, Buffer[0]));
+  }
+
+  gBS->Stall(STALL_AFTER_REC_RESP_US);
+  if (LastExecutedCommand == CMD_STOP_TRANSMISSION) {
+    DEBUG((DEBUG_MMCHOST_SD, "ArasanMMCHost: soft-resetting after CMD12\n"));
+    return SoftReset(SRC | SRD);
+  }
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+MMCReadBlockData(
+                 IN EFI_MMC_HOST_PROTOCOL    *This,
+                 IN EFI_LBA                  Lba,
+                 IN UINTN                    Length,
+                 IN UINT32*                  Buffer
+                 )
+{
+  UINTN MmcStatus;
+  UINTN RemLength;
+  UINTN Count;
+
+  DEBUG((DEBUG_VERBOSE, "%a(%u): LBA: 0x%x, Length: 0x%x, Buffer: 0x%x)\n",
+         __FUNCTION__, __LINE__, Lba, Length, Buffer));
+
+  if (Buffer == NULL) {
+    DEBUG((DEBUG_ERROR, "%a(%u): NULL Buffer\n",
+           __FUNCTION__, __LINE__));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Length % sizeof(UINT32) != 0) {
+    DEBUG((DEBUG_ERROR, "%a(%u): bad Length %u\n",
+           __FUNCTION__, __LINE__, Length));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  RemLength = Length;
+  while (RemLength != 0) {
+    UINTN RetryCount = 0;
+    UINT32 BlockLen = MIN(RemLength, BLEN_512BYTES);
+
+    while (RetryCount < MAX_RETRY_COUNT) {
+      MmcStatus = MmioRead32(MMCHS_INT_STAT);
+      if ((MmcStatus & BRR) != 0) {
+        MmioWrite32(MMCHS_INT_STAT, BRR);
+        /*
+         * Data is ready.
+         */
+        mFwProtocol->SetLed(TRUE);
+        for (Count = 0; Count < BlockLen; Count += 4, Buffer++) {
+          *Buffer = MmioRead32(MMCHS_DATA);
+        }
+
+        mFwProtocol->SetLed(FALSE);
+        break;
+      }
+
+      gBS->Stall(STALL_AFTER_RETRY_US);
+      RetryCount++;
+    }
+
+    if (RetryCount == MAX_RETRY_COUNT) {
+      DEBUG((DEBUG_ERROR, "%a(%u): %lu/%lu MMCHS_INT_STAT: %08x\n",
+             __FUNCTION__, __LINE__, Length - RemLength,
+             Length, MmcStatus));
+      return EFI_TIMEOUT;
+    }
+
+    RemLength -= BlockLen;
+    gBS->Stall(STALL_AFTER_READ_US);
+  }
+
+  MmioWrite32(MMCHS_INT_STAT, BRR);
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+MMCWriteBlockData(
+                  IN EFI_MMC_HOST_PROTOCOL    *This,
+                  IN EFI_LBA                  Lba,
+                  IN UINTN                    Length,
+                  IN UINT32*                  Buffer
+                  )
+{
+  UINTN MmcStatus;
+  UINTN RemLength;
+  UINTN Count;
+
+  DEBUG((DEBUG_VERBOSE, "%a(%u): LBA: 0x%x, Length: 0x%x, Buffer: 0x%x)\n",
+         __FUNCTION__, __LINE__, Lba, Length, Buffer));
+
+  if (Buffer == NULL) {
+    DEBUG((DEBUG_ERROR, "%a(%u): NULL Buffer\n",
+           __FUNCTION__, __LINE__));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Length % sizeof(UINT32) != 0) {
+    DEBUG((DEBUG_ERROR, "%a(%u): bad Length %u\n",
+           __FUNCTION__, __LINE__, Length));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  RemLength = Length;
+  while (RemLength != 0) {
+    UINTN RetryCount = 0;
+    UINT32 BlockLen = MIN(RemLength, BLEN_512BYTES);
+
+    while (RetryCount < MAX_RETRY_COUNT) {
+      MmcStatus = MmioRead32(MMCHS_INT_STAT);
+      if ((MmcStatus & BWR) != 0) {
+        MmioWrite32(MMCHS_INT_STAT, BWR);
+        /*
+         * Can write data.
+         */
+        mFwProtocol->SetLed(TRUE);
+        for (Count = 0; Count < BlockLen; Count += 4, Buffer++) {
+          MmioWrite32(MMCHS_DATA, *Buffer);
+        }
+
+        mFwProtocol->SetLed(FALSE);
+        break;
+      }
+
+      gBS->Stall(STALL_AFTER_RETRY_US);
+      RetryCount++;
+    }
+
+    if (RetryCount == MAX_RETRY_COUNT) {
+      DEBUG((DEBUG_ERROR, "%a(%u): %lu/%lu MMCHS_INT_STAT: %08x\n",
+             __FUNCTION__, __LINE__, Length - RemLength,
+             Length, MmcStatus));
+      return EFI_TIMEOUT;
+    }
+
+    RemLength -= BlockLen;
+    gBS->Stall(STALL_AFTER_WRITE_US);
+  }
+
+  MmioWrite32(MMCHS_INT_STAT, BWR);
+  return EFI_SUCCESS;
+}
+
+BOOLEAN
+MMCIsMultiBlock (
+  IN EFI_MMC_HOST_PROTOCOL *This
+  )
+{
+  return TRUE;
+}
+
+EFI_MMC_HOST_PROTOCOL gMMCHost =
+  {
+    MMC_HOST_PROTOCOL_REVISION,
+    MMCIsCardPresent,
+    MMCIsReadOnly,
+    MMCBuildDevicePath,
+    MMCNotifyState,
+    MMCSendCommand,
+    MMCReceiveResponse,
+    MMCReadBlockData,
+    MMCWriteBlockData,
+    NULL,
+    MMCIsMultiBlock
+  };
+
+EFI_STATUS
+MMCInitialize(
+              IN EFI_HANDLE          ImageHandle,
+              IN EFI_SYSTEM_TABLE    *SystemTable
+              )
+{
+  EFI_STATUS Status;
+  EFI_HANDLE Handle = NULL;
+
+  DEBUG((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCInitialize()\n"));
+
+  if (!PcdGet32 (PcdSdIsArasan)) {
+    DEBUG((DEBUG_INFO, "SD is not routed to Arasan\n"));
+    return EFI_REQUEST_UNLOAD_IMAGE;
+  }
+
+  Status = gBS->LocateProtocol (&gRaspberryPiFirmwareProtocolGuid, NULL,
+                                (VOID **)&mFwProtocol);
+  ASSERT_EFI_ERROR (Status);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->InstallMultipleProtocolInterfaces(
+     &Handle,
+     &gRaspberryPiMmcHostProtocolGuid, &gMMCHost,
+     NULL
+     );
+  ASSERT_EFI_ERROR(Status);
+
+  return Status;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.h b/Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.h
new file mode 100644
index 000000000000..6b4f0da4a425
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.h
@@ -0,0 +1,50 @@
+/** @file
+ *
+ *  Copyright (c) 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 _MMC_HOST_DXE_H_
+#define _MMC_HOST_DXE_H_
+
+#include <Uefi.h>
+
+#include <Library/BaseLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DmaLib.h>
+
+#include <Protocol/EmbeddedExternalDevice.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/PiMmcHost.h>
+#include <Protocol/RaspberryPiFirmware.h>
+
+#include <IndustryStandard/Bcm2836.h>
+#include <IndustryStandard/Bcm2836MmcHs.h>
+#include <IndustryStandard/RpiFirmware.h>
+
+#define MAX_RETRY_COUNT (1000 * 20)
+
+#define STALL_AFTER_SEND_CMD_US (200) // in microseconds
+#define STALL_AFTER_REC_RESP_US (50)
+#define STALL_AFTER_WRITE_US (200)
+#define STALL_AFTER_READ_US (20)
+#define STALL_AFTER_RETRY_US (20)
+
+#define MAX_DIVISOR_VALUE 1023
+
+#endif
diff --git a/Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf b/Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf
new file mode 100644
index 000000000000..4d70c1b72815
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf
@@ -0,0 +1,53 @@
+#/** @file
+#
+#  Copyright (c) 2017, Andrei Warkentin <andrey.warkentin at gmail.com>
+#  Copyright (c) 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                    = 0x00010005
+  BASE_NAME                      = ArasanMMCHost
+  FILE_GUID                      = 100c2cfa-b586-4198-9b4c-1683d195b1da
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = MMCInitialize
+
+
+[Sources.common]
+  ArasanMmcHostDxe.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+
+[LibraryClasses]
+  PcdLib
+  UefiLib
+  UefiDriverEntryPoint
+  MemoryAllocationLib
+  IoLib
+  DmaLib
+  CacheMaintenanceLib
+
+[Guids]
+
+[Protocols]
+  gRaspberryPiMmcHostProtocolGuid ## PRODUCES
+  gRaspberryPiFirmwareProtocolGuid ## CONSUMES
+
+[Pcd]
+  gRaspberryPiTokenSpaceGuid.PcdSdIsArasan
+
+[Depex]
+  gRaspberryPiFirmwareProtocolGuid AND gRaspberryPiConfigAppliedProtocolGuid
diff --git a/Platform/Broadcom/Bcm283x/Drivers/Bcm2836InterruptDxe/Bcm2836InterruptDxe.c b/Platform/Broadcom/Bcm283x/Drivers/Bcm2836InterruptDxe/Bcm2836InterruptDxe.c
new file mode 100644
index 000000000000..dda61665031d
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/Bcm2836InterruptDxe/Bcm2836InterruptDxe.c
@@ -0,0 +1,367 @@
+/** @file
+ *
+ *  Copyright (c) 2016, Linaro, Ltd. 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 <PiDxe.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include <IndustryStandard/Bcm2836.h>
+
+#include <Protocol/Cpu.h>
+#include <Protocol/HardwareInterrupt.h>
+
+//
+// This currently only implements support for the architected timer interrupts
+// on the per-CPU interrupt controllers.
+//
+#define NUM_IRQS                    (4)
+
+#ifdef MDE_CPU_AARCH64
+#define ARM_ARCH_EXCEPTION_IRQ      EXCEPT_AARCH64_IRQ
+#else
+#define ARM_ARCH_EXCEPTION_IRQ      EXCEPT_ARM_IRQ
+#endif
+
+STATIC CONST
+EFI_PHYSICAL_ADDRESS RegBase = FixedPcdGet32 (PcdInterruptBaseAddress);
+
+//
+// Notifications
+//
+STATIC EFI_EVENT                    mExitBootServicesEvent;
+STATIC HARDWARE_INTERRUPT_HANDLER   mRegisteredInterruptHandlers[NUM_IRQS];
+
+/**
+  Shutdown our hardware
+
+  DXE Core will disable interrupts and turn off the timer and disable interrupts
+  after all the event handlers have run.
+
+  @param[in]  Event   The Event that is being processed
+  @param[in]  Context Event Context
+**/
+STATIC
+VOID
+EFIAPI
+ExitBootServicesEvent (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  // Disable all interrupts
+  MmioWrite32 (RegBase + BCM2836_INTC_TIMER_CONTROL_OFFSET, 0);
+}
+
+/**
+  Enable interrupt source Source.
+
+  @param This     Instance pointer for this protocol
+  @param Source   Hardware source of the interrupt
+
+  @retval EFI_SUCCESS       Source interrupt enabled.
+  @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EnableInterruptSource (
+  IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
+  IN HARDWARE_INTERRUPT_SOURCE          Source
+  )
+{
+  if (Source >= NUM_IRQS) {
+    ASSERT(FALSE);
+    return EFI_UNSUPPORTED;
+  }
+
+  MmioOr32 (RegBase + BCM2836_INTC_TIMER_CONTROL_OFFSET, 1 << Source);
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Disable interrupt source Source.
+
+  @param This     Instance pointer for this protocol
+  @param Source   Hardware source of the interrupt
+
+  @retval EFI_SUCCESS       Source interrupt disabled.
+  @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+DisableInterruptSource (
+  IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
+  IN HARDWARE_INTERRUPT_SOURCE          Source
+  )
+{
+  if (Source >= NUM_IRQS) {
+    ASSERT(FALSE);
+    return EFI_UNSUPPORTED;
+  }
+
+  MmioAnd32 (RegBase + BCM2836_INTC_TIMER_CONTROL_OFFSET, ~(1 << Source));
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Register Handler for the specified interrupt source.
+
+  @param This     Instance pointer for this protocol
+  @param Source   Hardware source of the interrupt
+  @param Handler  Callback for interrupt. NULL to unregister
+
+  @retval EFI_SUCCESS Source was updated to support Handler.
+  @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+RegisterInterruptSource (
+  IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
+  IN HARDWARE_INTERRUPT_SOURCE          Source,
+  IN HARDWARE_INTERRUPT_HANDLER         Handler
+  )
+{
+  if (Source >= NUM_IRQS) {
+    ASSERT (FALSE);
+    return EFI_UNSUPPORTED;
+  }
+
+  if (Handler == NULL && mRegisteredInterruptHandlers[Source] == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Handler != NULL && mRegisteredInterruptHandlers[Source] != NULL) {
+    return EFI_ALREADY_STARTED;
+  }
+
+  mRegisteredInterruptHandlers[Source] = Handler;
+  return EnableInterruptSource(This, Source);
+}
+
+
+/**
+  Return current state of interrupt source Source.
+
+  @param This     Instance pointer for this protocol
+  @param Source   Hardware source of the interrupt
+  @param InterruptState  TRUE: source enabled, FALSE: source disabled.
+
+  @retval EFI_SUCCESS       InterruptState is valid
+  @retval EFI_DEVICE_ERROR  InterruptState is not valid
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GetInterruptSourceState (
+  IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
+  IN HARDWARE_INTERRUPT_SOURCE          Source,
+  IN BOOLEAN                            *InterruptState
+  )
+{
+  if (InterruptState == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Source >= NUM_IRQS) {
+    ASSERT(FALSE);
+    return EFI_UNSUPPORTED;
+  }
+
+  *InterruptState = (MmioRead32 (RegBase + BCM2836_INTC_TIMER_CONTROL_OFFSET) &
+                     (1 << Source)) != 0;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Signal to the hardware that the End Of Intrrupt state
+  has been reached.
+
+  @param This     Instance pointer for this protocol
+  @param Source   Hardware source of the interrupt
+
+  @retval EFI_SUCCESS       Source interrupt EOI'ed.
+  @retval EFI_DEVICE_ERROR  Hardware could not be programmed.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EndOfInterrupt (
+  IN EFI_HARDWARE_INTERRUPT_PROTOCOL    *This,
+  IN HARDWARE_INTERRUPT_SOURCE          Source
+  )
+{
+  return EFI_SUCCESS;
+}
+
+
+/**
+  EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
+
+  @param  InterruptType    Defines the type of interrupt or exception that
+                           occurred on the processor.This parameter is processor
+                           architecture specific.
+  @param  SystemContext    A pointer to the processor context when
+                           the interrupt occurred on the processor.
+
+  @return None
+
+**/
+STATIC
+VOID
+EFIAPI
+IrqInterruptHandler (
+  IN EFI_EXCEPTION_TYPE           InterruptType,
+  IN EFI_SYSTEM_CONTEXT           SystemContext
+  )
+{
+  HARDWARE_INTERRUPT_HANDLER  InterruptHandler;
+  HARDWARE_INTERRUPT_SOURCE   Source;
+  UINT32                      RegVal;
+
+  RegVal = MmioRead32 (RegBase + BCM2836_INTC_TIMER_PENDING_OFFSET) &
+           ((1 << NUM_IRQS) - 1);
+  Source = HighBitSet32 (RegVal);
+  if (Source < 0) {
+    return;
+  }
+
+  InterruptHandler = mRegisteredInterruptHandlers [Source];
+  if (InterruptHandler != NULL) {
+    // Call the registered interrupt handler.
+    InterruptHandler (Source, SystemContext);
+  }
+}
+
+//
+// The protocol instance produced by this driver
+//
+STATIC EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptProtocol = {
+  RegisterInterruptSource,
+  EnableInterruptSource,
+  DisableInterruptSource,
+  GetInterruptSourceState,
+  EndOfInterrupt
+};
+
+STATIC VOID *mCpuArchProtocolNotifyEventRegistration;
+
+STATIC
+VOID
+EFIAPI
+CpuArchEventProtocolNotify (
+  IN  EFI_EVENT       Event,
+  IN  VOID            *Context
+  )
+{
+  EFI_CPU_ARCH_PROTOCOL   *Cpu;
+  EFI_STATUS              Status;
+
+  //
+  // Get the CPU protocol that this driver requires.
+  //
+  Status = gBS->LocateProtocol(&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu);
+  if (EFI_ERROR (Status)) {
+    return;
+  }
+
+  //
+  // Unregister the default exception handler.
+  //
+  Status = Cpu->RegisterInterruptHandler(Cpu, ARM_ARCH_EXCEPTION_IRQ, NULL);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Cpu->RegisterInterruptHandler() - %r\n",
+      __FUNCTION__, Status));
+    ASSERT (FALSE);
+    return;
+  }
+
+  //
+  // Register to receive interrupts
+  //
+  Status = Cpu->RegisterInterruptHandler(Cpu, ARM_ARCH_EXCEPTION_IRQ,
+                                         IrqInterruptHandler);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Cpu->RegisterInterruptHandler() - %r\n",
+      __FUNCTION__, Status));
+    ASSERT (FALSE);
+    return;
+  }
+}
+
+
+/**
+  Initialize the state information for the CPU Architectural Protocol
+
+  @param  ImageHandle   of the loaded driver
+  @param  SystemTable   Pointer to the System Table
+
+  @retval EFI_SUCCESS           Protocol registered
+  @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
+  @retval EFI_DEVICE_ERROR      Hardware problems
+
+**/
+EFI_STATUS
+InterruptDxeInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_STATUS Status;
+  EFI_EVENT  CpuArchEvent;
+
+  // Make sure the Interrupt Controller Protocol is not already installed in the system.
+  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid);
+
+  Status = gBS->InstallMultipleProtocolInterfaces(
+                  &ImageHandle,
+                  &gHardwareInterruptProtocolGuid,
+                  &gHardwareInterruptProtocol,
+                  NULL);
+  ASSERT_EFI_ERROR(Status);
+
+  //
+  // Install the interrupt handler as soon as the CPU arch protocol appears.
+  //
+  CpuArchEvent = EfiCreateProtocolNotifyEvent (
+                   &gEfiCpuArchProtocolGuid,
+                   TPL_CALLBACK,
+                   CpuArchEventProtocolNotify,
+                   NULL,
+                   &mCpuArchProtocolNotifyEventRegistration
+                   );
+  ASSERT (CpuArchEvent != NULL);
+
+  // Register for an ExitBootServicesEvent
+  Status = gBS->CreateEvent(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY,
+                  ExitBootServicesEvent, NULL, &mExitBootServicesEvent);
+
+  ASSERT_EFI_ERROR(Status);
+
+  return Status;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/Bcm2836InterruptDxe/Bcm2836InterruptDxe.inf b/Platform/Broadcom/Bcm283x/Drivers/Bcm2836InterruptDxe/Bcm2836InterruptDxe.inf
new file mode 100644
index 000000000000..831ae1bfeeab
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/Bcm2836InterruptDxe/Bcm2836InterruptDxe.inf
@@ -0,0 +1,48 @@
+#/** @file
+#
+#  Copyright (c) 2017, Andrei Warkentin <andrey.warkentin at gmail.com>
+#  Copyright (c) 2016 Linaro, Ltd. 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                    = 0x00010019
+  BASE_NAME                      = Bcm2836InterruptDxe
+  FILE_GUID                      = 3944f2d7-2e09-4fc0-9e98-008375641453
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = InterruptDxeInitialize
+
+[Sources]
+  Bcm2836InterruptDxe.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  IoLib
+  UefiBootServicesTableLib
+  UefiLib
+  UefiDriverEntryPoint
+
+[Protocols]
+  gHardwareInterruptProtocolGuid  ## PRODUCES
+  gEfiCpuArchProtocolGuid         ## CONSUMES ## NOTIFY
+
+[FixedPcd]
+  gEmbeddedTokenSpaceGuid.PcdInterruptBaseAddress
+
+[Depex]
+  TRUE
diff --git a/Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxe.c b/Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxe.c
new file mode 100644
index 000000000000..4e3c68ca2994
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxe.c
@@ -0,0 +1,356 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *
+ *  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 <Library/HiiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/GpioLib.h>
+#include <Protocol/RaspberryPiFirmware.h>
+#include <IndustryStandard/RpiFirmware.h>
+#include "ConfigDxeFormSetGuid.h"
+
+extern UINT8 ConfigDxeHiiBin[];
+extern UINT8 ConfigDxeStrings[];
+
+STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol;
+
+typedef struct {
+  VENDOR_DEVICE_PATH VendorDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL End;
+} HII_VENDOR_DEVICE_PATH;
+
+STATIC HII_VENDOR_DEVICE_PATH mVendorDevicePath = {
+  {
+    {
+      HARDWARE_DEVICE_PATH,
+      HW_VENDOR_DP,
+      {
+        (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
+        (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
+      }
+    },
+    CONFIGDXE_FORM_SET_GUID
+  },
+  {
+    END_DEVICE_PATH_TYPE,
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    {
+      (UINT8) (END_DEVICE_PATH_LENGTH),
+      (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
+    }
+  }
+};
+
+
+STATIC EFI_STATUS
+InstallHiiPages (
+  VOID
+  )
+{
+  EFI_STATUS     Status;
+  EFI_HII_HANDLE HiiHandle;
+  EFI_HANDLE     DriverHandle;
+
+  DriverHandle = NULL;
+  Status = gBS->InstallMultipleProtocolInterfaces (&DriverHandle,
+                  &gEfiDevicePathProtocolGuid,
+                  &mVendorDevicePath,
+                  NULL);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  HiiHandle = HiiAddPackages (&gConfigDxeFormSetGuid,
+                              DriverHandle,
+                              ConfigDxeStrings,
+                              ConfigDxeHiiBin,
+                              NULL);
+
+  if (HiiHandle == NULL) {
+    gBS->UninstallMultipleProtocolInterfaces (DriverHandle,
+                  &gEfiDevicePathProtocolGuid,
+                  &mVendorDevicePath,
+                  NULL);
+    return EFI_OUT_OF_RESOURCES;
+  }
+  return EFI_SUCCESS;
+}
+
+
+STATIC EFI_STATUS
+SetupVariables (
+  VOID
+  )
+{
+  UINTN Size;
+  UINT32 Var32;
+  EFI_STATUS Status;
+
+  /*
+   * Create the vars with default value.
+   * If we don't, forms won't be able to update.
+   */
+
+  Size = sizeof (UINT32);
+  Status = gRT->GetVariable(L"CpuClock",
+                            &gConfigDxeFormSetGuid,
+                            NULL,  &Size, &Var32);
+  if (EFI_ERROR (Status)) {
+    PcdSet32 (PcdCpuClock, PcdGet32 (PcdCpuClock));
+  }
+
+  Size = sizeof (UINT32);
+  Status = gRT->GetVariable(L"SdIsArasan",
+                            &gConfigDxeFormSetGuid,
+                            NULL,  &Size, &Var32);
+  if (EFI_ERROR (Status)) {
+    PcdSet32 (PcdSdIsArasan, PcdGet32 (PcdSdIsArasan));
+  }
+
+  Size = sizeof (UINT32);
+  Status = gRT->GetVariable(L"MmcDisableMulti",
+                            &gConfigDxeFormSetGuid,
+                            NULL,  &Size, &Var32);
+  if (EFI_ERROR (Status)) {
+    PcdSet32 (PcdMmcDisableMulti, PcdGet32 (PcdMmcDisableMulti));
+  }
+
+  Size = sizeof (UINT32);
+  Status = gRT->GetVariable(L"MmcForce1Bit",
+                            &gConfigDxeFormSetGuid,
+                            NULL,  &Size, &Var32);
+  if (EFI_ERROR (Status)) {
+    PcdSet32 (PcdMmcForce1Bit, PcdGet32 (PcdMmcForce1Bit));
+  }
+
+  Size = sizeof (UINT32);
+  Status = gRT->GetVariable(L"MmcForceDefaultSpeed",
+                            &gConfigDxeFormSetGuid,
+                            NULL,  &Size, &Var32);
+  if (EFI_ERROR (Status)) {
+    PcdSet32 (PcdMmcForceDefaultSpeed, PcdGet32 (PcdMmcForceDefaultSpeed));
+  }
+
+  Size = sizeof (UINT32);
+  Status = gRT->GetVariable(L"MmcSdDefaultSpeedMHz",
+                            &gConfigDxeFormSetGuid,
+                            NULL,  &Size, &Var32);
+  if (EFI_ERROR (Status)) {
+    PcdSet32 (PcdMmcSdDefaultSpeedMHz, PcdGet32 (PcdMmcSdDefaultSpeedMHz));
+  }
+
+  Size = sizeof (UINT32);
+  Status = gRT->GetVariable(L"MmcSdHighSpeedMHz",
+                            &gConfigDxeFormSetGuid,
+                            NULL,  &Size, &Var32);
+  if (EFI_ERROR (Status)) {
+    PcdSet32 (PcdMmcSdHighSpeedMHz, PcdGet32 (PcdMmcSdHighSpeedMHz));
+  }
+
+  Size = sizeof (UINT32);
+  Status = gRT->GetVariable(L"DebugEnableJTAG",
+                            &gConfigDxeFormSetGuid,
+                            NULL,  &Size, &Var32);
+  if (EFI_ERROR (Status)) {
+    PcdSet32 (PcdDebugEnableJTAG, PcdGet32 (PcdDebugEnableJTAG));
+  }
+
+  Size = sizeof (UINT32);
+  Status = gRT->GetVariable(L"DebugShowUEFIExit",
+                            &gConfigDxeFormSetGuid,
+                            NULL,  &Size, &Var32);
+  if (EFI_ERROR (Status)) {
+    PcdSet32 (PcdDebugShowUEFIExit, PcdGet32 (PcdDebugShowUEFIExit));
+  }
+
+  Size = sizeof (UINT32);
+  Status = gRT->GetVariable(L"DisplayEnableVModes",
+                            &gConfigDxeFormSetGuid,
+                            NULL,  &Size, &Var32);
+  if (EFI_ERROR (Status)) {
+    PcdSet32 (PcdDisplayEnableVModes, PcdGet32 (PcdDisplayEnableVModes));
+  }
+
+  Size = sizeof (UINT32);
+  Status = gRT->GetVariable(L"DisplayEnableSShot",
+                            &gConfigDxeFormSetGuid,
+                            NULL,  &Size, &Var32);
+  if (EFI_ERROR (Status)) {
+    PcdSet32 (PcdDisplayEnableSShot, PcdGet32 (PcdDisplayEnableSShot));
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+STATIC VOID
+ApplyVariables (
+  VOID
+  )
+{
+  UINTN Gpio34Group;
+  UINTN Gpio48Group;
+  EFI_STATUS Status;
+  UINT32 CpuClock = PcdGet32 (PcdCpuClock);
+  UINT32 Rate = 0;
+
+  if (CpuClock != 0) {
+    if (CpuClock == 2) {
+      /*
+       * Maximum: 1.2GHz on RPi 3, 1.4GHz on RPi 3B+, unless
+       * overridden with arm_freq=xxx in config.txt.
+       */
+      Status = mFwProtocol->GetMaxClockRate(RPI_FW_CLOCK_RATE_ARM, &Rate);
+      if (Status != EFI_SUCCESS) {
+        DEBUG((DEBUG_ERROR, "Couldn't get the max CPU speed, leaving as is: %r\n",
+               Status));
+      }
+    } else {
+      Rate = 600 * 1000000;
+    }
+  }
+
+  if (Rate != 0) {
+    DEBUG((DEBUG_INFO, "Setting CPU speed to %uHz\n", Rate));
+    Status = mFwProtocol->SetClockRate(RPI_FW_CLOCK_RATE_ARM, Rate);
+    if (Status != EFI_SUCCESS) {
+      DEBUG((DEBUG_ERROR, "Couldn't set the CPU speed: %r\n",
+             Status));
+    }
+  }
+
+  Status = mFwProtocol->GetClockRate(RPI_FW_CLOCK_RATE_ARM, &Rate);
+  if (Status != EFI_SUCCESS) {
+    DEBUG((DEBUG_ERROR, "Couldn't get the CPU speed: %r\n",
+           Status));
+  } else {
+    DEBUG((DEBUG_INFO, "Current CPU speed is %uHz\n", Rate));
+  }
+
+  /*
+   * Switching two groups around, so disable both first.
+   *
+   * No, I've not seen a problem, but having a group be
+   * routed to two sets of pins seems like asking for trouble.
+   */
+  GpioPinFuncSet(34, GPIO_FSEL_INPUT);
+  GpioPinFuncSet(35, GPIO_FSEL_INPUT);
+  GpioPinFuncSet(36, GPIO_FSEL_INPUT);
+  GpioPinFuncSet(37, GPIO_FSEL_INPUT);
+  GpioPinFuncSet(38, GPIO_FSEL_INPUT);
+  GpioPinFuncSet(39, GPIO_FSEL_INPUT);
+  GpioPinFuncSet(48, GPIO_FSEL_INPUT);
+  GpioPinFuncSet(49, GPIO_FSEL_INPUT);
+  GpioPinFuncSet(50, GPIO_FSEL_INPUT);
+  GpioPinFuncSet(51, GPIO_FSEL_INPUT);
+  GpioPinFuncSet(52, GPIO_FSEL_INPUT);
+  GpioPinFuncSet(53, GPIO_FSEL_INPUT);
+  if (PcdGet32 (PcdSdIsArasan)) {
+    DEBUG((DEBUG_INFO, "Routing SD to Arasan\n"));
+    Gpio48Group = GPIO_FSEL_ALT3;
+    /*
+     * Route SDIO to SdHost.
+     */
+    Gpio34Group = GPIO_FSEL_ALT0;
+  } else {
+    DEBUG((DEBUG_INFO, "Routing SD to SdHost\n"));
+    Gpio48Group = GPIO_FSEL_ALT0;
+    /*
+     * Route SDIO to Arasan.
+     */
+    Gpio34Group = GPIO_FSEL_ALT3;
+  }
+  GpioPinFuncSet(34, Gpio34Group);
+  GpioPinFuncSet(35, Gpio34Group);
+  GpioPinFuncSet(36, Gpio34Group);
+  GpioPinFuncSet(37, Gpio34Group);
+  GpioPinFuncSet(38, Gpio34Group);
+  GpioPinFuncSet(39, Gpio34Group);
+  GpioPinFuncSet(48, Gpio48Group);
+  GpioPinFuncSet(49, Gpio48Group);
+  GpioPinFuncSet(50, Gpio48Group);
+  GpioPinFuncSet(51, Gpio48Group);
+  GpioPinFuncSet(52, Gpio48Group);
+  GpioPinFuncSet(53, Gpio48Group);
+
+  /*
+   * JTAG pin    JTAG sig    GPIO      Mode    Header pin
+   * 1           VREF        N/A               1
+   * 3           nTRST       GPIO22    ALT4    15
+   * 4           GND         N/A               9
+   * 5           TDI         GPIO4     ALT5    7
+   * 7           TMS         GPIO27    ALT4    13
+   * 9           TCK         GPIO25    ALT4    22
+   * 11          RTCK        GPIO23    ALT4    16
+   * 13          TDO         GPIO24    ALT4    18
+   */
+  if (PcdGet32 (PcdDebugEnableJTAG)) {
+    GpioPinFuncSet(22, GPIO_FSEL_ALT4);
+    GpioPinFuncSet(4,  GPIO_FSEL_ALT5);
+    GpioPinFuncSet(27, GPIO_FSEL_ALT4);
+    GpioPinFuncSet(25, GPIO_FSEL_ALT4);
+    GpioPinFuncSet(23, GPIO_FSEL_ALT4);
+    GpioPinFuncSet(24, GPIO_FSEL_ALT4);
+  } else {
+    GpioPinFuncSet(22, GPIO_FSEL_INPUT);
+    GpioPinFuncSet(4,  GPIO_FSEL_INPUT);
+    GpioPinFuncSet(27, GPIO_FSEL_INPUT);
+    GpioPinFuncSet(25, GPIO_FSEL_INPUT);
+    GpioPinFuncSet(23, GPIO_FSEL_INPUT);
+    GpioPinFuncSet(24, GPIO_FSEL_INPUT);
+  }
+}
+
+
+EFI_STATUS
+EFIAPI
+ConfigInitialize(
+  IN EFI_HANDLE ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  EFI_STATUS Status;
+
+  Status = gBS->LocateProtocol (&gRaspberryPiFirmwareProtocolGuid,
+                                NULL, (VOID **)&mFwProtocol);
+  ASSERT_EFI_ERROR (Status);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = SetupVariables();
+  if (Status != EFI_SUCCESS) {
+    DEBUG((DEBUG_ERROR, "Couldn't not setup NV vars: %r\n",
+           Status));
+  }
+
+  ApplyVariables();
+  Status = gBS->InstallProtocolInterface (&ImageHandle,
+                                          &gRaspberryPiConfigAppliedProtocolGuid,
+                                          EFI_NATIVE_INTERFACE,
+                                          NULL);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = InstallHiiPages();
+  if (Status != EFI_SUCCESS) {
+    DEBUG((DEBUG_ERROR, "Couldn't install ConfigDxe configuration pages: %r\n",
+           Status));
+  }
+
+  return EFI_SUCCESS;
+}
+
diff --git a/Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxe.inf b/Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxe.inf
new file mode 100644
index 000000000000..1eb429de1ae0
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxe.inf
@@ -0,0 +1,81 @@
+#/** @file
+#
+#  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+#
+#  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                    = 0x00010005
+  BASE_NAME                      = ConfigDxe
+  FILE_GUID                      = 755cbac2-b23f-4b92-bc8e-fb01ce5907b7
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = ConfigInitialize
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+#  VALID_ARCHITECTURES           = AARCH64
+#
+
+[Sources]
+  ConfigDxe.c
+  ConfigDxeHii.vfr
+  ConfigDxeHii.uni
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  ArmPlatformPkg/ArmPlatformPkg.dec
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  DxeServicesTableLib
+  PcdLib
+  UefiBootServicesTableLib
+  UefiRuntimeServicesTableLib
+  UefiDriverEntryPoint
+  HiiLib
+  GpioLib
+
+[Guids]
+  gConfigDxeFormSetGuid
+
+[Protocols]
+  gRaspberryPiFirmwareProtocolGuid ## CONSUMES
+  gRaspberryPiConfigAppliedProtocolGuid ## PRODUCES
+
+[Pcd]
+  gRaspberryPiTokenSpaceGuid.PcdHypEnable
+  gRaspberryPiTokenSpaceGuid.PcdHypLogMask
+  gRaspberryPiTokenSpaceGuid.PcdHypWindowsDebugHook
+  gRaspberryPiTokenSpaceGuid.PcdHypWin2000Mask
+  gRaspberryPiTokenSpaceGuid.PcdCpuClock
+  gRaspberryPiTokenSpaceGuid.PcdSdIsArasan
+  gRaspberryPiTokenSpaceGuid.PcdMmcForce1Bit
+  gRaspberryPiTokenSpaceGuid.PcdMmcForceDefaultSpeed
+  gRaspberryPiTokenSpaceGuid.PcdMmcSdDefaultSpeedMHz
+  gRaspberryPiTokenSpaceGuid.PcdMmcSdHighSpeedMHz
+  gRaspberryPiTokenSpaceGuid.PcdMmcDisableMulti
+  gRaspberryPiTokenSpaceGuid.PcdDebugEnableJTAG
+  gRaspberryPiTokenSpaceGuid.PcdDebugShowUEFIExit
+  gRaspberryPiTokenSpaceGuid.PcdDisplayEnableVModes
+  gRaspberryPiTokenSpaceGuid.PcdDisplayEnableSShot
+
+[FeaturePcd]
+
+[Depex]
+  gPcdProtocolGuid AND gRaspberryPiFirmwareProtocolGuid
diff --git a/Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxeFormSetGuid.h b/Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxeFormSetGuid.h
new file mode 100644
index 000000000000..b29ae3a42461
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxeFormSetGuid.h
@@ -0,0 +1,23 @@
+/** @file
+ *
+ *  Copyright (c) 2018 Andrei Warkentin <andrey.warkentin at gmail.com>
+ *
+ *  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 CONFIGDXE_FORM_SET_GUID_H
+#define CONFIGDXE_FORM_SET_GUID_H
+
+#define CONFIGDXE_FORM_SET_GUID \
+  {0xCD7CC258, 0x31DB, 0x22E6, {0x9F, 0x22, 0x63, 0xB0, 0xB8, 0xEE, 0xD6, 0xB5}}
+
+extern EFI_GUID gConfigDxeFormSetGuid;
+
+#endif /* CONFIGDXE_FORM_SET_GUID_H */
diff --git a/Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxeHii.uni b/Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxeHii.uni
new file mode 100644
index 000000000000..bf09261d5e4a
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxeHii.uni
@@ -0,0 +1,100 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *
+ *  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.
+ *
+ **/
+
+#langdef en-US  "English"
+
+#string STR_NULL_STRING          #language en-US ""
+
+#string STR_FORM_SET_TITLE       #language en-US "Raspberry Pi Configuration"
+#string STR_FORM_SET_TITLE_HELP  #language en-US "Press <Enter> to configure system settings."
+
+/*
+ * Chipset config.
+ */
+
+#string STR_CHIPSET_FORM_TITLE      #language en-US "Chipset Configuration"
+#string STR_CHIPSET_FORM_SUBTITLE   #language en-US "Note: OS may override settings when booted."
+
+#string STR_CHIPSET_CLOCK_CPU_PROMPT #language en-US "CPU Clock"
+#string STR_CHIPSET_CLOCK_CPU_HELP   #language en-US "CPU Speed"
+#string STR_CHIPSET_CLOCK_CPU_NA     #language en-US "Don't Override"
+#string STR_CHIPSET_CLOCK_CPU_600MHZ #language en-US "Min (600MHz)"
+#string STR_CHIPSET_CLOCK_CPU_MAX    #language en-US "Max"
+
+#string STR_CHIPSET_SD_PROMPT        #language en-US "uSD Routing"
+#string STR_CHIPSET_SD_HELP          #language en-US "Choose host controller to drive uSD slot"
+#string STR_CHIPSET_SD_SDHOST        #language en-US "Broadcom SDHOST"
+#string STR_CHIPSET_SD_ARASAN        #language en-US "Arasan SDHCI"
+
+/*
+ * MMC/SD configuration.
+ */
+
+#string STR_MMC_FORM_TITLE       #language en-US "SD/MMC Tweaks"
+#string STR_MMC_FORM_SUBTITLE    #language en-US "Note: UEFI only, OS will override settings when booted."
+
+#string STR_MMC_DISMULTI_PROMPT  #language en-US "Multi-Block Support"
+#string STR_MMC_DISMULTI_HELP    #language en-US "Use CMD18/CMD25 for transfers when possible"
+#string STR_MMC_DISMULTI_N       #language en-US "Multi-block transfers"
+#string STR_MMC_DISMULTI_Y       #language en-US "Single-block transfers"
+
+#string STR_MMC_FORCE1BIT_PROMPT #language en-US "uSD Max Bus Width"
+#string STR_MMC_FORCE1BIT_HELP   #language en-US "Tweak for bad media"
+#string STR_MMC_FORCE1BIT_Y      #language en-US "1 Bit Mode"
+#string STR_MMC_FORCE1BIT_N      #language en-US "4 Bit Mode"
+
+#string STR_MMC_FORCEDS_PROMPT   #language en-US "uSD Force Default Speed"
+#string STR_MMC_FORCEDS_HELP     #language en-US "Tweak for bad media"
+#string STR_MMC_FORCEDS_Y        #language en-US "Force Default Speed"
+#string STR_MMC_FORCEDS_N        #language en-US "Allow High Speed"
+
+#string STR_MMC_SD_DS_PROMPT     #language en-US "SD Default Speed (MHz)"
+#string STR_MMC_SD_DS_HELP       #language en-US "Override default 25Mhz"
+
+#string STR_MMC_SD_HS_PROMPT     #language en-US "SD High Speed (MHz)"
+#string STR_MMC_SD_HS_HELP       #language en-US "Override default 50Mhz"
+
+
+/*
+ * Display settings.
+ */
+
+#string STR_DISPLAY_FORM_TITLE      #language en-US "Display"
+#string STR_DISPLAY_FORM_SUBTITLE   #language en-US "UEFI video driver settings"
+
+#string STR_DISPLAY_VMODES_PROMPT   #language en-US "Resolutions"
+#string STR_DISPLAY_VMODES_HELP     #language en-US "Support for non-native modes"
+#string STR_DISPLAY_VMODES_ENABLE   #language en-US "Also support 640x480, 800x600, 1024x768, 720p and 1080p"
+#string STR_DISPLAY_VMODES_DISABLE  #language en-US "Only native resolution"
+
+#string STR_DISPLAY_SSHOT_PROMPT    #language en-US "Screenshot Support"
+#string STR_DISPLAY_SSHOT_HELP      #language en-US "Save screen capture as a BMP on the first writable file system found"
+#string STR_DISPLAY_SSHOT_ENABLE    #language en-US "Control-Alt-F12"
+#string STR_DISPLAY_SSHOT_DISABLE   #language en-US "Not Enabled"
+
+/*
+ * Debugging settings go here.
+ */
+#string STR_DEBUG_FORM_TITLE        #language en-US "Debugging"
+#string STR_DEBUG_FORM_SUBTITLE     #language en-US "For UEFI/OS Developers"
+
+#string STR_DEBUG_JTAG_PROMPT       #language en-US "JTAG Routing"
+#string STR_DEBUG_JTAG_HELP         #language en-US "Signals (nTRST, TDI, TMS, TCK, RTCK, TDO) -> Header pins (15, 7, 13, 22, 16, 18)"
+#string STR_DEBUG_JTAG_ENABLE       #language en-US "Enable JTAG via GPIO"
+#string STR_DEBUG_JTAG_DISABLE      #language en-US "Disable JTAG"
+
+#string STR_DEBUG_EXIT_SHOW_PROMPT  #language en-US "Verbose ExitBootServices"
+#string STR_DEBUG_EXIT_SHOW_HELP    #language en-US "Show message when UEFI hands off to OS"
+#string STR_DEBUG_EXIT_SHOW_NO      #language en-US "Do nothing"
+#string STR_DEBUG_EXIT_SHOW_YES     #language en-US "Show farewell message"
diff --git a/Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxeHii.vfr b/Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxeHii.vfr
new file mode 100644
index 000000000000..d3d98acc2edf
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxeHii.vfr
@@ -0,0 +1,306 @@
+/** @file
+ *
+ *  Copyright (c) 2018 Andrei Warkentin <andrey.warkentin at gmail.com>
+ *
+ *  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 <Guid/HiiPlatformSetupFormset.h>
+#include "ConfigDxeFormSetGuid.h"
+
+typedef struct {
+  /*
+   * 0 - One mode for the boot resolution.
+   * 1 - Adds additional "typical" resolutions like
+   *     640x480, 800x600, 1024 x 768, 720p and 1080p.
+   */
+   UINT32 Enable;
+} DISPLAY_ENABLE_VMODES_VARSTORE_DATA;
+
+typedef struct {
+  /*
+   * 0 - No screenshot support.
+   * 1 - Screenshot support via hotkey.
+   */
+   UINT32 Enable;
+} DISPLAY_ENABLE_SSHOT_VARSTORE_DATA;
+
+typedef struct {
+  /*
+   * 0 - No JTAG.
+   * 1 - JTAG mode.
+   */
+   UINT32 Enable;
+} DEBUG_ENABLE_JTAG_VARSTORE_DATA;
+
+typedef struct {
+  /*
+   * 0 - Don't show UEFI exit message.
+   * 1 - Show UEFI exit message.
+   */
+   UINT32 Show;
+} DEBUG_SHOW_UEFI_EXIT_VARSTORE_DATA;
+
+typedef struct {
+  /*
+   * 0 - don't change the clock rate.
+   * 1 - 600MHz.
+   * 2 - maximum.
+   */
+  UINT32 Clock;
+} CHIPSET_CPU_CLOCK_VARSTORE_DATA;
+
+typedef struct {
+  /*
+   * 0 - uSD slot routed to Broadcom SDHOST.
+   * 1 - uSD slot routed to Arasan SDHCI.
+   */
+  UINT32 Routing;
+} CHIPSET_SD_VARSTORE_DATA;
+
+typedef struct {
+  /*
+   * 0 - Don't disable multi-block.
+   * 1 - Disable multi-block commands.
+   */
+  UINT32 DisableMulti;
+} MMC_DISMULTI_VARSTORE_DATA;
+
+typedef struct {
+  /*
+   * 0 - Don't force 1 bit mode.
+   * 1 - Force 1 bit mode.
+   */
+  UINT32 Force1Bit;
+} MMC_FORCE1BIT_VARSTORE_DATA;
+
+typedef struct {
+  /*
+   * 0 - Don't force default speed.
+   * 1 - Force default speed.
+   */
+  UINT32 ForceDS;
+} MMC_FORCEDS_VARSTORE_DATA;
+
+typedef struct {
+  /*
+   * Default Speed MHz override (25MHz default).
+   */
+  UINT32 MHz;
+} MMC_SD_DS_MHZ_VARSTORE_DATA;
+
+typedef struct {
+  /*
+   * High Speed MHz override (50MHz default).
+   */
+  UINT32 MHz;
+} MMC_SD_HS_MHZ_VARSTORE_DATA;
+
+//
+// EFI Variable attributes
+//
+#define EFI_VARIABLE_NON_VOLATILE       0x00000001
+#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002
+#define EFI_VARIABLE_RUNTIME_ACCESS     0x00000004
+#define EFI_VARIABLE_READ_ONLY          0x00000008
+
+formset
+    guid      = CONFIGDXE_FORM_SET_GUID,
+    title     = STRING_TOKEN(STR_FORM_SET_TITLE),
+    help      = STRING_TOKEN(STR_FORM_SET_TITLE_HELP),
+    classguid = EFI_HII_PLATFORM_SETUP_FORMSET_GUID,
+
+    efivarstore CHIPSET_CPU_CLOCK_VARSTORE_DATA,
+      attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+      name  = CpuClock,
+      guid  = CONFIGDXE_FORM_SET_GUID;
+
+    efivarstore CHIPSET_SD_VARSTORE_DATA,
+      attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+      name  = SdIsArasan,
+      guid  = CONFIGDXE_FORM_SET_GUID;
+
+    efivarstore MMC_DISMULTI_VARSTORE_DATA,
+      attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+      name  = MmcDisableMulti,
+      guid  = CONFIGDXE_FORM_SET_GUID;
+
+    efivarstore MMC_FORCE1BIT_VARSTORE_DATA,
+      attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+      name  = MmcForce1Bit,
+      guid  = CONFIGDXE_FORM_SET_GUID;
+
+    efivarstore MMC_FORCEDS_VARSTORE_DATA,
+      attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+      name  = MmcForceDefaultSpeed,
+      guid  = CONFIGDXE_FORM_SET_GUID;
+
+    efivarstore MMC_SD_DS_MHZ_VARSTORE_DATA,
+      attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+      name  = MmcSdDefaultSpeedMHz,
+      guid  = CONFIGDXE_FORM_SET_GUID;
+
+    efivarstore MMC_SD_HS_MHZ_VARSTORE_DATA,
+      attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+      name  = MmcSdHighSpeedMHz,
+      guid  = CONFIGDXE_FORM_SET_GUID;
+
+    efivarstore DEBUG_ENABLE_JTAG_VARSTORE_DATA,
+      attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+      name  = DebugEnableJTAG,
+      guid  = CONFIGDXE_FORM_SET_GUID;
+
+    efivarstore DEBUG_SHOW_UEFI_EXIT_VARSTORE_DATA,
+      attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+      name  = DebugShowUEFIExit,
+      guid  = CONFIGDXE_FORM_SET_GUID;
+
+    efivarstore DISPLAY_ENABLE_VMODES_VARSTORE_DATA,
+      attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+      name  = DisplayEnableVModes,
+      guid  = CONFIGDXE_FORM_SET_GUID;
+
+    efivarstore DISPLAY_ENABLE_SSHOT_VARSTORE_DATA,
+      attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+      name  = DisplayEnableSShot,
+      guid  = CONFIGDXE_FORM_SET_GUID;
+
+    form formid = 1,
+        title  = STRING_TOKEN(STR_FORM_SET_TITLE);
+        subtitle text = STRING_TOKEN(STR_NULL_STRING);
+
+        goto 0x1002,
+           prompt = STRING_TOKEN(STR_CHIPSET_FORM_TITLE),
+           help = STRING_TOKEN(STR_NULL_STRING);
+
+        goto 0x1003,
+           prompt = STRING_TOKEN(STR_MMC_FORM_TITLE),
+           help = STRING_TOKEN(STR_NULL_STRING);
+
+        goto 0x1004,
+            prompt = STRING_TOKEN(STR_DISPLAY_FORM_TITLE),
+            help = STRING_TOKEN(STR_NULL_STRING);
+
+        goto 0x1005,
+            prompt = STRING_TOKEN(STR_DEBUG_FORM_TITLE),
+            help = STRING_TOKEN(STR_NULL_STRING);
+    endform;
+
+    form formid = 0x1002,
+        title  = STRING_TOKEN(STR_CHIPSET_FORM_TITLE);
+        subtitle text = STRING_TOKEN(STR_CHIPSET_FORM_SUBTITLE);
+
+        oneof varid = CpuClock.Clock,
+            prompt      = STRING_TOKEN(STR_CHIPSET_CLOCK_CPU_PROMPT),
+            help        = STRING_TOKEN(STR_CHIPSET_CLOCK_CPU_HELP),
+            flags       = NUMERIC_SIZE_4 | INTERACTIVE | RESET_REQUIRED,
+            option text = STRING_TOKEN(STR_CHIPSET_CLOCK_CPU_NA), value = 0, flags = DEFAULT;
+            option text = STRING_TOKEN(STR_CHIPSET_CLOCK_CPU_600MHZ), value = 1, flags = 0;
+            option text = STRING_TOKEN(STR_CHIPSET_CLOCK_CPU_MAX), value = 2, flags = 0;
+        endoneof;
+
+        oneof varid = SdIsArasan.Routing,
+            prompt      = STRING_TOKEN(STR_CHIPSET_SD_PROMPT),
+            help        = STRING_TOKEN(STR_CHIPSET_SD_HELP),
+            flags       = NUMERIC_SIZE_4 | INTERACTIVE | RESET_REQUIRED,
+            option text = STRING_TOKEN(STR_CHIPSET_SD_ARASAN), value = 1, flags = 0;
+            option text = STRING_TOKEN(STR_CHIPSET_SD_SDHOST), value = 0, flags = DEFAULT;
+        endoneof;
+    endform;
+
+    form formid = 0x1003,
+        title  = STRING_TOKEN(STR_MMC_FORM_TITLE);
+        subtitle text = STRING_TOKEN(STR_MMC_FORM_SUBTITLE);
+
+        oneof varid = MmcDisableMulti.DisableMulti,
+            prompt      = STRING_TOKEN(STR_MMC_DISMULTI_PROMPT),
+            help        = STRING_TOKEN(STR_MMC_DISMULTI_HELP),
+            flags       = NUMERIC_SIZE_4 | INTERACTIVE | RESET_REQUIRED,
+            option text = STRING_TOKEN(STR_MMC_DISMULTI_N), value = 0, flags = DEFAULT;
+            option text = STRING_TOKEN(STR_MMC_DISMULTI_Y), value = 1, flags = 0;
+        endoneof;
+
+        oneof varid = MmcForce1Bit.Force1Bit,
+            prompt      = STRING_TOKEN(STR_MMC_FORCE1BIT_PROMPT),
+            help        = STRING_TOKEN(STR_MMC_FORCE1BIT_HELP),
+            flags       = NUMERIC_SIZE_4 | INTERACTIVE | RESET_REQUIRED,
+            option text = STRING_TOKEN(STR_MMC_FORCE1BIT_N), value = 0, flags = DEFAULT;
+            option text = STRING_TOKEN(STR_MMC_FORCE1BIT_Y), value = 1, flags = 0;
+        endoneof;
+
+        oneof varid = MmcForceDefaultSpeed.ForceDS,
+            prompt      = STRING_TOKEN(STR_MMC_FORCEDS_PROMPT),
+            help        = STRING_TOKEN(STR_MMC_FORCEDS_HELP),
+            flags       = NUMERIC_SIZE_4 | INTERACTIVE | RESET_REQUIRED,
+            option text = STRING_TOKEN(STR_MMC_FORCEDS_N), value = 0, flags = DEFAULT;
+            option text = STRING_TOKEN(STR_MMC_FORCEDS_Y), value = 1, flags = 0;
+        endoneof;
+
+        numeric varid = MmcSdDefaultSpeedMHz.MHz,
+             prompt  = STRING_TOKEN(STR_MMC_SD_DS_PROMPT),
+             help    = STRING_TOKEN(STR_MMC_SD_DS_HELP),
+             flags   = DISPLAY_UINT_DEC | NUMERIC_SIZE_4 | INTERACTIVE | RESET_REQUIRED,
+             minimum = 25,
+             maximum = 100,
+             default = 25,
+        endnumeric;
+
+        numeric varid = MmcSdHighSpeedMHz.MHz,
+             prompt  = STRING_TOKEN(STR_MMC_SD_HS_PROMPT),
+             help    = STRING_TOKEN(STR_MMC_SD_HS_HELP),
+             flags   = DISPLAY_UINT_DEC | NUMERIC_SIZE_4 | INTERACTIVE | RESET_REQUIRED,
+             minimum = 50,
+             maximum = 100,
+             default = 50,
+        endnumeric;
+    endform;
+
+    form formid = 0x1004,
+        title  = STRING_TOKEN(STR_DISPLAY_FORM_TITLE);
+        subtitle text = STRING_TOKEN(STR_DISPLAY_FORM_SUBTITLE);
+
+        oneof varid = DisplayEnableVModes.Enable,
+            prompt      = STRING_TOKEN(STR_DISPLAY_VMODES_PROMPT),
+            help        = STRING_TOKEN(STR_DISPLAY_VMODES_HELP),
+            flags       = NUMERIC_SIZE_4 | INTERACTIVE | RESET_REQUIRED,
+            option text = STRING_TOKEN(STR_DISPLAY_VMODES_ENABLE), value = 1, flags = DEFAULT;
+            option text = STRING_TOKEN(STR_DISPLAY_VMODES_DISABLE), value = 0, flags = 0;
+        endoneof;
+
+        oneof varid = DisplayEnableSShot.Enable,
+            prompt      = STRING_TOKEN(STR_DISPLAY_SSHOT_PROMPT),
+            help        = STRING_TOKEN(STR_DISPLAY_SSHOT_HELP),
+            flags       = NUMERIC_SIZE_4 | INTERACTIVE | RESET_REQUIRED,
+            option text = STRING_TOKEN(STR_DISPLAY_SSHOT_ENABLE), value = 1, flags = DEFAULT;
+            option text = STRING_TOKEN(STR_DISPLAY_SSHOT_DISABLE), value = 0, flags = 0;
+        endoneof;
+    endform;
+
+    form formid = 0x1005,
+        title  = STRING_TOKEN(STR_DEBUG_FORM_TITLE);
+        subtitle text = STRING_TOKEN(STR_DEBUG_FORM_SUBTITLE);
+
+        oneof varid = DebugEnableJTAG.Enable,
+            prompt      = STRING_TOKEN(STR_DEBUG_JTAG_PROMPT),
+            help        = STRING_TOKEN(STR_DEBUG_JTAG_HELP),
+            flags       = NUMERIC_SIZE_4 | INTERACTIVE | RESET_REQUIRED,
+            option text = STRING_TOKEN(STR_DEBUG_JTAG_ENABLE), value = 1, flags = 0;
+            option text = STRING_TOKEN(STR_DEBUG_JTAG_DISABLE), value = 0, flags = DEFAULT;
+        endoneof;
+
+        oneof varid = DebugShowUEFIExit.Show,
+            prompt      = STRING_TOKEN(STR_DEBUG_EXIT_SHOW_PROMPT),
+            help        = STRING_TOKEN(STR_DEBUG_EXIT_SHOW_HELP),
+            flags       = NUMERIC_SIZE_4 | INTERACTIVE,
+            option text = STRING_TOKEN(STR_DEBUG_EXIT_SHOW_NO), value = 0, flags = DEFAULT;
+            option text = STRING_TOKEN(STR_DEBUG_EXIT_SHOW_YES), value = 1, flags = 0;
+        endoneof;
+    endform;
+endformset;
diff --git a/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/ComponentName.c b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/ComponentName.c
new file mode 100644
index 000000000000..e639826c60b1
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/ComponentName.c
@@ -0,0 +1,222 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2006-2016, Intel 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 "DisplayDxe.h"
+
+STATIC
+EFI_STATUS
+EFIAPI
+ComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+);
+
+STATIC
+EFI_STATUS
+EFIAPI
+ComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,
+  IN  EFI_HANDLE                  ControllerHandle,
+  IN  EFI_HANDLE                  ChildHandle,
+  IN  CHAR8                       *Language,
+  OUT CHAR16                      **ControllerName
+  );
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gComponentName = {
+  ComponentNameGetDriverName,
+  ComponentNameGetControllerName,
+  "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) ComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) ComponentNameGetControllerName,
+  "en"
+};
+
+
+STATIC EFI_UNICODE_STRING_TABLE mDriverName[] = {
+  {
+    "eng;en",
+    (CHAR16 *)L"Raspberry Pi Display Driver"
+  },
+  {
+    NULL,
+    NULL
+  }
+};
+
+STATIC EFI_UNICODE_STRING_TABLE mDeviceName[] = {
+  {
+    "eng;en",
+    (CHAR16 *)L"Raspberry Pi Framebuffer"
+  },
+  {
+    NULL,
+    NULL
+  }
+};
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mDriverName,
+           DriverName,
+           (BOOLEAN)(This == &gComponentName)
+           );
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,
+  IN  EFI_HANDLE                  ControllerHandle,
+  IN  EFI_HANDLE                  ChildHandle,
+  IN  CHAR8                       *Language,
+  OUT CHAR16                      **ControllerName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mDeviceName,
+           ControllerName,
+           (BOOLEAN)(This == &gComponentName)
+           );
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.c b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.c
new file mode 100644
index 000000000000..e8b7f67c8870
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.c
@@ -0,0 +1,606 @@
+/** @file
+ *
+ *  Copyright (c) 2017-2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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 "DisplayDxe.h"
+
+#define POS_TO_FB(posX, posY) ((UINT8 *)                                \
+                               ((UINTN)This->Mode->FrameBufferBase +    \
+                                (posY) * This->Mode->Info->PixelsPerScanLine * \
+                                PI3_BYTES_PER_PIXEL +                   \
+                                (posX) * PI3_BYTES_PER_PIXEL))
+
+STATIC
+EFI_STATUS
+EFIAPI
+DriverSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
+  );
+
+STATIC
+EFI_STATUS
+EFIAPI
+DriverStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
+  );
+
+STATIC
+EFI_STATUS
+EFIAPI
+DriverStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  Controller,
+  IN UINTN                       NumberOfChildren,
+  IN EFI_HANDLE                  *ChildHandleBuffer
+  );
+
+STATIC
+EFI_STATUS
+EFIAPI
+DisplayQueryMode(
+                 IN  EFI_GRAPHICS_OUTPUT_PROTOCOL          *This,
+                 IN  UINT32                                ModeNumber,
+                 OUT UINTN                                 *SizeOfInfo,
+                 OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION  **Info
+                 );
+
+STATIC
+EFI_STATUS
+EFIAPI
+DisplaySetMode(
+               IN  EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
+               IN  UINT32                       ModeNumber
+               );
+
+STATIC
+EFI_STATUS
+EFIAPI
+DisplayBlt(
+           IN  EFI_GRAPHICS_OUTPUT_PROTOCOL            *This,
+           IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL           *BltBuffer,   OPTIONAL
+           IN  EFI_GRAPHICS_OUTPUT_BLT_OPERATION       BltOperation,
+           IN  UINTN                                   SourceX,
+           IN  UINTN                                   SourceY,
+           IN  UINTN                                   DestinationX,
+           IN  UINTN                                   DestinationY,
+           IN  UINTN                                   Width,
+           IN  UINTN                                   Height,
+           IN  UINTN                                   Delta         OPTIONAL
+           );
+
+STATIC EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = {
+  DriverSupported,
+  DriverStart,
+  DriverStop,
+  0xa,
+  NULL,
+  NULL
+};
+
+typedef struct {
+  VENDOR_DEVICE_PATH DisplayDevicePath;
+  EFI_DEVICE_PATH EndDevicePath;
+} DISPLAY_DEVICE_PATH;
+
+typedef struct {
+  UINT32 Width;
+  UINT32 Height;
+} GOP_MODE_DATA;
+
+STATIC UINT32 mBootWidth;
+STATIC UINT32 mBootHeight;
+STATIC EFI_HANDLE mDevice;
+STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol;
+STATIC EFI_CPU_ARCH_PROTOCOL *mCpu;
+
+STATIC UINTN mLastMode;
+STATIC GOP_MODE_DATA mGopModeData[] = {
+  { 800,  600  }, /* Legacy */
+  { 640,  480  }, /* Legacy */
+  { 1024, 768  }, /* Legacy */
+  { 1280, 720  }, /* 720p */
+  { 1920, 1080 }, /* 1080p */
+  { 0,    0    }, /* Physical */
+};
+
+STATIC DISPLAY_DEVICE_PATH mDisplayProtoDevicePath =
+  {
+    {
+      {
+        HARDWARE_DEVICE_PATH,
+        HW_VENDOR_DP,
+        {
+          (UINT8)(sizeof(VENDOR_DEVICE_PATH)),
+          (UINT8)((sizeof(VENDOR_DEVICE_PATH)) >> 8),
+        }
+      },
+      EFI_CALLER_ID_GUID,
+    },
+    {
+      END_DEVICE_PATH_TYPE,
+      END_ENTIRE_DEVICE_PATH_SUBTYPE,
+      {
+        sizeof(EFI_DEVICE_PATH_PROTOCOL),
+        0
+      }
+    }
+  };
+
+#define PI3_BITS_PER_PIXEL              (32)
+#define PI3_BYTES_PER_PIXEL             (PI3_BITS_PER_PIXEL / 8)
+
+EFI_GRAPHICS_OUTPUT_PROTOCOL gDisplayProto = {
+  DisplayQueryMode,
+  DisplaySetMode,
+  DisplayBlt,
+  NULL
+};
+
+STATIC
+EFI_STATUS
+EFIAPI
+DisplayQueryMode(
+                 IN  EFI_GRAPHICS_OUTPUT_PROTOCOL          *This,
+                 IN  UINT32                                ModeNumber,
+                 OUT UINTN                                 *SizeOfInfo,
+                 OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION  **Info
+                 )
+{
+  EFI_STATUS Status;
+  GOP_MODE_DATA *Mode;
+
+  if (ModeNumber > mLastMode) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = gBS->AllocatePool(
+                             EfiBootServicesData,
+                             sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION),
+                             (VOID **)Info
+                             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Mode = &mGopModeData[ModeNumber];
+
+  *SizeOfInfo = sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
+  (*Info)->Version = This->Mode->Info->Version;
+  (*Info)->HorizontalResolution = Mode->Width;
+  (*Info)->VerticalResolution = Mode->Height;
+  (*Info)->PixelFormat = This->Mode->Info->PixelFormat;
+  (*Info)->PixelsPerScanLine = Mode->Width;
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+VOID
+ClearScreen(
+  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL *This
+  )
+{
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL Fill;
+
+  Fill.Red                      = 0x00;
+  Fill.Green                    = 0x00;
+  Fill.Blue                     = 0x00;
+  This->Blt (This, &Fill, EfiBltVideoFill,
+             0, 0, 0, 0, This->Mode->Info->HorizontalResolution,
+             This->Mode->Info->VerticalResolution,
+             This->Mode->Info->HorizontalResolution *
+             sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+DisplaySetMode(
+  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
+  IN  UINT32                       ModeNumber
+  )
+{
+  UINTN FbSize;
+  UINTN FbPitch;
+  EFI_STATUS Status;
+  EFI_PHYSICAL_ADDRESS FbBase;
+  GOP_MODE_DATA *Mode = &mGopModeData[ModeNumber];
+
+  if (ModeNumber > mLastMode) {
+    return EFI_UNSUPPORTED;
+  }
+
+  DEBUG((DEBUG_INFO, "Setting mode %u from %u: %u x %u\n",
+         ModeNumber, This->Mode->Mode, Mode->Width, Mode->Height));
+  Status = mFwProtocol->GetFB(Mode->Width, Mode->Height,
+                              PI3_BITS_PER_PIXEL, &FbBase,
+                              &FbSize, &FbPitch);
+  if (EFI_ERROR(Status)) {
+    DEBUG((DEBUG_ERROR, "Could not set mode %u\n", ModeNumber));
+    return EFI_DEVICE_ERROR;
+  }
+
+  DEBUG((DEBUG_INFO, "Mode %u: %u x %u framebuffer is %u bytes at %p\n",
+         ModeNumber, Mode->Width, Mode->Height, FbSize, FbBase));
+
+  if (FbPitch / PI3_BYTES_PER_PIXEL != Mode->Width) {
+    DEBUG((DEBUG_ERROR, "Error: Expected width %u, got width %u\n",
+           Mode->Width, FbPitch / PI3_BYTES_PER_PIXEL));
+    return EFI_DEVICE_ERROR;
+  }
+
+  /*
+   * WT, because certain OS loaders access the frame buffer directly
+   * and we don't want to see corruption due to missing WB cache
+   * maintenance. Performance with WT is good.
+   */
+  Status = mCpu->SetMemoryAttributes(mCpu, FbBase,
+                                     ALIGN_VALUE(FbSize, EFI_PAGE_SIZE),
+                                     EFI_MEMORY_WT);
+  if (Status != EFI_SUCCESS) {
+    DEBUG((DEBUG_ERROR, "Couldn't set framebuffer attributes: %r\n", Status));
+    return Status;
+  }
+
+  This->Mode->Mode = ModeNumber;
+  This->Mode->Info->Version = 0;
+  This->Mode->Info->HorizontalResolution = Mode->Width;
+  This->Mode->Info->VerticalResolution = Mode->Height;
+  /*
+   * NOTE: Windows REQUIRES BGR in 32 or 24 bit format.
+   */
+  This->Mode->Info->PixelFormat = PixelBlueGreenRedReserved8BitPerColor;
+  This->Mode->Info->PixelsPerScanLine = Mode->Width;
+  This->Mode->SizeOfInfo = sizeof(*This->Mode->Info);
+  This->Mode->FrameBufferBase = FbBase;
+  This->Mode->FrameBufferSize = FbSize;
+
+  ClearScreen(This);
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+DisplayBlt(
+           IN  EFI_GRAPHICS_OUTPUT_PROTOCOL      *This,
+           IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL     *BltBuffer,   OPTIONAL
+           IN  EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
+           IN  UINTN                             SourceX,
+           IN  UINTN                             SourceY,
+           IN  UINTN                             DestinationX,
+           IN  UINTN                             DestinationY,
+           IN  UINTN                             Width,
+           IN  UINTN                             Height,
+           IN  UINTN                             Delta         OPTIONAL
+           )
+{
+  UINT8 *VidBuf, *BltBuf, *VidBuf1;
+  UINTN i;
+
+  switch(BltOperation) {
+  case EfiBltVideoFill:
+    BltBuf = (UINT8 *)BltBuffer;
+
+    for (i = 0; i < Height; i++) {
+      VidBuf = POS_TO_FB(DestinationX, DestinationY + i);
+
+      SetMem32(VidBuf, Width * PI3_BYTES_PER_PIXEL, *(UINT32 *) BltBuf);
+    }
+    break;
+
+  case EfiBltVideoToBltBuffer:
+    if (Delta == 0) {
+      Delta = Width * PI3_BYTES_PER_PIXEL;
+    }
+
+    for (i = 0; i < Height; i++) {
+      VidBuf = POS_TO_FB(SourceX, SourceY + i);
+
+      BltBuf = (UINT8 *)((UINTN)BltBuffer + (DestinationY + i) * Delta +
+                         DestinationX * PI3_BYTES_PER_PIXEL);
+
+      gBS->CopyMem((VOID *)BltBuf, (VOID *)VidBuf, PI3_BYTES_PER_PIXEL * Width);
+    }
+    break;
+
+  case EfiBltBufferToVideo:
+    if (Delta == 0) {
+      Delta = Width * PI3_BYTES_PER_PIXEL;
+    }
+
+    for (i = 0; i < Height; i++) {
+      VidBuf = POS_TO_FB(DestinationX, DestinationY + i);
+      BltBuf = (UINT8 *)((UINTN) BltBuffer + (SourceY + i) * Delta +
+                         SourceX * PI3_BYTES_PER_PIXEL);
+
+      gBS->CopyMem((VOID *)VidBuf, (VOID *)BltBuf, Width * PI3_BYTES_PER_PIXEL);
+    }
+    break;
+
+  case EfiBltVideoToVideo:
+    for (i = 0; i < Height; i++) {
+      VidBuf = POS_TO_FB(SourceX, SourceY + i);
+      VidBuf1 = POS_TO_FB(DestinationX, DestinationY + i);
+
+      gBS->CopyMem((VOID *)VidBuf1, (VOID *)VidBuf, Width * PI3_BYTES_PER_PIXEL);
+    }
+    break;
+
+  default:
+    ASSERT_EFI_ERROR(EFI_SUCCESS);
+    break;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+   Initialize the state information for the Display Dxe
+
+   @param  ImageHandle   of the loaded driver
+   @param  SystemTable   Pointer to the System Table
+
+   @retval EFI_SUCCESS           Protocol registered
+   @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
+   @retval EFI_DEVICE_ERROR      Hardware problems
+
+**/
+EFI_STATUS
+EFIAPI
+DisplayDxeInitialize (
+                      IN EFI_HANDLE         ImageHandle,
+                      IN EFI_SYSTEM_TABLE   *SystemTable
+                      )
+{
+  EFI_STATUS Status;
+
+  Status = gBS->LocateProtocol (&gRaspberryPiFirmwareProtocolGuid, NULL,
+                                (VOID **)&mFwProtocol);
+  ASSERT_EFI_ERROR (Status);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL,
+                                (VOID **) &mCpu);
+  ASSERT_EFI_ERROR (Status);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Query the current display resolution from mailbox
+  Status = mFwProtocol->GetFBSize(&mBootWidth, &mBootHeight);
+  if(EFI_ERROR(Status)) {
+    return Status;
+  }
+
+  DEBUG((DEBUG_INFO, "Display boot mode is %u x %u\n",
+         mBootWidth, mBootHeight));
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+    &mDevice, &gEfiDevicePathProtocolGuid,
+    &mDisplayProtoDevicePath, &gEfiCallerIdGuid,
+    NULL, NULL);
+  ASSERT_EFI_ERROR (Status);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = EfiLibInstallDriverBindingComponentName2 (
+             ImageHandle,
+             SystemTable,
+             &mDriverBinding,
+             ImageHandle,
+             &gComponentName,
+             &gComponentName2
+             );
+  ASSERT_EFI_ERROR (Status);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+DriverSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
+  )
+{
+  VOID *Temp;
+
+  if (Controller != mDevice) {
+    return EFI_UNSUPPORTED;
+  }
+
+  if (gBS->HandleProtocol(Controller, &gEfiGraphicsOutputProtocolGuid,
+                          (VOID **) &Temp) == EFI_SUCCESS) {
+    return EFI_ALREADY_STARTED;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+DriverStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
+  )
+{
+  UINTN Index;
+  EFI_STATUS Status;
+  VOID *Dummy;
+
+  Status = gBS->OpenProtocol (
+    Controller,
+    &gEfiCallerIdGuid,
+    (VOID **) &Dummy,
+    This->DriverBindingHandle,
+    Controller,
+    EFI_OPEN_PROTOCOL_BY_DRIVER
+    );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  gDisplayProto.Mode = AllocateZeroPool(sizeof(EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE));
+  if (gDisplayProto.Mode == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto done;
+  }
+
+  gDisplayProto.Mode->Info = AllocateZeroPool(sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION));
+  if (gDisplayProto.Mode->Info == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto done;
+  }
+
+
+  if (PcdGet32(PcdDisplayEnableVModes)) {
+    mLastMode = ELES(mGopModeData) - 1;
+  } else {
+    mLastMode = 0;
+    /*
+     * mBootWidth x mBootHeight may not be sensible,
+     * so clean it up, since we won't be adding
+     * any other extra vmodes.
+     */
+    if (mBootWidth < 640 ||
+        mBootHeight < 480) {
+      mBootWidth = 640;
+      mBootHeight = 480;
+    }
+  }
+
+  mGopModeData[mLastMode].Width = mBootWidth;
+  mGopModeData[mLastMode].Height = mBootHeight;
+
+  for (Index = 0; Index <= mLastMode; Index++) {
+    UINTN FbSize;
+    UINTN FbPitch;
+    EFI_PHYSICAL_ADDRESS FbBase;
+
+    GOP_MODE_DATA *Mode = &mGopModeData[Index];
+
+    Status = mFwProtocol->GetFB(Mode->Width, Mode->Height,
+                                PI3_BITS_PER_PIXEL, &FbBase,
+                                &FbSize, &FbPitch);
+    if (EFI_ERROR(Status)) {
+      goto done;
+    }
+
+    //
+    // There is no way to communicate pitch back to OS. OS and even UEFI
+    // expect a fully linear frame buffer. So the width should
+    // be based on the frame buffer's pitch value. In some cases VC
+    // firmware would allocate ao frame buffer with some padding
+    // presumably to be 8 byte align.
+    //
+    Mode->Width = FbPitch / PI3_BYTES_PER_PIXEL;
+
+    DEBUG((DEBUG_INFO, "Mode %u: %u x %u framebuffer is %u bytes at %p\n",
+           Index, Mode->Width, Mode->Height, FbSize, FbBase));
+
+    ASSERT (FbPitch != 0);
+    ASSERT (FbBase != 0);
+    ASSERT (FbSize != 0);
+  }
+
+  // Both set the mode and initialize current mode information.
+  gDisplayProto.Mode->MaxMode = mLastMode + 1;
+  DisplaySetMode(&gDisplayProto, 0);
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+    &Controller, &gEfiGraphicsOutputProtocolGuid,
+    &gDisplayProto, NULL);
+  if (EFI_ERROR (Status)) {
+    goto done;
+  }
+
+  if (PcdGet32(PcdDisplayEnableSShot)) {
+    RegisterScreenshotHandlers();
+  } else {
+    DEBUG((DEBUG_INFO, "Screenshot capture disabled\n"));
+  }
+
+done:
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "Could not start DisplayDxe: %r\n", Status));
+    if (gDisplayProto.Mode->Info != NULL) {
+      FreePool(gDisplayProto.Mode->Info);
+      gDisplayProto.Mode->Info = NULL;
+    }
+
+    if (gDisplayProto.Mode != NULL) {
+      FreePool(gDisplayProto.Mode);
+      gDisplayProto.Mode = NULL;
+    }
+
+    gBS->CloseProtocol (
+      Controller,
+      &gEfiCallerIdGuid,
+      This->DriverBindingHandle,
+      Controller
+      );
+  }
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+DriverStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  Controller,
+  IN UINTN                       NumberOfChildren,
+  IN EFI_HANDLE                  *ChildHandleBuffer
+  )
+{
+  EFI_STATUS Status;
+
+  ClearScreen(&gDisplayProto);
+
+  Status = gBS->UninstallMultipleProtocolInterfaces (
+    Controller, &gEfiGraphicsOutputProtocolGuid,
+    &gDisplayProto, NULL);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  FreePool(gDisplayProto.Mode->Info);
+  gDisplayProto.Mode->Info = NULL;
+  FreePool(gDisplayProto.Mode);
+  gDisplayProto.Mode = NULL;
+
+  gBS->CloseProtocol (
+    Controller,
+    &gEfiCallerIdGuid,
+    This->DriverBindingHandle,
+    Controller
+    );
+
+  return Status;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.h b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.h
new file mode 100644
index 000000000000..9fa3d4f6af83
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.h
@@ -0,0 +1,43 @@
+/** @file
+ *
+ *  Copyright (c) 2017-2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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 _DISPLAY_H_
+#define _DISPLAY_H_
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/PcdLib.h>
+#include <Library/IoLib.h>
+#include <Library/TimerLib.h>
+#include <Protocol/GraphicsOutput.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/RaspberryPiFirmware.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Protocol/Cpu.h>
+#include <Utils.h>
+
+extern EFI_GRAPHICS_OUTPUT_PROTOCOL gDisplayProto;
+extern EFI_COMPONENT_NAME_PROTOCOL  gComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gComponentName2;
+
+VOID
+RegisterScreenshotHandlers(
+  VOID
+  );
+
+#endif /* _DISPLAY_H_ */
diff --git a/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.inf b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.inf
new file mode 100644
index 000000000000..1603edd4909d
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.inf
@@ -0,0 +1,71 @@
+#/** @file
+#
+#  Component description file for Graphics Output module
+#
+#  Copyright (c) 2017, Andrei Warkentin <andrey.warkentin at gmail.com>
+#  Copyright (c) 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                    = 0x00010005
+  BASE_NAME                      = DisplayDxe
+  FILE_GUID                      = c5deae31-fad2-4030-841b-cfc9644d2c5b
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = DisplayDxeInitialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  DRIVER_BINDING                =  gGraphicsConsoleDriverBinding
+#  COMPONENT_NAME                =  gGraphicsConsoleComponentName
+#  COMPONENT_NAME2               =  gGraphicsConsoleComponentName2
+#
+
+[Sources]
+  DisplayDxe.c
+  Screenshot.c
+  ComponentName.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  ArmPkg/ArmPkg.dec
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  UefiLib
+  MemoryAllocationLib
+  UefiDriverEntryPoint
+  IoLib
+  TimerLib
+  BmpSupportLib
+  UefiRuntimeServicesTableLib
+
+[Protocols]
+  gEfiLoadedImageProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiGraphicsOutputProtocolGuid
+  gRaspberryPiFirmwareProtocolGuid
+  gEfiCpuArchProtocolGuid
+  gEfiSimpleFileSystemProtocolGuid
+  gEfiSimpleTextInputExProtocolGuid
+
+[Pcd]
+  gRaspberryPiTokenSpaceGuid.PcdDisplayEnableVModes
+  gRaspberryPiTokenSpaceGuid.PcdDisplayEnableSShot
+
+[Guids]
+
+[Depex]
+  gEfiCpuArchProtocolGuid AND gRaspberryPiFirmwareProtocolGuid
diff --git a/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/Screenshot.c b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/Screenshot.c
new file mode 100644
index 000000000000..a2feeba6084f
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/Screenshot.c
@@ -0,0 +1,379 @@
+/** @file
+ *
+ *  Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *
+ *  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.
+ *
+ **/
+
+/*
+ * Loosely based on CrScreenShotDxe (https://github.com/LongSoft/CrScreenshotDxe).
+ *
+ * Copyright (c) 2016, Nikolaj Schlej, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "DisplayDxe.h"
+#include <Protocol/SimpleFileSystem.h>
+#include <Library/PrintLib.h>
+#include <Library/BmpSupportLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+/*
+ * ShowStatus defs.
+ */
+#define STATUS_SQUARE_SIDE 5
+#define STATUS_YELLOW 0xff, 0xff, 0x00
+#define STATUS_GREEN  0x00, 0xff, 0x00
+#define STATUS_BLUE   0x00, 0x00, 0xff
+#define STATUS_RED    0xff, 0x00, 0x00
+
+EFI_STATUS
+ShowStatus (
+  IN EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput,
+  IN UINT8 Red,
+  IN UINT8 Green,
+  IN UINT8 Blue
+  )
+{
+  UINTN Index;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL Square[STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE];
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL Backup[STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE];
+
+  for (Index = 0 ; Index < STATUS_SQUARE_SIDE * STATUS_SQUARE_SIDE; Index++) {
+    Square[Index].Blue = Blue;
+    Square[Index].Green = Green;
+    Square[Index].Red = Red;
+    Square[Index].Reserved = 0x00;
+  }
+
+  // Backup current image.
+  GraphicsOutput->Blt(GraphicsOutput, Backup,
+                      EfiBltVideoToBltBuffer, 0, 0, 0, 0,
+                      STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0);
+
+  // Draw the status square.
+  GraphicsOutput->Blt(GraphicsOutput, Square,
+                      EfiBltBufferToVideo, 0, 0, 0, 0,
+                      STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0);
+
+  // Wait 500ms.
+  gBS->Stall(500*1000);
+
+  // Restore the backup.
+  GraphicsOutput->Blt(GraphicsOutput, Backup,
+                      EfiBltBufferToVideo, 0, 0, 0, 0,
+                      STATUS_SQUARE_SIDE, STATUS_SQUARE_SIDE, 0);
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+FindWritableFs (
+    OUT EFI_FILE_PROTOCOL **WritableFs
+    )
+{
+  EFI_FILE_PROTOCOL *Fs = NULL;
+  EFI_HANDLE *HandleBuffer = NULL;
+  UINTN      HandleCount;
+  UINTN      Index;
+
+  EFI_STATUS Status = gBS->LocateHandleBuffer(ByProtocol,
+                                              &gEfiSimpleFileSystemProtocolGuid,
+                                              NULL, &HandleCount, &HandleBuffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  for (Index = 0; Index < HandleCount; Index++) {
+    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFs = NULL;
+    EFI_FILE_PROTOCOL *File = NULL;
+
+    Status = gBS->HandleProtocol(HandleBuffer[Index],
+                                 &gEfiSimpleFileSystemProtocolGuid,
+                                 (VOID **) &SimpleFs);
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      /*
+       * Not supposed to happen.
+       */
+      continue;
+    }
+
+    Status = SimpleFs->OpenVolume(SimpleFs, &Fs);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "%a OpenVolume[%u] returned %r\n", __FUNCTION__,
+             Index, Status));
+      continue;
+    }
+
+    Status = Fs->Open(Fs, &File, L"--------.---",
+                      EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ |
+                      EFI_FILE_MODE_WRITE, 0);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "%a Open[%u] returned %r\n", __FUNCTION__,
+             Index, Status));
+      continue;
+    }
+
+    /*
+     * Okay, we have a writable filesystem!
+     */
+    Fs->Delete(File);
+    *WritableFs = Fs;
+    Status = EFI_SUCCESS;
+    break;
+  }
+
+  if (HandleBuffer) {
+    FreePool(HandleBuffer);
+  }
+
+  return Status;
+}
+
+STATIC
+VOID
+TakeScreenshot(
+  VOID
+  )
+{
+  VOID *BmpImage = NULL;
+  EFI_FILE_PROTOCOL *Fs = NULL;
+  EFI_FILE_PROTOCOL *File = NULL;
+  EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = &gDisplayProto;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Image = NULL;
+  EFI_STATUS Status;
+  CHAR16 FileName[8+1+3+1];
+  UINT32 ScreenWidth;
+  UINT32 ScreenHeight;
+  UINTN ImageSize;
+  UINTN BmpSize;
+  UINTN Index;
+  EFI_TIME Time;
+
+  Status = FindWritableFs(&Fs);
+  if (EFI_ERROR (Status)) {
+    ShowStatus(GraphicsOutput, STATUS_YELLOW);
+  }
+
+  ScreenWidth  = GraphicsOutput->Mode->Info->HorizontalResolution;
+  ScreenHeight = GraphicsOutput->Mode->Info->VerticalResolution;
+  ImageSize = ScreenWidth * ScreenHeight;
+
+  Status = gRT->GetTime(&Time, NULL);
+  if (!EFI_ERROR(Status)) {
+    UnicodeSPrint(FileName, sizeof(FileName), L"%02d%02d%02d%02d.bmp",
+                  Time.Day, Time.Hour, Time.Minute, Time.Second);
+  } else {
+    UnicodeSPrint(FileName, sizeof(FileName), L"scrnshot.bmp");
+  }
+
+  Image = AllocatePool(ImageSize * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
+  if (Image == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    ShowStatus(GraphicsOutput, STATUS_RED);
+    goto done;
+  }
+
+  Status = GraphicsOutput->Blt(GraphicsOutput, Image,
+                               EfiBltVideoToBltBuffer, 0, 0, 0, 0,
+                               ScreenWidth, ScreenHeight, 0);
+  if (EFI_ERROR(Status)) {
+    ShowStatus(GraphicsOutput, STATUS_RED);
+    goto done;
+  }
+
+  for (Index = 0; Index < ImageSize; Index++) {
+    if (Image[Index].Red != 0x00 ||
+        Image[Index].Green != 0x00 ||
+        Image[Index].Blue != 0x00) {
+      break;
+    }
+  }
+
+  if (Index == ImageSize) {
+    ShowStatus(GraphicsOutput, STATUS_BLUE);
+    goto done;
+  }
+
+  Status = TranslateGopBltToBmp(Image, ScreenHeight, ScreenWidth,
+                                &BmpImage, (UINT32 *) &BmpSize);
+  if (EFI_ERROR(Status)) {
+    ShowStatus(GraphicsOutput, STATUS_RED);
+    goto done;
+  }
+
+  Status = Fs->Open(Fs, &File, FileName, EFI_FILE_MODE_CREATE |
+                    EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0);
+  if (EFI_ERROR (Status)) {
+    ShowStatus(GraphicsOutput, STATUS_RED);
+    goto done;
+  }
+
+  Status = File->Write(File, &BmpSize, BmpImage);
+  File->Close(File);
+  if (EFI_ERROR (Status)) {
+    ShowStatus(GraphicsOutput, STATUS_RED);
+    goto done;
+  }
+
+  ShowStatus(GraphicsOutput, STATUS_GREEN);
+done:
+  if (BmpImage != NULL) {
+    FreePool (BmpImage);
+  }
+
+  if (Image != NULL) {
+    FreePool (Image);
+  }
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+ScreenshotKeyHandler (
+  IN EFI_KEY_DATA *KeyData
+  )
+{
+  TakeScreenshot();
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+ProcessScreenshotHandler(
+  IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx
+  )
+{
+  EFI_STATUS Status;
+  EFI_HANDLE Handle;
+  EFI_KEY_DATA ScreenshotKey;
+
+  /*
+   * LCtrl+LAlt+F12
+   */
+  ScreenshotKey.Key.ScanCode = SCAN_F12;
+  ScreenshotKey.Key.UnicodeChar = 0;
+  ScreenshotKey.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID |
+    EFI_LEFT_CONTROL_PRESSED | EFI_LEFT_ALT_PRESSED;
+  ScreenshotKey.KeyState.KeyToggleState = 0;
+
+  Status = SimpleTextInEx->RegisterKeyNotify (
+                                              SimpleTextInEx,
+                                              &ScreenshotKey,
+                                              ScreenshotKeyHandler,
+                                              &Handle
+                                              );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: couldn't register key notification: %r\n",
+            __FUNCTION__, Status));
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+VOID
+ProcessScreenshotHandlers(
+  VOID
+  )
+{
+  UINTN Index;
+  EFI_STATUS Status;
+  UINTN HandleCount;
+  EFI_HANDLE *HandleBuffer;
+  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx;
+
+  Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleTextInputExProtocolGuid,
+                                    NULL, &HandleCount, &HandleBuffer);
+  if (EFI_ERROR (Status)) {
+    return;
+  }
+
+  for (Index = 0; Index < HandleCount; Index++) {
+    Status = gBS->HandleProtocol (HandleBuffer[Index],
+                                  &gEfiSimpleTextInputExProtocolGuid,
+                                  (VOID **) &SimpleTextInEx);
+    if (EFI_ERROR (Status)) {
+      ASSERT_EFI_ERROR (Status);
+      /*
+       * Not supposed to happen.
+       */
+      continue;
+    }
+
+    Status = ProcessScreenshotHandler(SimpleTextInEx);
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+  }
+}
+
+STATIC
+VOID
+EFIAPI
+OnTextInExInstall (
+  IN EFI_EVENT Event,
+  IN VOID *Context
+  )
+{
+  ProcessScreenshotHandlers();
+}
+
+VOID
+RegisterScreenshotHandlers(
+  VOID
+  )
+{
+  EFI_STATUS Status;
+  EFI_EVENT TextInExInstallEvent;
+  VOID *TextInExInstallRegistration;
+
+  ProcessScreenshotHandlers();
+
+  Status = gBS->CreateEvent(EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+                            OnTextInExInstall, NULL,
+                            &TextInExInstallEvent);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: couldn't create protocol install event: %r\n",
+            __FUNCTION__, Status));
+    return;
+  }
+
+  Status = gBS->RegisterProtocolNotify(&gEfiSimpleTextInputExProtocolGuid,
+                                       TextInExInstallEvent,
+                                       &TextInExInstallRegistration);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: couldn't register protocol install notify: %r\n",
+            __FUNCTION__, Status));
+    return;
+  }
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/ComponentName.c b/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/ComponentName.c
new file mode 100644
index 000000000000..e5935b19a250
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/ComponentName.c
@@ -0,0 +1,183 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2006-2016, Intel 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 "GraphicsConsole.h"
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gGraphicsConsoleComponentName = {
+  GraphicsConsoleComponentNameGetDriverName,
+  GraphicsConsoleComponentNameGetControllerName,
+  "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gGraphicsConsoleComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) GraphicsConsoleComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) GraphicsConsoleComponentNameGetControllerName,
+  "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mGraphicsConsoleDriverNameTable[] = {
+  {
+    "eng;en",
+    (CHAR16 *)L"Graphics Console Driver"
+  },
+  {
+    NULL,
+    NULL
+  }
+};
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mGraphicsConsoleDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gGraphicsConsoleComponentName)
+           );
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  )
+{
+  return EFI_UNSUPPORTED;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsole.c b/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsole.c
new file mode 100644
index 000000000000..4df5b37e5d4f
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsole.c
@@ -0,0 +1,1836 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2006-2016, Intel 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 "GraphicsConsole.h"
+
+//
+// Graphics Console Device Private Data template
+//
+GRAPHICS_CONSOLE_DEV    mGraphicsConsoleDevTemplate = {
+  GRAPHICS_CONSOLE_DEV_SIGNATURE,
+  (EFI_GRAPHICS_OUTPUT_PROTOCOL *) NULL,
+  {
+    GraphicsConsoleConOutReset,
+    GraphicsConsoleConOutOutputString,
+    GraphicsConsoleConOutTestString,
+    GraphicsConsoleConOutQueryMode,
+    GraphicsConsoleConOutSetMode,
+    GraphicsConsoleConOutSetAttribute,
+    GraphicsConsoleConOutClearScreen,
+    GraphicsConsoleConOutSetCursorPosition,
+    GraphicsConsoleConOutEnableCursor,
+    (EFI_SIMPLE_TEXT_OUTPUT_MODE *) NULL
+  },
+  {
+    0,
+    -1,
+    EFI_TEXT_ATTR(EFI_LIGHTGRAY, EFI_BLACK),
+    0,
+    0,
+    TRUE
+  },
+  (GRAPHICS_CONSOLE_MODE_DATA *) NULL,
+  (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) NULL,
+  {
+    (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *) NULL,
+    (EFI_GRAPHICS_OUTPUT_PROTOCOL *) NULL,
+    TRUE,
+  }
+};
+
+GRAPHICS_CONSOLE_MODE_DATA mGraphicsConsoleModeData[] = {
+  {100, 31},
+  //
+  // New modes can be added here.
+  // The last entry is specific for full screen mode.
+  //
+  {0, 0}
+};
+
+EFI_HII_DATABASE_PROTOCOL   *mHiiDatabase;
+EFI_HII_FONT_PROTOCOL       *mHiiFont;
+EFI_HII_HANDLE              mHiiHandle;
+VOID                        *mHiiRegistration;
+
+EFI_GUID             mFontPackageListGuid = {0xf5f219d3, 0x7006, 0x4648, {0xac, 0x8d, 0xd6, 0x1d, 0xfb, 0x7b, 0xc6, 0xad}};
+
+CHAR16               mCrLfString[3] = { CHAR_CARRIAGE_RETURN, CHAR_LINEFEED, CHAR_NULL };
+
+EFI_GRAPHICS_OUTPUT_BLT_PIXEL        mGraphicsEfiColors[16] = {
+  //
+  // B    G    R   reserved
+  //
+  {0x00, 0x00, 0x00, 0x00},  // BLACK
+  {0x98, 0x00, 0x00, 0x00},  // LIGHTBLUE
+  {0x00, 0x98, 0x00, 0x00},  // LIGHGREEN
+  {0x98, 0x98, 0x00, 0x00},  // LIGHCYAN
+  {0x00, 0x00, 0x98, 0x00},  // LIGHRED
+  {0x98, 0x00, 0x98, 0x00},  // MAGENTA
+  {0x00, 0x98, 0x98, 0x00},  // BROWN
+  {0x98, 0x98, 0x98, 0x00},  // LIGHTGRAY
+  {0x30, 0x30, 0x30, 0x00},  // DARKGRAY - BRIGHT BLACK
+  {0xff, 0x00, 0x00, 0x00},  // BLUE
+  {0x00, 0xff, 0x00, 0x00},  // LIME
+  {0xff, 0xff, 0x00, 0x00},  // CYAN
+  {0x00, 0x00, 0xff, 0x00},  // RED
+  {0xff, 0x00, 0xff, 0x00},  // FUCHSIA
+  {0x00, 0xff, 0xff, 0x00},  // YELLOW
+  {0xff, 0xff, 0xff, 0x00}   // WHITE
+};
+
+EFI_NARROW_GLYPH     mCursorGlyph = {
+  0x0000,
+  0x00,
+  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF }
+};
+
+CHAR16       SpaceStr[] = { NARROW_CHAR, ' ', 0 };
+
+EFI_DRIVER_BINDING_PROTOCOL gGraphicsConsoleDriverBinding = {
+  GraphicsConsoleControllerDriverSupported,
+  GraphicsConsoleControllerDriverStart,
+  GraphicsConsoleControllerDriverStop,
+  0xa,
+  NULL,
+  NULL
+};
+
+/**
+  Test to see if Graphics Console could be supported on the Controller.
+
+  Graphics Console could be supported if Graphics Output Protocol
+  exists on the Controller.
+
+  @param  This                Protocol instance pointer.
+  @param  Controller          Handle of device to test.
+  @param  RemainingDevicePath Optional parameter use to pick a specific child
+                              device to start.
+
+  @retval EFI_SUCCESS         This driver supports this device.
+  @retval other               This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleControllerDriverSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
+  EFI_DEVICE_PATH_PROTOCOL     *DevicePath;
+
+  GraphicsOutput = NULL;
+  //
+  // Open the IO Abstraction(s) needed to perform the supported test
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiGraphicsOutputProtocolGuid,
+                  (VOID **) &GraphicsOutput,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  ASSERT (GraphicsOutput != NULL);
+
+  //
+  // We need to ensure that we do not layer on top of a virtual handle.
+  // We need to ensure that the handles produced by the conspliter do not
+  // get used.
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiDevicePathProtocolGuid,
+                  (VOID **) &DevicePath,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (!EFI_ERROR (Status)) {
+    gBS->CloseProtocol (
+          Controller,
+          &gEfiDevicePathProtocolGuid,
+          This->DriverBindingHandle,
+          Controller
+          );
+  } else {
+    goto Error;
+  }
+
+  //
+  // Does Hii Exist?  If not, we aren't ready to run
+  //
+  Status = EfiLocateHiiProtocol ();
+
+  //
+  // Close the I/O Abstraction(s) used to perform the supported test
+  //
+Error:
+  gBS->CloseProtocol (
+                      Controller,
+                      &gEfiGraphicsOutputProtocolGuid,
+                      This->DriverBindingHandle,
+                      Controller
+                      );
+  return Status;
+}
+
+/**
+  Initialize all the text modes which the graphics console supports.
+
+  It returns information for available text modes that the graphics can support.
+
+  @param[in]  HorizontalResolution     The size of video screen in pixels in the X dimension.
+  @param[in]  VerticalResolution       The size of video screen in pixels in the Y dimension.
+  @param[in]  GopModeNumber            The graphics mode number which graphis console is based on.
+  @param[out] TextModeCount            The total number of text modes that graphics console supports.
+  @param[out] TextModeData             The buffer to the text modes column and row information.
+                                       Caller is responsible to free it when it's non-NULL.
+
+  @retval EFI_SUCCESS                  The supporting mode information is returned.
+  @retval EFI_INVALID_PARAMETER        The parameters are invalid.
+
+**/
+EFI_STATUS
+InitializeGraphicsConsoleTextMode (
+  IN UINT32                        HorizontalResolution,
+  IN UINT32                        VerticalResolution,
+  IN UINT32                        GopModeNumber,
+  OUT UINTN                        *TextModeCount,
+  OUT GRAPHICS_CONSOLE_MODE_DATA   **TextModeData
+  )
+{
+  UINTN                       Index;
+  UINTN                       Count;
+  GRAPHICS_CONSOLE_MODE_DATA  *ModeBuffer;
+  GRAPHICS_CONSOLE_MODE_DATA  *NewModeBuffer;
+  UINTN                       ValidCount;
+  UINTN                       ValidIndex;
+  UINTN                       MaxColumns;
+  UINTN                       MaxRows;
+
+  if ((TextModeCount == NULL) || (TextModeData == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Count = sizeof (mGraphicsConsoleModeData) / sizeof (GRAPHICS_CONSOLE_MODE_DATA);
+
+  //
+  // Compute the maximum number of text Rows and Columns that this current graphics mode can support.
+  // To make graphics console work well, MaxColumns and MaxRows should not be zero.
+  //
+  MaxColumns = HorizontalResolution / EFI_GLYPH_WIDTH;
+  MaxRows    = VerticalResolution / EFI_GLYPH_HEIGHT;
+
+  //
+  // According to UEFI spec, all output devices support at least 80x25 text mode.
+  //
+  ASSERT ((MaxColumns >= 80) && (MaxRows >= 25));
+
+  //
+  // Add full screen mode to the last entry.
+  //
+  mGraphicsConsoleModeData[Count - 1].Columns = MaxColumns;
+  mGraphicsConsoleModeData[Count - 1].Rows    = MaxRows;
+
+  //
+  // Get defined mode buffer pointer.
+  //
+  ModeBuffer = mGraphicsConsoleModeData;
+
+  //
+  // Here we make sure that the final mode exposed does not include the duplicated modes,
+  // and does not include the invalid modes which exceed the max column and row.
+  // Reserve 2 modes for 80x25, 80x50 of graphics console.
+  //
+  NewModeBuffer = AllocateZeroPool (sizeof (GRAPHICS_CONSOLE_MODE_DATA) * (Count + 2));
+  ASSERT (NewModeBuffer != NULL);
+
+  //
+  // Mode 0 and mode 1 is for 80x25, 80x50 according to UEFI spec.
+  //
+  ValidCount = 0;
+
+  NewModeBuffer[ValidCount].Columns       = 80;
+  NewModeBuffer[ValidCount].Rows          = 25;
+  NewModeBuffer[ValidCount].GopWidth      = HorizontalResolution;
+  NewModeBuffer[ValidCount].GopHeight     = VerticalResolution;
+  NewModeBuffer[ValidCount].GopModeNumber = GopModeNumber;
+  NewModeBuffer[ValidCount].DeltaX        = (HorizontalResolution - (NewModeBuffer[ValidCount].Columns * EFI_GLYPH_WIDTH)) >> 1;
+  NewModeBuffer[ValidCount].DeltaY        = (VerticalResolution - (NewModeBuffer[ValidCount].Rows * EFI_GLYPH_HEIGHT)) >> 1;
+  ValidCount++;
+
+  if ((MaxColumns >= 80) && (MaxRows >= 50)) {
+    NewModeBuffer[ValidCount].Columns = 80;
+    NewModeBuffer[ValidCount].Rows    = 50;
+    NewModeBuffer[ValidCount].DeltaX  = (HorizontalResolution - (80 * EFI_GLYPH_WIDTH)) >> 1;
+    NewModeBuffer[ValidCount].DeltaY  = (VerticalResolution - (50 * EFI_GLYPH_HEIGHT)) >> 1;
+  }
+  NewModeBuffer[ValidCount].GopWidth      = HorizontalResolution;
+  NewModeBuffer[ValidCount].GopHeight     = VerticalResolution;
+  NewModeBuffer[ValidCount].GopModeNumber = GopModeNumber;
+  ValidCount++;
+
+  //
+  // Start from mode 2 to put the valid mode other than 80x25 and 80x50 in the output mode buffer.
+  //
+  for (Index = 0; Index < Count; Index++) {
+    if ((ModeBuffer[Index].Columns == 0) || (ModeBuffer[Index].Rows == 0) ||
+        (ModeBuffer[Index].Columns > MaxColumns) || (ModeBuffer[Index].Rows > MaxRows)) {
+      //
+      // Skip the pre-defined mode which is invalid or exceeds the max column and row.
+      //
+      continue;
+    }
+    for (ValidIndex = 0; ValidIndex < ValidCount; ValidIndex++) {
+      if ((ModeBuffer[Index].Columns == NewModeBuffer[ValidIndex].Columns) &&
+          (ModeBuffer[Index].Rows == NewModeBuffer[ValidIndex].Rows)) {
+        //
+        // Skip the duplicated mode.
+        //
+        break;
+      }
+    }
+    if (ValidIndex == ValidCount) {
+      NewModeBuffer[ValidCount].Columns       = ModeBuffer[Index].Columns;
+      NewModeBuffer[ValidCount].Rows          = ModeBuffer[Index].Rows;
+      NewModeBuffer[ValidCount].GopWidth      = HorizontalResolution;
+      NewModeBuffer[ValidCount].GopHeight     = VerticalResolution;
+      NewModeBuffer[ValidCount].GopModeNumber = GopModeNumber;
+      NewModeBuffer[ValidCount].DeltaX        = (HorizontalResolution - (NewModeBuffer[ValidCount].Columns * EFI_GLYPH_WIDTH)) >> 1;
+      NewModeBuffer[ValidCount].DeltaY        = (VerticalResolution - (NewModeBuffer[ValidCount].Rows * EFI_GLYPH_HEIGHT)) >> 1;
+      ValidCount++;
+    }
+  }
+
+  DEBUG_CODE (
+    for (Index = 0; Index < ValidCount; Index++) {
+      DEBUG ((DEBUG_INFO, "Graphics - Mode %d, Column = %d, Row = %d\n",
+                           Index, NewModeBuffer[Index].Columns, NewModeBuffer[Index].Rows));
+    }
+  );
+
+  //
+  // Return valid mode count and mode information buffer.
+  //
+  *TextModeCount = ValidCount;
+  *TextModeData  = NewModeBuffer;
+  return EFI_SUCCESS;
+}
+
+/**
+  Start this driver on Controller by opening Graphics Output Protocol,
+  and installing Simple Text Out protocol on Controller.
+
+  @param  This                 Protocol instance pointer.
+  @param  Controller           Handle of device to bind driver to
+  @param  RemainingDevicePath  Optional parameter use to pick a specific child
+                               device to start.
+
+  @retval EFI_SUCCESS          This driver is added to Controller.
+  @retval other                This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleControllerDriverStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  )
+{
+  EFI_STATUS                           Status;
+  GRAPHICS_CONSOLE_DEV                 *Private;
+  UINT32                               HorizontalResolution;
+  UINT32                               VerticalResolution;
+  UINT32                               ModeIndex;
+  UINTN                                MaxMode;
+  UINT32                               ModeNumber;
+  EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE    *Mode;
+  UINTN                                SizeOfInfo;
+  EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
+
+  ModeNumber = 0;
+
+  //
+  // Initialize the Graphics Console device instance
+  //
+  Private = AllocateCopyPool (
+              sizeof (GRAPHICS_CONSOLE_DEV),
+              &mGraphicsConsoleDevTemplate
+              );
+  if (Private == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Private->SimpleTextOutput.Mode = &(Private->SimpleTextOutputMode);
+
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiGraphicsOutputProtocolGuid,
+                  (VOID **) &Private->GraphicsOutput,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto Error;
+  }
+
+  Private->ExtendedTextOutput.TextOut = &(Private->SimpleTextOutput);
+  Private->ExtendedTextOutput.GraphicsOutput = Private->GraphicsOutput;
+
+  HorizontalResolution  = PcdGet32 (PcdVideoHorizontalResolution);
+  VerticalResolution    = PcdGet32 (PcdVideoVerticalResolution);
+
+  ASSERT (Private->GraphicsOutput != NULL);
+  //
+  // The console is build on top of Graphics Output Protocol, find the mode number
+  // for the user-defined mode; if there are multiple video devices,
+  // graphic console driver will set all the video devices to the same mode.
+  //
+  if ((HorizontalResolution == 0x0) || (VerticalResolution == 0x0)) {
+    //
+    // Find the highest resolution which GOP supports.
+    //
+    MaxMode = Private->GraphicsOutput->Mode->MaxMode;
+
+    for (ModeIndex = 0; ModeIndex < MaxMode; ModeIndex++) {
+      Status = Private->GraphicsOutput->QueryMode (
+                                                   Private->GraphicsOutput,
+                                                   ModeIndex,
+                                                   &SizeOfInfo,
+                                                   &Info
+                                                   );
+      if (!EFI_ERROR (Status)) {
+        if ((Info->HorizontalResolution > HorizontalResolution) ||
+            ((Info->HorizontalResolution == HorizontalResolution) && (Info->VerticalResolution > VerticalResolution))) {
+          HorizontalResolution = Info->HorizontalResolution;
+          VerticalResolution   = Info->VerticalResolution;
+          ModeNumber           = ModeIndex;
+        }
+        FreePool (Info);
+      }
+    }
+    if ((HorizontalResolution == 0x0) || (VerticalResolution == 0x0)) {
+      Status = EFI_UNSUPPORTED;
+      goto Error;
+    }
+  } else {
+    //
+    // Use user-defined resolution
+    //
+    Status = CheckModeSupported (
+                                 Private->GraphicsOutput,
+                                 HorizontalResolution,
+                                 VerticalResolution,
+                                 &ModeNumber
+                                 );
+    if (EFI_ERROR (Status)) {
+      //
+      // if not supporting current mode, try 800x600 which is required by UEFI/EFI spec.
+      //
+      HorizontalResolution = 800;
+      VerticalResolution   = 600;
+      Status = CheckModeSupported (
+                                   Private->GraphicsOutput,
+                                   HorizontalResolution,
+                                   VerticalResolution,
+                                   &ModeNumber
+                                   );
+      Mode = Private->GraphicsOutput->Mode;
+      if (EFI_ERROR (Status) && Mode->MaxMode != 0) {
+        //
+        // Set default mode failed or device don't support default mode, then get the current mode information.
+        //
+        HorizontalResolution = Mode->Info->HorizontalResolution;
+        VerticalResolution = Mode->Info->VerticalResolution;
+        ModeNumber = Mode->Mode;
+        }
+    }
+  }
+  if (ModeNumber != Private->GraphicsOutput->Mode->Mode) {
+    //
+    // Current graphics mode is not set or is not set to the mode which we has found,
+    // set the new graphic mode.
+    //
+    Status = Private->GraphicsOutput->SetMode (Private->GraphicsOutput, ModeNumber);
+    if (EFI_ERROR (Status)) {
+      //
+      // The mode set operation failed
+      //
+      goto Error;
+    }
+  }
+
+  DEBUG ((DEBUG_INFO, "GraphicsConsole video resolution %d x %d\n", HorizontalResolution, VerticalResolution));
+
+  //
+  // Initialize the mode which GraphicsConsole supports.
+  //
+  Status = InitializeGraphicsConsoleTextMode (
+             HorizontalResolution,
+             VerticalResolution,
+             ModeNumber,
+             &MaxMode,
+             &Private->ModeData
+             );
+
+  if (EFI_ERROR (Status)) {
+    goto Error;
+  }
+
+  //
+  // Update the maximum number of modes
+  //
+  Private->SimpleTextOutputMode.MaxMode = (INT32) MaxMode;
+
+  DEBUG_CODE_BEGIN ();
+    Status = GraphicsConsoleConOutSetMode (&Private->SimpleTextOutput, 0);
+    if (EFI_ERROR (Status)) {
+      goto Error;
+    }
+    Status = GraphicsConsoleConOutOutputString (&Private->SimpleTextOutput, (CHAR16 *)L"Graphics Console Started\n\r");
+    if (EFI_ERROR (Status)) {
+      goto Error;
+    }
+  DEBUG_CODE_END ();
+
+  //
+  // Install protocol interfaces for the Graphics Console device.
+  //
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &Controller,
+                  &gEfiSimpleTextOutProtocolGuid,
+                  &Private->SimpleTextOutput,
+                  &gExtendedTextOutputProtocolGuid,
+                  &Private->ExtendedTextOutput,
+                  NULL
+                  );
+
+Error:
+  if (EFI_ERROR (Status)) {
+    //
+    // Close GOP.
+    //
+    gBS->CloseProtocol (
+                        Controller,
+                        &gEfiGraphicsOutputProtocolGuid,
+                        This->DriverBindingHandle,
+                        Controller
+                        );
+
+    if (Private->LineBuffer != NULL) {
+      FreePool (Private->LineBuffer);
+    }
+
+    if (Private->ModeData != NULL) {
+      FreePool (Private->ModeData);
+    }
+
+    //
+    // Free private data
+    //
+    FreePool (Private);
+  }
+
+  return Status;
+}
+
+/**
+  Stop this driver on Controller by removing Simple Text Out protocol
+  and closing the Graphics Output Protocol on Controller.
+
+
+  @param  This              Protocol instance pointer.
+  @param  Controller        Handle of device to stop driver on
+  @param  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
+                            children is zero stop the entire bus driver.
+  @param  ChildHandleBuffer List of Child Handles to Stop.
+
+  @retval EFI_SUCCESS       This driver is removed Controller.
+  @retval EFI_NOT_STARTED   Simple Text Out protocol could not be found the
+                            Controller.
+  @retval other             This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleControllerDriverStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL   *This,
+  IN  EFI_HANDLE                    Controller,
+  IN  UINTN                         NumberOfChildren,
+  IN  EFI_HANDLE                    *ChildHandleBuffer
+  )
+{
+  EFI_STATUS                       Status;
+  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *SimpleTextOutput;
+  GRAPHICS_CONSOLE_DEV             *Private;
+
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiSimpleTextOutProtocolGuid,
+                  (VOID **) &SimpleTextOutput,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    return EFI_NOT_STARTED;
+  }
+
+  Private = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (SimpleTextOutput);
+
+  Status = gBS->UninstallMultipleProtocolInterfaces (
+                  Controller,
+                  &gEfiSimpleTextOutProtocolGuid,
+                  &Private->SimpleTextOutput,
+                  &gExtendedTextOutputProtocolGuid,
+                  &Private->ExtendedTextOutput,
+                  NULL
+                  );
+
+  if (!EFI_ERROR (Status)) {
+    //
+    // Close GOP.
+    //
+    gBS->CloseProtocol (
+                        Controller,
+                        &gEfiGraphicsOutputProtocolGuid,
+                        This->DriverBindingHandle,
+                        Controller
+                        );
+
+    if (Private->LineBuffer != NULL) {
+      FreePool (Private->LineBuffer);
+    }
+
+    if (Private->ModeData != NULL) {
+      FreePool (Private->ModeData);
+    }
+
+    //
+    // Free our instance data
+    //
+    FreePool (Private);
+  }
+
+  return Status;
+}
+
+/**
+  Check if the current specific mode supported the user defined resolution
+  for the Graphics Console device based on Graphics Output Protocol.
+
+  If yes, set the graphic devcice's current mode to this specific mode.
+
+  @param  GraphicsOutput        Graphics Output Protocol instance pointer.
+  @param  HorizontalResolution  User defined horizontal resolution
+  @param  VerticalResolution    User defined vertical resolution.
+  @param  CurrentModeNumber     Current specific mode to be check.
+
+  @retval EFI_SUCCESS       The mode is supported.
+  @retval EFI_UNSUPPORTED   The specific mode is out of range of graphics
+                            device supported.
+  @retval other             The specific mode does not support user defined
+                            resolution or failed to set the current mode to the
+                            specific mode on graphics device.
+
+**/
+EFI_STATUS
+CheckModeSupported (
+  EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput,
+  IN  UINT32                    HorizontalResolution,
+  IN  UINT32                    VerticalResolution,
+  OUT UINT32                    *CurrentModeNumber
+  )
+{
+  UINT32     ModeNumber;
+  EFI_STATUS Status;
+  UINTN      SizeOfInfo;
+  EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
+  UINT32     MaxMode;
+
+  Status  = EFI_SUCCESS;
+  MaxMode = GraphicsOutput->Mode->MaxMode;
+
+  for (ModeNumber = 0; ModeNumber < MaxMode; ModeNumber++) {
+    Status = GraphicsOutput->QueryMode (
+                       GraphicsOutput,
+                       ModeNumber,
+                       &SizeOfInfo,
+                       &Info
+                       );
+    if (!EFI_ERROR (Status)) {
+      if ((Info->HorizontalResolution == HorizontalResolution) &&
+          (Info->VerticalResolution == VerticalResolution)) {
+        if ((GraphicsOutput->Mode->Info->HorizontalResolution == HorizontalResolution) &&
+            (GraphicsOutput->Mode->Info->VerticalResolution == VerticalResolution)) {
+          //
+          // If video device has been set to this mode, we do not need to SetMode again
+          //
+          FreePool (Info);
+          break;
+        } else {
+          Status = GraphicsOutput->SetMode (GraphicsOutput, ModeNumber);
+          if (!EFI_ERROR (Status)) {
+            FreePool (Info);
+            break;
+          }
+        }
+      }
+      FreePool (Info);
+    }
+  }
+
+  if (ModeNumber == GraphicsOutput->Mode->MaxMode) {
+    Status = EFI_UNSUPPORTED;
+  }
+
+  *CurrentModeNumber = ModeNumber;
+  return Status;
+}
+
+
+/**
+  Locate HII Database protocol and HII Font protocol.
+
+  @retval  EFI_SUCCESS     HII Database protocol and HII Font protocol
+                           are located successfully.
+  @return  other           Failed to locate HII Database protocol or
+                           HII Font protocol.
+
+**/
+EFI_STATUS
+EfiLocateHiiProtocol (
+  VOID
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = gBS->LocateProtocol (&gEfiHiiDatabaseProtocolGuid, NULL, (VOID **) &mHiiDatabase);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->LocateProtocol (&gEfiHiiFontProtocolGuid, NULL, (VOID **) &mHiiFont);
+  return Status;
+}
+
+//
+// Body of the STO functions
+//
+
+/**
+  Reset the text output device hardware and optionally run diagnostics.
+
+  Implements SIMPLE_TEXT_OUTPUT.Reset().
+  If ExtendeVerification is TRUE, then perform dependent Graphics Console
+  device reset, and set display mode to mode 0.
+  If ExtendedVerification is FALSE, only set display mode to mode 0.
+
+  @param  This                  Protocol instance pointer.
+  @param  ExtendedVerification  Indicates that the driver may perform a more
+                                exhaustive verification operation of the device
+                                during reset.
+
+  @retval EFI_SUCCESS          The text output device was reset.
+  @retval EFI_DEVICE_ERROR     The text output device is not functioning correctly and
+                               could not be reset.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutReset (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  BOOLEAN                          ExtendedVerification
+  )
+{
+  EFI_STATUS    Status;
+  Status = This->SetMode (This, 0);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  Status = This->SetAttribute (This, EFI_TEXT_ATTR (This->Mode->Attribute & 0x0F, EFI_BACKGROUND_BLACK));
+  return Status;
+}
+
+
+/**
+  Write a Unicode string to the output device.
+
+  Implements SIMPLE_TEXT_OUTPUT.OutputString().
+  The Unicode string will be converted to Glyphs and will be
+  sent to the Graphics Console.
+
+  @param  This                    Protocol instance pointer.
+  @param  WString                 The NULL-terminated Unicode string to be displayed
+                                  on the output device(s). All output devices must
+                                  also support the Unicode drawing defined in this file.
+
+  @retval EFI_SUCCESS             The string was output to the device.
+  @retval EFI_DEVICE_ERROR        The device reported an error while attempting to output
+                                  the text.
+  @retval EFI_UNSUPPORTED         The output device's mode is not currently in a
+                                  defined text mode.
+  @retval EFI_WARN_UNKNOWN_GLYPH  This warning code indicates that some of the
+                                  characters in the Unicode string could not be
+                                  rendered and were skipped.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutOutputString (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  CHAR16                           *WString
+  )
+{
+  GRAPHICS_CONSOLE_DEV  *Private;
+  EFI_GRAPHICS_OUTPUT_PROTOCOL   *GraphicsOutput;
+  INTN                  Mode;
+  UINTN                 MaxColumn;
+  UINTN                 MaxRow;
+  UINTN                 Width;
+  UINTN                 Height;
+  UINTN                 Delta;
+  EFI_STATUS            Status;
+  BOOLEAN               Warning;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  Foreground;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  Background;
+  UINTN                 DeltaX;
+  UINTN                 DeltaY;
+  UINTN                 Count;
+  UINTN                 Index;
+  INT32                 OriginAttribute;
+  EFI_TPL               OldTpl;
+
+  if (This->Mode->Mode == -1) {
+    //
+    // If current mode is not valid, return error.
+    //
+    return EFI_UNSUPPORTED;
+  }
+
+  Status = EFI_SUCCESS;
+
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+  //
+  // Current mode
+  //
+  Mode      = This->Mode->Mode;
+  Private   = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (This);
+  GraphicsOutput = Private->GraphicsOutput;
+
+  MaxColumn = Private->ModeData[Mode].Columns;
+  MaxRow    = Private->ModeData[Mode].Rows;
+  DeltaX    = (UINTN) Private->ModeData[Mode].DeltaX;
+  DeltaY    = (UINTN) Private->ModeData[Mode].DeltaY;
+  Width     = MaxColumn * EFI_GLYPH_WIDTH;
+  Height    = (MaxRow - 1) * EFI_GLYPH_HEIGHT;
+  Delta     = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
+
+  //
+  // The Attributes won't change when during the time OutputString is called
+  //
+  GetTextColors (This, &Foreground, &Background);
+
+  FlushCursor (This);
+
+  Warning = FALSE;
+
+  //
+  // Backup attribute
+  //
+  OriginAttribute = This->Mode->Attribute;
+
+  while (*WString != L'\0') {
+
+    if (*WString == CHAR_BACKSPACE) {
+      //
+      // If the cursor is at the left edge of the display, then move the cursor
+      // one row up.
+      //
+      if (This->Mode->CursorColumn == 0 && This->Mode->CursorRow > 0) {
+        This->Mode->CursorRow--;
+        This->Mode->CursorColumn = (INT32) (MaxColumn - 1);
+        This->OutputString (This, SpaceStr);
+        FlushCursor (This);
+        This->Mode->CursorRow--;
+        This->Mode->CursorColumn = (INT32) (MaxColumn - 1);
+      } else if (This->Mode->CursorColumn > 0) {
+        //
+        // If the cursor is not at the left edge of the display, then move the cursor
+        // left one column.
+        //
+        This->Mode->CursorColumn--;
+        This->OutputString (This, SpaceStr);
+        FlushCursor (This);
+        This->Mode->CursorColumn--;
+      }
+
+      WString++;
+
+    } else if (*WString == CHAR_LINEFEED) {
+      //
+      // If the cursor is at the bottom of the display, then scroll the display one
+      // row, and do not update the cursor position. Otherwise, move the cursor
+      // down one row.
+      //
+      if (This->Mode->CursorRow == (INT32) (MaxRow - 1)) {
+        //
+        // Scroll Screen Up One Row
+        //
+        GraphicsOutput->Blt (
+                             GraphicsOutput,
+                             NULL,
+                             EfiBltVideoToVideo,
+                             DeltaX,
+                             DeltaY + EFI_GLYPH_HEIGHT,
+                             DeltaX,
+                             DeltaY,
+                             Width,
+                             Height,
+                             Delta
+                             );
+
+        //
+        // Print Blank Line at last line
+        //
+        GraphicsOutput->Blt (
+                             GraphicsOutput,
+                             &Background,
+                             EfiBltVideoFill,
+                             0,
+                             0,
+                             DeltaX,
+                             DeltaY + Height,
+                             Width,
+                             EFI_GLYPH_HEIGHT,
+                             Delta
+                             );
+      } else {
+        This->Mode->CursorRow++;
+      }
+
+      WString++;
+
+    } else if (*WString == CHAR_CARRIAGE_RETURN) {
+      //
+      // Move the cursor to the beginning of the current row.
+      //
+      This->Mode->CursorColumn = 0;
+      WString++;
+
+    } else if (*WString == WIDE_CHAR) {
+
+      This->Mode->Attribute |= EFI_WIDE_ATTRIBUTE;
+      WString++;
+
+    } else if (*WString == NARROW_CHAR) {
+
+      This->Mode->Attribute &= (~ (UINT32) EFI_WIDE_ATTRIBUTE);
+      WString++;
+
+    } else {
+      //
+      // Print the character at the current cursor position and move the cursor
+      // right one column. If this moves the cursor past the right edge of the
+      // display, then the line should wrap to the beginning of the next line. This
+      // is equivalent to inserting a CR and an LF. Note that if the cursor is at the
+      // bottom of the display, and the line wraps, then the display will be scrolled
+      // one line.
+      // If wide char is going to be displayed, need to display one character at a time
+      // Or, need to know the display length of a certain string.
+      //
+      // Index is used to determine how many character width units (wide = 2, narrow = 1)
+      // Count is used to determine how many characters are used regardless of their attributes
+      //
+      for (Count = 0, Index = 0; (This->Mode->CursorColumn + Index) < MaxColumn; Count++, Index++) {
+        if (WString[Count] == CHAR_NULL ||
+            WString[Count] == CHAR_BACKSPACE ||
+            WString[Count] == CHAR_LINEFEED ||
+            WString[Count] == CHAR_CARRIAGE_RETURN ||
+            WString[Count] == WIDE_CHAR ||
+            WString[Count] == NARROW_CHAR) {
+          break;
+        }
+        //
+        // Is the wide attribute on?
+        //
+        if ((This->Mode->Attribute & EFI_WIDE_ATTRIBUTE) != 0) {
+          //
+          // If wide, add one more width unit than normal since we are going to increment at the end of the for loop
+          //
+          Index++;
+          //
+          // This is the end-case where if we are at column 79 and about to print a wide character
+          // We should prevent this from happening because we will wrap inappropriately.  We should
+          // not print this character until the next line.
+          //
+          if ((This->Mode->CursorColumn + Index + 1) > MaxColumn) {
+            Index++;
+            break;
+          }
+        }
+      }
+
+      Status = DrawUnicodeWeightAtCursorN (This, WString, Count);
+      if (EFI_ERROR (Status)) {
+        Warning = TRUE;
+      }
+      //
+      // At the end of line, output carriage return and line feed
+      //
+      WString += Count;
+      This->Mode->CursorColumn += (INT32) Index;
+      if (This->Mode->CursorColumn > (INT32) MaxColumn) {
+        This->Mode->CursorColumn -= 2;
+        This->OutputString (This, SpaceStr);
+      }
+
+      if (This->Mode->CursorColumn >= (INT32) MaxColumn) {
+        FlushCursor (This);
+        if (!Private->ExtendedTextOutput.AutoWrap) {
+          This->Mode->CursorColumn = MaxColumn - 1;
+        } else {
+          This->OutputString (This, mCrLfString);
+        }
+        FlushCursor (This);
+      }
+    }
+  }
+
+  This->Mode->Attribute = OriginAttribute;
+
+  FlushCursor (This);
+
+  if (Warning) {
+    Status = EFI_WARN_UNKNOWN_GLYPH;
+  }
+
+  gBS->RestoreTPL (OldTpl);
+  return Status;
+
+}
+
+/**
+  Verifies that all characters in a Unicode string can be output to the
+  target device.
+
+  Implements SIMPLE_TEXT_OUTPUT.TestString().
+  If one of the characters in the *Wstring is neither valid valid Unicode
+  drawing characters, not ASCII code, then this function will return
+  EFI_UNSUPPORTED
+
+  @param  This    Protocol instance pointer.
+  @param  WString The NULL-terminated Unicode string to be examined for the output
+                  device(s).
+
+  @retval EFI_SUCCESS      The device(s) are capable of rendering the output string.
+  @retval EFI_UNSUPPORTED  Some of the characters in the Unicode string cannot be
+                           rendered by one or more of the output devices mapped
+                           by the EFI handle.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutTestString (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  CHAR16                           *WString
+  )
+{
+  EFI_STATUS            Status;
+  UINT16                Count;
+
+  EFI_IMAGE_OUTPUT      *Blt;
+
+  Blt   = NULL;
+  Count = 0;
+
+  while (WString[Count] != 0) {
+    Status = mHiiFont->GetGlyph (
+                         mHiiFont,
+                         WString[Count],
+                         NULL,
+                         &Blt,
+                         NULL
+                         );
+    if (Blt != NULL) {
+      FreePool (Blt);
+      Blt = NULL;
+    }
+    Count++;
+
+    if (EFI_ERROR (Status)) {
+      return EFI_UNSUPPORTED;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Returns information for an available text mode that the output device(s)
+  supports
+
+  Implements SIMPLE_TEXT_OUTPUT.QueryMode().
+  It returnes information for an available text mode that the Graphics Console supports.
+  In this driver,we only support text mode 80x25, which is defined as mode 0.
+
+  @param  This                  Protocol instance pointer.
+  @param  ModeNumber            The mode number to return information on.
+  @param  Columns               The returned columns of the requested mode.
+  @param  Rows                  The returned rows of the requested mode.
+
+  @retval EFI_SUCCESS           The requested mode information is returned.
+  @retval EFI_UNSUPPORTED       The mode number is not valid.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutQueryMode (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  UINTN                            ModeNumber,
+  OUT UINTN                            *Columns,
+  OUT UINTN                            *Rows
+  )
+{
+  GRAPHICS_CONSOLE_DEV  *Private;
+  EFI_STATUS            Status;
+  EFI_TPL               OldTpl;
+
+  if (ModeNumber >= (UINTN) This->Mode->MaxMode) {
+    return EFI_UNSUPPORTED;
+  }
+
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+  Status = EFI_SUCCESS;
+
+  Private   = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (This);
+
+  *Columns  = Private->ModeData[ModeNumber].Columns;
+  *Rows     = Private->ModeData[ModeNumber].Rows;
+
+  if (*Columns <= 0 || *Rows <= 0) {
+    Status = EFI_UNSUPPORTED;
+    goto Done;
+
+  }
+
+Done:
+  gBS->RestoreTPL (OldTpl);
+  return Status;
+}
+
+
+/**
+  Sets the output device(s) to a specified mode.
+
+  Implements SIMPLE_TEXT_OUTPUT.SetMode().
+  Set the Graphics Console to a specified mode. In this driver, we only support mode 0.
+
+  @param  This                  Protocol instance pointer.
+  @param  ModeNumber            The text mode to set.
+
+  @retval EFI_SUCCESS           The requested text mode is set.
+  @retval EFI_DEVICE_ERROR      The requested text mode cannot be set because of
+                                Graphics Console device error.
+  @retval EFI_UNSUPPORTED       The text mode number is not valid.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutSetMode (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  UINTN                            ModeNumber
+  )
+{
+  EFI_STATUS                      Status;
+  GRAPHICS_CONSOLE_DEV            *Private;
+  GRAPHICS_CONSOLE_MODE_DATA      *ModeData;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL   *NewLineBuffer;
+  EFI_GRAPHICS_OUTPUT_PROTOCOL    *GraphicsOutput;
+  EFI_TPL                         OldTpl;
+
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+
+  Private   = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (This);
+  GraphicsOutput = Private->GraphicsOutput;
+
+  //
+  // Make sure the requested mode number is supported
+  //
+  if (ModeNumber >= (UINTN) This->Mode->MaxMode) {
+    Status = EFI_UNSUPPORTED;
+    goto Done;
+  }
+
+  ModeData  = &(Private->ModeData[ModeNumber]);
+
+  if (ModeData->Columns <= 0 && ModeData->Rows <= 0) {
+    Status = EFI_UNSUPPORTED;
+    goto Done;
+  }
+
+  //
+  // If the mode has been set at least one other time, then LineBuffer will not be NULL
+  //
+  if (Private->LineBuffer != NULL) {
+    //
+    // If the new mode is the same as the old mode, then just return EFI_SUCCESS
+    //
+    if ((INT32) ModeNumber == This->Mode->Mode) {
+      //
+      // Clear the current text window on the current graphics console
+      //
+      This->ClearScreen (This);
+      Status = EFI_SUCCESS;
+      goto Done;
+    }
+    //
+    // Otherwise, the size of the text console and/or the GOP mode will
+    // be changed, so erase the cursor, and free the LineBuffer for the
+    // current mode
+    //
+    FlushCursor (This);
+
+    FreePool (Private->LineBuffer);
+  }
+
+  //
+  // Attempt to allocate a line buffer for the requested mode number
+  //
+  NewLineBuffer = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * ModeData->Columns * EFI_GLYPH_WIDTH * EFI_GLYPH_HEIGHT);
+
+  if (NewLineBuffer == NULL) {
+    //
+    // The new line buffer could not be allocated, so return an error.
+    // No changes to the state of the current console have been made, so the current console is still valid
+    //
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Done;
+  }
+
+  //
+  // Assign the current line buffer to the newly allocated line buffer
+  //
+  Private->LineBuffer = NewLineBuffer;
+
+  if (ModeData->GopModeNumber != GraphicsOutput->Mode->Mode) {
+    //
+    // Either no graphics mode is currently set, or it is set
+    // to the wrong resolution, so set the new graphics mode
+    //
+    Status = GraphicsOutput->SetMode (GraphicsOutput, ModeData->GopModeNumber);
+    if (EFI_ERROR (Status)) {
+      //
+      // The mode set operation failed
+      //
+      goto Done;
+    }
+  } else {
+    //
+    // The current graphics mode is correct, so simply clear the entire display
+    //
+    Status = GraphicsOutput->Blt (
+                                  GraphicsOutput,
+                                  &mGraphicsEfiColors[0],
+                                  EfiBltVideoFill,
+                                  0,
+                                  0,
+                                  0,
+                                  0,
+                                  ModeData->GopWidth,
+                                  ModeData->GopHeight,
+                                  0
+                                  );
+  }
+
+  //
+  // The new mode is valid, so commit the mode change
+  //
+  This->Mode->Mode = (INT32) ModeNumber;
+
+  //
+  // Move the text cursor to the upper left hand corner of the display and flush it
+  //
+  This->Mode->CursorColumn  = 0;
+  This->Mode->CursorRow     = 0;
+
+  FlushCursor (This);
+
+  Status = EFI_SUCCESS;
+
+Done:
+  gBS->RestoreTPL (OldTpl);
+  return Status;
+}
+
+
+/**
+  Sets the background and foreground colors for the OutputString () and
+  ClearScreen () functions.
+
+  Implements SIMPLE_TEXT_OUTPUT.SetAttribute().
+
+  @param  This                  Protocol instance pointer.
+  @param  Attribute             The attribute to set. Bits 0..3 are the foreground
+                                color, and bits 4..6 are the background color.
+                                All other bits are undefined and must be zero.
+
+  @retval EFI_SUCCESS           The requested attribute is set.
+  @retval EFI_DEVICE_ERROR      The requested attribute cannot be set due to Graphics Console port error.
+  @retval EFI_UNSUPPORTED       The attribute requested is not defined.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutSetAttribute (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  UINTN                            Attribute
+  )
+{
+  EFI_TPL               OldTpl;
+
+  if ((Attribute | 0x7F) != 0x7F) {
+    return EFI_UNSUPPORTED;
+  }
+
+  if ((INT32) Attribute == This->Mode->Attribute) {
+    return EFI_SUCCESS;
+  }
+
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+
+  FlushCursor (This);
+
+  This->Mode->Attribute = (INT32) Attribute;
+
+  FlushCursor (This);
+
+  gBS->RestoreTPL (OldTpl);
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Clears the output device(s) display to the currently selected background
+  color.
+
+  Implements SIMPLE_TEXT_OUTPUT.ClearScreen().
+
+  @param  This                  Protocol instance pointer.
+
+  @retval  EFI_SUCCESS      The operation completed successfully.
+  @retval  EFI_DEVICE_ERROR The device had an error and could not complete the request.
+  @retval  EFI_UNSUPPORTED  The output device is not in a valid text mode.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutClearScreen (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This
+  )
+{
+  EFI_STATUS                    Status;
+  GRAPHICS_CONSOLE_DEV          *Private;
+  GRAPHICS_CONSOLE_MODE_DATA    *ModeData;
+  EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL Foreground;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background;
+  EFI_TPL                       OldTpl;
+
+  if (This->Mode->Mode == -1) {
+    //
+    // If current mode is not valid, return error.
+    //
+    return EFI_UNSUPPORTED;
+  }
+
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+
+  Private   = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (This);
+  GraphicsOutput = Private->GraphicsOutput;
+  ModeData  = &(Private->ModeData[This->Mode->Mode]);
+
+  GetTextColors (This, &Foreground, &Background);
+  Status = GraphicsOutput->Blt (
+                                GraphicsOutput,
+                                &Background,
+                                EfiBltVideoFill,
+                                0,
+                                0,
+                                0,
+                                0,
+                                ModeData->GopWidth,
+                                ModeData->GopHeight,
+                                0
+                                );
+
+  This->Mode->CursorColumn  = 0;
+  This->Mode->CursorRow     = 0;
+
+  FlushCursor (This);
+
+  gBS->RestoreTPL (OldTpl);
+
+  return Status;
+}
+
+
+/**
+  Sets the current coordinates of the cursor position.
+
+  Implements SIMPLE_TEXT_OUTPUT.SetCursorPosition().
+
+  @param  This        Protocol instance pointer.
+  @param  Column      The position to set the cursor to. Must be greater than or
+                      equal to zero and less than the number of columns and rows
+                      by QueryMode ().
+  @param  Row         The position to set the cursor to. Must be greater than or
+                      equal to zero and less than the number of columns and rows
+                      by QueryMode ().
+
+  @retval EFI_SUCCESS      The operation completed successfully.
+  @retval EFI_DEVICE_ERROR The device had an error and could not complete the request.
+  @retval EFI_UNSUPPORTED  The output device is not in a valid text mode, or the
+                           cursor position is invalid for the current mode.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutSetCursorPosition (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  UINTN                            Column,
+  IN  UINTN                            Row
+  )
+{
+  GRAPHICS_CONSOLE_DEV        *Private;
+  GRAPHICS_CONSOLE_MODE_DATA  *ModeData;
+  EFI_STATUS                  Status;
+  EFI_TPL                     OldTpl;
+
+  if (This->Mode->Mode == -1) {
+    //
+    // If current mode is not valid, return error.
+    //
+    return EFI_UNSUPPORTED;
+  }
+
+  Status = EFI_SUCCESS;
+
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+
+  Private   = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (This);
+  ModeData  = &(Private->ModeData[This->Mode->Mode]);
+
+  if ((Column >= ModeData->Columns) || (Row >= ModeData->Rows)) {
+    Status = EFI_UNSUPPORTED;
+    goto Done;
+  }
+
+  if ((This->Mode->CursorColumn == (INT32) Column) && (This->Mode->CursorRow == (INT32) Row)) {
+    Status = EFI_SUCCESS;
+    goto Done;
+  }
+
+  FlushCursor (This);
+
+  This->Mode->CursorColumn  = (INT32) Column;
+  This->Mode->CursorRow     = (INT32) Row;
+
+  FlushCursor (This);
+
+Done:
+  gBS->RestoreTPL (OldTpl);
+
+  return Status;
+}
+
+
+/**
+  Makes the cursor visible or invisible.
+
+  Implements SIMPLE_TEXT_OUTPUT.EnableCursor().
+
+  @param  This                  Protocol instance pointer.
+  @param  Visible               If TRUE, the cursor is set to be visible, If FALSE,
+                                the cursor is set to be invisible.
+
+  @retval EFI_SUCCESS           The operation completed successfully.
+  @retval EFI_UNSUPPORTED       The output device's mode is not currently in a
+                                defined text mode.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutEnableCursor (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  BOOLEAN                          Visible
+  )
+{
+  EFI_TPL               OldTpl;
+
+  if (This->Mode->Mode == -1) {
+    //
+    // If current mode is not valid, return error.
+    //
+    return EFI_UNSUPPORTED;
+  }
+
+  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
+
+  FlushCursor (This);
+
+  This->Mode->CursorVisible = Visible;
+
+  FlushCursor (This);
+
+  gBS->RestoreTPL (OldTpl);
+  return EFI_SUCCESS;
+}
+
+/**
+  Gets Graphics Console devcie's foreground color and background color.
+
+  @param  This                  Protocol instance pointer.
+  @param  Foreground            Returned text foreground color.
+  @param  Background            Returned text background color.
+
+  @retval EFI_SUCCESS           It returned always.
+
+**/
+EFI_STATUS
+GetTextColors (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL    *Foreground,
+  OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL    *Background
+  )
+{
+  INTN  Attribute;
+
+  Attribute   = This->Mode->Attribute & 0x7F;
+
+  *Foreground = mGraphicsEfiColors[Attribute & 0x0f];
+  *Background = mGraphicsEfiColors[Attribute >> 4];
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Draw Unicode string on the Graphics Console device's screen.
+
+  @param  This                  Protocol instance pointer.
+  @param  UnicodeWeight         One Unicode string to be displayed.
+  @param  Count                 The count of Unicode string.
+
+  @retval EFI_OUT_OF_RESOURCES  If no memory resource to use.
+  @retval EFI_UNSUPPORTED       If no Graphics Output Protocol exists.
+  @retval EFI_SUCCESS           Drawing Unicode string implemented successfully.
+
+**/
+EFI_STATUS
+DrawUnicodeWeightAtCursorN (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  CHAR16                           *UnicodeWeight,
+  IN  UINTN                            Count
+  )
+{
+  EFI_STATUS                        Status;
+  GRAPHICS_CONSOLE_DEV              *Private;
+  EFI_IMAGE_OUTPUT                  *Blt;
+  EFI_STRING                        String;
+  EFI_FONT_DISPLAY_INFO             *FontInfo;
+
+  Private = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (This);
+  Blt = (EFI_IMAGE_OUTPUT *) AllocateZeroPool (sizeof (EFI_IMAGE_OUTPUT));
+  if (Blt == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Blt->Width        = (UINT16) (Private->ModeData[This->Mode->Mode].GopWidth);
+  Blt->Height       = (UINT16) (Private->ModeData[This->Mode->Mode].GopHeight);
+
+  String = AllocateCopyPool ((Count + 1) * sizeof (CHAR16), UnicodeWeight);
+  if (String == NULL) {
+    FreePool (Blt);
+    return EFI_OUT_OF_RESOURCES;
+  }
+  //
+  // Set the end character
+  //
+  *(String + Count) = L'\0';
+
+  FontInfo = (EFI_FONT_DISPLAY_INFO *) AllocateZeroPool (sizeof (EFI_FONT_DISPLAY_INFO));
+  if (FontInfo == NULL) {
+    FreePool (Blt);
+    FreePool (String);
+    return EFI_OUT_OF_RESOURCES;
+  }
+  //
+  // Get current foreground and background colors.
+  //
+  GetTextColors (This, &FontInfo->ForegroundColor, &FontInfo->BackgroundColor);
+
+  //
+  // If Graphics Output protocol exists, using HII Font protocol to draw.
+  //
+  Blt->Image.Screen = Private->GraphicsOutput;
+
+  Status = mHiiFont->StringToImage (
+                                    mHiiFont,
+                                    EFI_HII_IGNORE_IF_NO_GLYPH | EFI_HII_DIRECT_TO_SCREEN | EFI_HII_IGNORE_LINE_BREAK,
+                                    String,
+                                    FontInfo,
+                                    &Blt,
+                                    This->Mode->CursorColumn * EFI_GLYPH_WIDTH + Private->ModeData[This->Mode->Mode].DeltaX,
+                                    This->Mode->CursorRow * EFI_GLYPH_HEIGHT + Private->ModeData[This->Mode->Mode].DeltaY,
+                                    NULL,
+                                    NULL,
+                                    NULL
+                                    );
+
+  if (Blt != NULL) {
+    FreePool (Blt);
+  }
+  if (String != NULL) {
+    FreePool (String);
+  }
+  if (FontInfo != NULL) {
+    FreePool (FontInfo);
+  }
+  return Status;
+}
+
+/**
+  Flush the cursor on the screen.
+
+  If CursorVisible is FALSE, nothing to do and return directly.
+  If CursorVisible is TRUE,
+     i) If the cursor shows on screen, it will be erased.
+    ii) If the cursor does not show on screen, it will be shown.
+
+  @param  This                  Protocol instance pointer.
+
+  @retval EFI_SUCCESS           The cursor is erased successfully.
+
+**/
+EFI_STATUS
+FlushCursor (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This
+  )
+{
+  GRAPHICS_CONSOLE_DEV                *Private;
+  EFI_SIMPLE_TEXT_OUTPUT_MODE         *CurrentMode;
+  INTN                                GlyphX;
+  INTN                                GlyphY;
+  EFI_GRAPHICS_OUTPUT_PROTOCOL        *GraphicsOutput;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Foreground;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Background;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION BltChar[EFI_GLYPH_HEIGHT][EFI_GLYPH_WIDTH];
+  UINTN                               PosX;
+  UINTN                               PosY;
+
+  CurrentMode = This->Mode;
+
+  if (!CurrentMode->CursorVisible) {
+    return EFI_SUCCESS;
+  }
+
+  Private = GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS (This);
+  GraphicsOutput = Private->GraphicsOutput;
+
+  //
+  // In this driver, only narrow character was supported.
+  //
+  //
+  // Blt a character to the screen
+  //
+  GlyphX  = (CurrentMode->CursorColumn * EFI_GLYPH_WIDTH) + Private->ModeData[CurrentMode->Mode].DeltaX;
+  GlyphY  = (CurrentMode->CursorRow * EFI_GLYPH_HEIGHT) + Private->ModeData[CurrentMode->Mode].DeltaY;
+
+  GraphicsOutput->Blt (
+                       GraphicsOutput,
+                       (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) BltChar,
+                       EfiBltVideoToBltBuffer,
+                       GlyphX,
+                       GlyphY,
+                       0,
+                       0,
+                       EFI_GLYPH_WIDTH,
+                       EFI_GLYPH_HEIGHT,
+                       EFI_GLYPH_WIDTH * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
+                       );
+
+  GetTextColors (This, &Foreground.Pixel, &Background.Pixel);
+
+  //
+  // Convert Monochrome bitmap of the Glyph to BltBuffer structure
+  //
+  for (PosY = 0; PosY < EFI_GLYPH_HEIGHT; PosY++) {
+    for (PosX = 0; PosX < EFI_GLYPH_WIDTH; PosX++) {
+      if ((mCursorGlyph.GlyphCol1[PosY] & (BIT0 << PosX)) != 0) {
+        BltChar[PosY][EFI_GLYPH_WIDTH - PosX - 1].Raw ^= Foreground.Raw;
+      }
+    }
+  }
+
+  GraphicsOutput->Blt (
+                       GraphicsOutput,
+                       (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) BltChar,
+                       EfiBltBufferToVideo,
+                       0,
+                       0,
+                       GlyphX,
+                       GlyphY,
+                       EFI_GLYPH_WIDTH,
+                       EFI_GLYPH_HEIGHT,
+                       EFI_GLYPH_WIDTH * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
+                       );
+
+  return EFI_SUCCESS;
+}
+
+/**
+  HII Database Protocol notification event handler.
+
+  Register font package when HII Database Protocol has been installed.
+
+  @param[in] Event    Event whose notification function is being invoked.
+  @param[in] Context  Pointer to the notification function's context.
+**/
+VOID
+EFIAPI
+RegisterFontPackage (
+  IN  EFI_EVENT       Event,
+  IN  VOID            *Context
+  )
+{
+  EFI_STATUS                           Status;
+  EFI_HII_SIMPLE_FONT_PACKAGE_HDR      *SimplifiedFont;
+  UINT32                               PackageLength;
+  UINT8                                *Package;
+  UINT8                                *Location;
+  EFI_HII_DATABASE_PROTOCOL            *HiiDatabase;
+
+  //
+  // Locate HII Database Protocol
+  //
+  Status = gBS->LocateProtocol (
+                  &gEfiHiiDatabaseProtocolGuid,
+                  NULL,
+                  (VOID **) &HiiDatabase
+                  );
+  if (EFI_ERROR (Status)) {
+    return;
+  }
+
+  //
+  // Add 4 bytes to the header for entire length for HiiAddPackages use only.
+  //
+  //    +--------------------------------+ <-- Package
+  //    |                                |
+  //    |    PackageLength(4 bytes)      |
+  //    |                                |
+  //    |--------------------------------| <-- SimplifiedFont
+  //    |                                |
+  //    |EFI_HII_SIMPLE_FONT_PACKAGE_HDR |
+  //    |                                |
+  //    |--------------------------------| <-- Location
+  //    |                                |
+  //    |     gUsStdNarrowGlyphData      |
+  //    |                                |
+  //    +--------------------------------+
+
+  PackageLength   = sizeof (EFI_HII_SIMPLE_FONT_PACKAGE_HDR) + mNarrowFontSize + 4;
+  Package = AllocateZeroPool (PackageLength);
+  ASSERT (Package != NULL);
+
+  WriteUnaligned32((UINT32 *) Package,PackageLength);
+  SimplifiedFont = (EFI_HII_SIMPLE_FONT_PACKAGE_HDR *) (Package + 4);
+  SimplifiedFont->Header.Length        = (UINT32) (PackageLength - 4);
+  SimplifiedFont->Header.Type          = EFI_HII_PACKAGE_SIMPLE_FONTS;
+  SimplifiedFont->NumberOfNarrowGlyphs = (UINT16) (mNarrowFontSize / sizeof (EFI_NARROW_GLYPH));
+
+  Location = (UINT8 *) (&SimplifiedFont->NumberOfWideGlyphs + 1);
+  CopyMem (Location, gUsStdNarrowGlyphData, mNarrowFontSize);
+
+  //
+  // Add this simplified font package to a package list then install it.
+  //
+  mHiiHandle = HiiAddPackages (
+                 &mFontPackageListGuid,
+                 NULL,
+                 Package,
+                 NULL
+                 );
+  ASSERT (mHiiHandle != NULL);
+  FreePool (Package);
+}
+
+/**
+  The user Entry Point for module GraphicsConsole. The user code starts with this function.
+
+  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
+  @param[in] SystemTable    A pointer to the EFI System Table.
+
+  @retval  EFI_SUCCESS       The entry point is executed successfully.
+  @return  other             Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeGraphicsConsole (
+  IN EFI_HANDLE           ImageHandle,
+  IN EFI_SYSTEM_TABLE     *SystemTable
+  )
+{
+  EFI_STATUS              Status;
+
+  //
+  // Register notify function on HII Database Protocol to add font package.
+  //
+  EfiCreateProtocolNotifyEvent (
+    &gEfiHiiDatabaseProtocolGuid,
+    TPL_CALLBACK,
+    RegisterFontPackage,
+    NULL,
+    &mHiiRegistration
+    );
+
+  //
+  // Install driver model protocol(s).
+  //
+  Status = EfiLibInstallDriverBindingComponentName2 (
+             ImageHandle,
+             SystemTable,
+             &gGraphicsConsoleDriverBinding,
+             ImageHandle,
+             &gGraphicsConsoleComponentName,
+             &gGraphicsConsoleComponentName2
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsole.h b/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsole.h
new file mode 100644
index 000000000000..ba93cab86bf3
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsole.h
@@ -0,0 +1,591 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2006-2016, Intel 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 _GRAPHICS_CONSOLE_H_
+#define _GRAPHICS_CONSOLE_H_
+
+#include <Uefi.h>
+#include <Protocol/ExtendedTextOut.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/HiiFont.h>
+#include <Protocol/HiiDatabase.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/HiiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/PcdLib.h>
+#include <Guid/MdeModuleHii.h>
+
+extern EFI_COMPONENT_NAME_PROTOCOL   gGraphicsConsoleComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL  gGraphicsConsoleComponentName2;
+extern EFI_DRIVER_BINDING_PROTOCOL   gGraphicsConsoleDriverBinding;
+extern EFI_NARROW_GLYPH              gUsStdNarrowGlyphData[];
+
+extern UINT32 mNarrowFontSize;
+
+typedef union {
+  EFI_NARROW_GLYPH  NarrowGlyph;
+  EFI_WIDE_GLYPH    WideGlyph;
+} GLYPH_UNION;
+
+//
+// Device Structure
+//
+#define GRAPHICS_CONSOLE_DEV_SIGNATURE  SIGNATURE_32 ('g', 's', 't', 'o')
+
+typedef struct {
+  UINTN   Columns;
+  UINTN   Rows;
+  INTN    DeltaX;
+  INTN    DeltaY;
+  UINT32  GopWidth;
+  UINT32  GopHeight;
+  UINT32  GopModeNumber;
+} GRAPHICS_CONSOLE_MODE_DATA;
+
+typedef struct {
+  UINTN                            Signature;
+  EFI_GRAPHICS_OUTPUT_PROTOCOL     *GraphicsOutput;
+  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  SimpleTextOutput;
+  EFI_SIMPLE_TEXT_OUTPUT_MODE      SimpleTextOutputMode;
+  GRAPHICS_CONSOLE_MODE_DATA       *ModeData;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL    *LineBuffer;
+  EXTENDED_TEXT_OUTPUT_PROTOCOL    ExtendedTextOutput;
+} GRAPHICS_CONSOLE_DEV;
+
+#define GRAPHICS_CONSOLE_CON_OUT_DEV_FROM_THIS(a) \
+  CR (a, GRAPHICS_CONSOLE_DEV, SimpleTextOutput, GRAPHICS_CONSOLE_DEV_SIGNATURE)
+
+
+//
+// EFI Component Name Functions
+//
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  );
+
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  );
+
+
+/**
+  Reset the text output device hardware and optionally run diagnostics.
+
+  Implements SIMPLE_TEXT_OUTPUT.Reset().
+  If ExtendeVerification is TRUE, then perform dependent Graphics Console
+  device reset, and set display mode to mode 0.
+  If ExtendedVerification is FALSE, only set display mode to mode 0.
+
+  @param  This                  Protocol instance pointer.
+  @param  ExtendedVerification  Indicates that the driver may perform a more
+                                exhaustive verification operation of the device
+                                during reset.
+
+  @retval EFI_SUCCESS          The text output device was reset.
+  @retval EFI_DEVICE_ERROR     The text output device is not functioning correctly and
+                               could not be reset.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutReset (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL    *This,
+  IN  BOOLEAN                            ExtendedVerification
+  );
+
+/**
+  Write a Unicode string to the output device.
+
+  Implements SIMPLE_TEXT_OUTPUT.OutputString().
+  The Unicode string will be converted to Glyphs and will be
+  sent to the Graphics Console.
+
+  @param  This                    Protocol instance pointer.
+  @param  WString                 The NULL-terminated Unicode string to be displayed
+                                  on the output device(s). All output devices must
+                                  also support the Unicode drawing defined in this file.
+
+  @retval EFI_SUCCESS             The string was output to the device.
+  @retval EFI_DEVICE_ERROR        The device reported an error while attempting to output
+                                  the text.
+  @retval EFI_UNSUPPORTED         The output device's mode is not currently in a
+                                  defined text mode.
+  @retval EFI_WARN_UNKNOWN_GLYPH  This warning code indicates that some of the
+                                  characters in the Unicode string could not be
+                                  rendered and were skipped.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutOutputString (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  CHAR16                           *WString
+  );
+
+/**
+  Verifies that all characters in a Unicode string can be output to the
+  target device.
+
+  Implements SIMPLE_TEXT_OUTPUT.TestString().
+  If one of the characters in the *Wstring is neither valid valid Unicode
+  drawing characters, not ASCII code, then this function will return
+  EFI_UNSUPPORTED
+
+  @param  This    Protocol instance pointer.
+  @param  WString The NULL-terminated Unicode string to be examined for the output
+                  device(s).
+
+  @retval EFI_SUCCESS      The device(s) are capable of rendering the output string.
+  @retval EFI_UNSUPPORTED  Some of the characters in the Unicode string cannot be
+                           rendered by one or more of the output devices mapped
+                           by the EFI handle.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutTestString (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  CHAR16                           *WString
+  );
+
+/**
+  Returns information for an available text mode that the output device(s)
+  supports
+
+  Implements SIMPLE_TEXT_OUTPUT.QueryMode().
+  It returnes information for an available text mode that the Graphics Console supports.
+  In this driver,we only support text mode 80x25, which is defined as mode 0.
+
+  @param  This                  Protocol instance pointer.
+  @param  ModeNumber            The mode number to return information on.
+  @param  Columns               The returned columns of the requested mode.
+  @param  Rows                  The returned rows of the requested mode.
+
+  @retval EFI_SUCCESS           The requested mode information is returned.
+  @retval EFI_UNSUPPORTED       The mode number is not valid.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutQueryMode (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  UINTN                            ModeNumber,
+  OUT UINTN                            *Columns,
+  OUT UINTN                            *Rows
+  );
+
+
+/**
+  Sets the output device(s) to a specified mode.
+
+  Implements SIMPLE_TEXT_OUTPUT.SetMode().
+  Set the Graphics Console to a specified mode. In this driver, we only support mode 0.
+
+  @param  This                  Protocol instance pointer.
+  @param  ModeNumber            The text mode to set.
+
+  @retval EFI_SUCCESS           The requested text mode is set.
+  @retval EFI_DEVICE_ERROR      The requested text mode cannot be set because of
+                                Graphics Console device error.
+  @retval EFI_UNSUPPORTED       The text mode number is not valid.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutSetMode (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  UINTN                            ModeNumber
+  );
+
+/**
+  Sets the background and foreground colors for the OutputString () and
+  ClearScreen () functions.
+
+  Implements SIMPLE_TEXT_OUTPUT.SetAttribute().
+
+  @param  This                  Protocol instance pointer.
+  @param  Attribute             The attribute to set. Bits 0..3 are the foreground
+                                color, and bits 4..6 are the background color.
+                                All other bits are undefined and must be zero.
+
+  @retval EFI_SUCCESS           The requested attribute is set.
+  @retval EFI_DEVICE_ERROR      The requested attribute cannot be set due to Graphics Console port error.
+  @retval EFI_UNSUPPORTED       The attribute requested is not defined.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutSetAttribute (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  UINTN                            Attribute
+  );
+
+/**
+  Clears the output device(s) display to the currently selected background
+  color.
+
+  Implements SIMPLE_TEXT_OUTPUT.ClearScreen().
+
+  @param  This                  Protocol instance pointer.
+
+  @retval  EFI_SUCCESS      The operation completed successfully.
+  @retval  EFI_DEVICE_ERROR The device had an error and could not complete the request.
+  @retval  EFI_UNSUPPORTED  The output device is not in a valid text mode.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutClearScreen (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This
+  );
+
+/**
+  Sets the current coordinates of the cursor position.
+
+  Implements SIMPLE_TEXT_OUTPUT.SetCursorPosition().
+
+  @param  This        Protocol instance pointer.
+  @param  Column      The position to set the cursor to. Must be greater than or
+                      equal to zero and less than the number of columns and rows
+                      by QueryMode ().
+  @param  Row         The position to set the cursor to. Must be greater than or
+                      equal to zero and less than the number of columns and rows
+                      by QueryMode ().
+
+  @retval EFI_SUCCESS      The operation completed successfully.
+  @retval EFI_DEVICE_ERROR The device had an error and could not complete the request.
+  @retval EFI_UNSUPPORTED  The output device is not in a valid text mode, or the
+                           cursor position is invalid for the current mode.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutSetCursorPosition (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  UINTN                            Column,
+  IN  UINTN                            Row
+  );
+
+
+/**
+  Makes the cursor visible or invisible.
+
+  Implements SIMPLE_TEXT_OUTPUT.EnableCursor().
+
+  @param  This                  Protocol instance pointer.
+  @param  Visible               If TRUE, the cursor is set to be visible, If FALSE,
+                                the cursor is set to be invisible.
+
+  @retval EFI_SUCCESS           The operation completed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleConOutEnableCursor (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  BOOLEAN                          Visible
+  );
+
+/**
+  Test to see if Graphics Console could be supported on the Controller.
+
+  Graphics Console could be supported if Graphics Output Protocol exists
+  on the Controller.
+
+  @param  This                Protocol instance pointer.
+  @param  Controller          Handle of device to test.
+  @param  RemainingDevicePath Optional parameter use to pick a specific child
+                              device to start.
+
+  @retval EFI_SUCCESS         This driver supports this device.
+  @retval other               This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleControllerDriverSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  );
+
+
+/**
+  Start this driver on Controller by opening the Graphics Output Protocol,
+  and installing Simple Text Out protocol on Controller.
+
+  @param  This                 Protocol instance pointer.
+  @param  Controller           Handle of device to bind driver to
+  @param  RemainingDevicePath  Optional parameter use to pick a specific child
+                               device to start.
+
+  @retval EFI_SUCCESS          This driver is added to Controller.
+  @retval other                This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleControllerDriverStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  );
+
+/**
+  Stop this driver on Controller by removing Simple Text Out protocol
+  and closing the Graphics Output Protocol on Controller.
+
+  @param  This              Protocol instance pointer.
+  @param  Controller        Handle of device to stop driver on
+  @param  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
+                            children is zero stop the entire bus driver.
+  @param  ChildHandleBuffer List of Child Handles to Stop.
+
+  @retval EFI_SUCCESS       This driver is removed Controller.
+  @retval EFI_NOT_STARTED   Simple Text Out protocol could not be found the
+                            Controller.
+  @retval other             This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+GraphicsConsoleControllerDriverStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN  EFI_HANDLE                     Controller,
+  IN  UINTN                          NumberOfChildren,
+  IN  EFI_HANDLE                     *ChildHandleBuffer
+  );
+
+
+/**
+  Locate HII Database protocol and HII Font protocol.
+
+  @retval  EFI_SUCCESS     HII Database protocol and HII Font protocol
+                           are located successfully.
+  @return  other           Failed to locate HII Database protocol or
+                           HII Font protocol.
+
+**/
+EFI_STATUS
+EfiLocateHiiProtocol (
+  VOID
+  );
+
+
+/**
+  Gets Graphics Console devcie's foreground color and background color.
+
+  @param  This                  Protocol instance pointer.
+  @param  Foreground            Returned text foreground color.
+  @param  Background            Returned text background color.
+
+  @retval EFI_SUCCESS           It returned always.
+
+**/
+EFI_STATUS
+GetTextColors (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL    *Foreground,
+  OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL    *Background
+  );
+
+/**
+  Draw Unicode string on the Graphics Console device's screen.
+
+  @param  This                  Protocol instance pointer.
+  @param  UnicodeWeight         One Unicode string to be displayed.
+  @param  Count                 The count of Unicode string.
+
+  @retval EFI_OUT_OF_RESOURCES  If no memory resource to use.
+  @retval EFI_UNSUPPORTED       If no Graphics Output Protocol exists.
+  @retval EFI_SUCCESS           Drawing Unicode string implemented successfully.
+
+**/
+EFI_STATUS
+DrawUnicodeWeightAtCursorN (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
+  IN  CHAR16                           *UnicodeWeight,
+  IN  UINTN                            Count
+  );
+
+/**
+  Flush the cursor on the screen.
+
+  If CursorVisible is FALSE, nothing to do and return directly.
+  If CursorVisible is TRUE,
+     i) If the cursor shows on screen, it will be erased.
+    ii) If the cursor does not show on screen, it will be shown.
+
+  @param  This                  Protocol instance pointer.
+
+  @retval EFI_SUCCESS           The cursor is erased successfully.
+
+**/
+EFI_STATUS
+FlushCursor (
+  IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This
+  );
+
+/**
+  Check if the current specific mode supported the user defined resolution
+  for the Graphics Console device based on Graphics Output Protocol.
+
+  If yes, set the graphic device's current mode to this specific mode.
+
+  @param  GraphicsOutput        Graphics Output Protocol instance pointer.
+  @param  HorizontalResolution  User defined horizontal resolution
+  @param  VerticalResolution    User defined vertical resolution.
+  @param  CurrentModeNumber     Current specific mode to be check.
+
+  @retval EFI_SUCCESS       The mode is supported.
+  @retval EFI_UNSUPPORTED   The specific mode is out of range of graphics
+                            device supported.
+  @retval other             The specific mode does not support user defined
+                            resolution or failed to set the current mode to the
+                            specific mode on graphics device.
+
+**/
+EFI_STATUS
+CheckModeSupported (
+  EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput,
+  IN  UINT32  HorizontalResolution,
+  IN  UINT32  VerticalResolution,
+  OUT UINT32  *CurrentModeNumber
+  );
+
+#endif
diff --git a/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsoleDxe.inf b/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsoleDxe.inf
new file mode 100644
index 000000000000..cf359b414682
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsoleDxe.inf
@@ -0,0 +1,74 @@
+#/** @file
+#
+#  Copyright (c) 2006-2014, Intel Corporation. All rights reserved.
+#  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+#
+#  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                    = 0x00010005
+  BASE_NAME                      = GraphicsConsoleDxe
+  MODULE_UNI_FILE                = GraphicsConsoleDxe.uni
+  FILE_GUID                      = CCCB0C28-4B24-11d5-9A5A-0090273FC14D
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.1
+  ENTRY_POINT                    = InitializeGraphicsConsole
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC AARCH64
+#
+#  DRIVER_BINDING                =  gGraphicsConsoleDriverBinding
+#  COMPONENT_NAME                =  gGraphicsConsoleComponentName
+#  COMPONENT_NAME2               =  gGraphicsConsoleComponentName2
+#
+
+[Sources]
+  ComponentName.c
+  NewFont.c
+  GraphicsConsole.c
+  GraphicsConsole.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+
+[LibraryClasses]
+  UefiBootServicesTableLib
+  UefiRuntimeServicesTableLib
+  MemoryAllocationLib
+  BmpSupportLib
+  BaseMemoryLib
+  UefiLib
+  UefiDriverEntryPoint
+  DebugLib
+  HiiLib
+  PcdLib
+
+[Protocols]
+  gEfiDevicePathProtocolGuid
+  gEfiSimpleTextOutProtocolGuid
+  gEfiGraphicsOutputProtocolGuid
+  gEfiHiiFontProtocolGuid
+  gEfiHiiDatabaseProtocolGuid
+  gExtendedTextOutputProtocolGuid
+
+[FeaturePcd]
+
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdVideoHorizontalResolution ## SOMETIMES_CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdVideoVerticalResolution   ## SOMETIMES_CONSUMES
+
+[UserExtensions.TianoCore."ExtraFiles"]
+  GraphicsConsoleDxeExtra.uni
diff --git a/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsoleDxe.uni b/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsoleDxe.uni
new file mode 100644
index 000000000000..09336b5cae36
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsoleDxe.uni
@@ -0,0 +1,19 @@
+/** @file
+ *
+ *  Copyright (c) 2006-2014, Intel Corporation. All rights reserved.
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *
+ *  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.
+ *
+ **/
+
+#string STR_MODULE_ABSTRACT             #language en-US "Console support on graphic devices"
+
+#string STR_MODULE_DESCRIPTION          #language en-US "Install SimpleTextOutputProtocol on GraphicsOutputProtocol devices\n"
+
diff --git a/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsoleDxeExtra.uni b/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsoleDxeExtra.uni
new file mode 100644
index 000000000000..cb8a6e0dbb5e
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsoleDxeExtra.uni
@@ -0,0 +1,20 @@
+/** @file
+ *
+ *  Copyright (c) 2006-2014, Intel Corporation. All rights reserved.
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *
+ *  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.
+ *
+ **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"Graphics Console DXE Driver"
+
+
diff --git a/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/NewFont.c b/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/NewFont.c
new file mode 100644
index 000000000000..6331b89b10dd
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/NewFont.c
@@ -0,0 +1,288 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *
+ *  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.
+ *
+ **/
+
+/*
+ * Based on ftp://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/PC/CP437.TXT and
+ * https://en.wikipedia.org/wiki/Code_page_437 for the cp437 Unicode equivalents
+ * and the cp437 8x19 font from that_editor.
+ *
+ * https://github.com/bisqwit/that_editor/blob/master/8x19.inc
+ * https://github.com/bisqwit/that_editor/blob/master/sourcematerial/vga8x19.bdf
+ * https://github.com/stsp/dosemu2/blob/master/COPYING.DOSEMU
+ */
+
+#include "GraphicsConsole.h"
+
+EFI_NARROW_GLYPH  gUsStdNarrowGlyphData[] = {
+  { 0x263a, 0x00, {0x00,0x00,0x7E,0x81,0xA5,0xA5,0x81,0x81,0xA5,0xA5,0x99,0x81,0x81,0x7E,0x00,0x00,0x00,0x00,0x00}},
+  { 0x263b, 0x00, {0x00,0x00,0x7E,0xFF,0xDB,0xFF,0xFF,0xFF,0xDB,0xE7,0xFF,0xFF,0xFF,0x7E,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2665, 0x00, {0x00,0x00,0x00,0x00,0x00,0x6C,0xFE,0xFE,0xFE,0xFE,0x7C,0x38,0x10,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2666, 0x00, {0x00,0x00,0x00,0x00,0x00,0x10,0x38,0x7C,0xFE,0xFE,0x7C,0x38,0x10,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2663, 0x00, {0x00,0x00,0x00,0x18,0x3C,0x3C,0xFF,0xE7,0xE7,0xE7,0xFF,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2660, 0x00, {0x00,0x00,0x00,0x18,0x3C,0x7E,0xFF,0xFF,0xFF,0x7E,0x7E,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2022, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x25d8, 0x00, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE7,0xC3,0xC3,0xE7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}},
+  { 0x25cb, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x66,0x66,0x42,0x66,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x25d9, 0x00, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xDB,0x99,0xBD,0xBD,0x99,0xDB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}},
+  { 0x2642, 0x00, {0x00,0x00,0x1E,0x06,0x0E,0x0A,0x1A,0x78,0xCC,0xCC,0xCC,0xCC,0xCC,0x78,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2640, 0x00, {0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0x00,0x00,0x00}},
+  { 0x266a, 0x00, {0x00,0x00,0x3F,0x33,0x33,0x3F,0x30,0x30,0x30,0x30,0x30,0x70,0xF0,0xE0,0x00,0x00,0x00,0x00,0x00}},
+  { 0x266b, 0x00, {0x00,0x00,0x7F,0x63,0x63,0x7F,0x63,0x63,0x63,0x63,0x63,0x67,0xE7,0xE6,0xC0,0x00,0x00,0x00,0x00}},
+  { 0x263c, 0x00, {0x00,0x00,0x00,0x18,0x18,0xDB,0xFF,0x3C,0xE7,0x3C,0xFF,0xDB,0x18,0x18,0x00,0x00,0x00,0x00,0x00}},
+  { 0x25ba, 0x00, {0x00,0x80,0xC0,0xE0,0xF0,0xF8,0xFC,0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00,0x00}},
+  { 0x25c4, 0x00, {0x00,0x02,0x06,0x0E,0x1E,0x3E,0x7E,0xFE,0x7E,0x3E,0x1E,0x0E,0x06,0x02,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2195, 0x00, {0x00,0x00,0x18,0x3C,0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x3C,0x18,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x203c, 0x00, {0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x66,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00b6, 0x00, {0x00,0x00,0x7F,0xDB,0xDB,0xDB,0xDB,0x7B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00a7, 0x00, {0x00,0x00,0x7C,0xC6,0x62,0x30,0x78,0x4C,0x64,0x3C,0x18,0x8C,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x25ac, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFE,0xFE,0xFE,0xFE,0x00,0x00,0x00,0x00,0x00}},
+  { 0x21a8, 0x00, {0x00,0x00,0x18,0x3C,0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x3C,0x18,0x7E,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2191, 0x00, {0x00,0x00,0x18,0x3C,0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2193, 0x00, {0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x3C,0x18,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2192, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x0C,0xFE,0x0C,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2190, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x60,0xFE,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x221f, 0x00, {0x00,0x00,0x00,0x00,0x00,0xC0,0xC0,0xC0,0xC0,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2194, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x6C,0xFE,0x6C,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x25b2, 0x00, {0x00,0x00,0x00,0x00,0x00,0x10,0x38,0x38,0x7C,0x7C,0xFE,0xFE,0xFE,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x25bc, 0x00, {0x00,0x00,0x00,0x00,0x00,0xFE,0xFE,0xFE,0x7C,0x7C,0x38,0x38,0x10,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0020, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0021, 0x00, {0x00,0x00,0x18,0x3C,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0022, 0x00, {0x66,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0023, 0x00, {0x00,0x00,0x00,0x6C,0x6C,0x6C,0xFE,0x6C,0x6C,0x6C,0xFE,0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0024, 0x00, {0x10,0x10,0x7C,0xD6,0xD6,0xD2,0x70,0x38,0x1C,0x16,0x96,0xD6,0xD6,0x7C,0x10,0x10,0x00,0x00,0x00}},
+  { 0x0025, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0xC2,0xC6,0x0C,0x18,0x30,0x60,0xC6,0x86,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0026, 0x00, {0x00,0x00,0x38,0x6C,0x6C,0x6C,0x38,0x76,0xDC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0027, 0x00, {0x30,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0028, 0x00, {0x00,0x00,0x0C,0x18,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x18,0x0C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0029, 0x00, {0x00,0x00,0x30,0x18,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x18,0x30,0x00,0x00,0x00,0x00,0x00}},
+  { 0x002a, 0x00, {0x00,0x00,0x00,0x00,0x00,0x66,0x24,0x3C,0xFF,0x3C,0x24,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x002b, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x002c, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x30,0x00,0x00,0x00,0x00}},
+  { 0x002d, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x002e, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00}},
+  { 0x002f, 0x00, {0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0030, 0x00, {0x00,0x00,0x38,0x6C,0xC6,0xC6,0xC6,0xD6,0xD6,0xC6,0xC6,0xC6,0x6C,0x38,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0031, 0x00, {0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0032, 0x00, {0x00,0x00,0x7C,0xC6,0x06,0x06,0x0C,0x18,0x30,0x60,0xC0,0xC0,0xC6,0xFE,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0033, 0x00, {0x00,0x00,0x7C,0xC6,0x06,0x06,0x06,0x3C,0x06,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0034, 0x00, {0x00,0x00,0x0C,0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0035, 0x00, {0x00,0x00,0xFE,0xC0,0xC0,0xC0,0xC0,0xFC,0x06,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0036, 0x00, {0x00,0x00,0x38,0x60,0xC0,0xC0,0xC0,0xFC,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0037, 0x00, {0x00,0x00,0xFE,0xC6,0x06,0x06,0x06,0x0C,0x18,0x30,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0038, 0x00, {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0039, 0x00, {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0x7E,0x06,0x06,0x06,0x06,0x0C,0x78,0x00,0x00,0x00,0x00,0x00}},
+  { 0x003a, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x003b, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00,0x00}},
+  { 0x003c, 0x00, {0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x60,0xC0,0x60,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00,0x00}},
+  { 0x003d, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x003e, 0x00, {0x00,0x00,0x00,0xC0,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0xC0,0x00,0x00,0x00,0x00,0x00}},
+  { 0x003f, 0x00, {0x00,0x00,0x7C,0xC6,0x86,0x06,0x0C,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0040, 0x00, {0x00,0x00,0x00,0x7C,0xC6,0xC6,0xCE,0xDE,0xDE,0xDE,0xDE,0xCC,0xC0,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0041, 0x00, {0x00,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0042, 0x00, {0x00,0x00,0xFC,0x66,0x66,0x66,0x66,0x7C,0x66,0x66,0x66,0x66,0x66,0xFC,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0043, 0x00, {0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC2,0x66,0x3C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0044, 0x00, {0x00,0x00,0xF8,0x6C,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x6C,0xF8,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0045, 0x00, {0x00,0x00,0xFE,0x66,0x62,0x62,0x68,0x78,0x68,0x60,0x62,0x62,0x66,0xFE,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0046, 0x00, {0x00,0x00,0xFE,0x66,0x62,0x62,0x68,0x78,0x68,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0047, 0x00, {0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xC0,0xC0,0xDE,0xC6,0xC6,0x66,0x3A,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0048, 0x00, {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0049, 0x00, {0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x004a, 0x00, {0x00,0x00,0x1E,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0xCC,0xCC,0xCC,0xCC,0x78,0x00,0x00,0x00,0x00,0x00}},
+  { 0x004b, 0x00, {0x00,0x00,0xE6,0x66,0x66,0x66,0x6C,0x78,0x78,0x6C,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00,0x00}},
+  { 0x004c, 0x00, {0x00,0x00,0xF0,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x62,0x62,0x66,0xFE,0x00,0x00,0x00,0x00,0x00}},
+  { 0x004d, 0x00, {0x00,0x00,0xC6,0xEE,0xFE,0xD6,0xD6,0xD6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00,0x00}},
+  { 0x004e, 0x00, {0x00,0x00,0xC6,0xE6,0xF6,0xDE,0xCE,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00,0x00}},
+  { 0x004f, 0x00, {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0050, 0x00, {0x00,0x00,0xFC,0x66,0x66,0x66,0x66,0x7C,0x60,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0051, 0x00, {0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xD6,0xDE,0x7C,0x0C,0x0E,0x00,0x00,0x00}},
+  { 0x0052, 0x00, {0x00,0x00,0xFC,0x66,0x66,0x66,0x66,0x7C,0x6C,0x66,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0053, 0x00, {0x00,0x00,0x7C,0xC6,0xC6,0xC2,0x60,0x38,0x0C,0x06,0x86,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0054, 0x00, {0x00,0x00,0x7E,0x7E,0x5A,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0055, 0x00, {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0056, 0x00, {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x10,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0057, 0x00, {0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0xEE,0x6C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0058, 0x00, {0x00,0x00,0xC6,0xC6,0x6C,0x6C,0x38,0x38,0x38,0x38,0x6C,0x6C,0xC6,0xC6,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0059, 0x00, {0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x005a, 0x00, {0x00,0x00,0xFE,0xC6,0x86,0x86,0x0C,0x18,0x30,0x60,0xC2,0xC2,0xC6,0xFE,0x00,0x00,0x00,0x00,0x00}},
+  { 0x005b, 0x00, {0x00,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x005c, 0x00, {0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x005d, 0x00, {0x00,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x005e, 0x00, {0x00,0x10,0x38,0x6C,0xC6,0x82,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x005f, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00}},
+  { 0x0060, 0x00, {0x30,0x30,0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0061, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x0C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0062, 0x00, {0x00,0x00,0xE0,0x60,0x60,0x60,0x78,0x6C,0x66,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0063, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC0,0xC0,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0064, 0x00, {0x00,0x00,0x1C,0x0C,0x0C,0x0C,0x3C,0x6C,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0065, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC6,0xFE,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0066, 0x00, {0x00,0x00,0x1C,0x36,0x32,0x30,0x78,0x30,0x30,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0067, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x76,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,0x0C,0xCC,0x78,0x00}},
+  { 0x0068, 0x00, {0x00,0x00,0xE0,0x60,0x60,0x60,0x6C,0x76,0x66,0x66,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0069, 0x00, {0x00,0x00,0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x006a, 0x00, {0x00,0x00,0x00,0x0C,0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0xCC,0xCC,0xCC,0x78,0x00}},
+  { 0x006b, 0x00, {0x00,0x00,0xE0,0x60,0x60,0x60,0x66,0x6C,0x78,0x78,0x6C,0x66,0x66,0xE6,0x00,0x00,0x00,0x00,0x00}},
+  { 0x006c, 0x00, {0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x006d, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x6C,0xFE,0xD6,0xD6,0xD6,0xD6,0xC6,0xC6,0x00,0x00,0x00,0x00,0x00}},
+  { 0x006e, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00,0x00}},
+  { 0x006f, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0070, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x66,0x7C,0x60,0x60,0x60,0xF0,0x00}},
+  { 0x0071, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x76,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x7C,0x0C,0x0C,0x0C,0x1E,0x00}},
+  { 0x0072, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0xDC,0x76,0x66,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0073, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0xC6,0xC2,0x78,0x0C,0x86,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0074, 0x00, {0x00,0x00,0x10,0x30,0x30,0x30,0xFC,0x30,0x30,0x30,0x30,0x30,0x36,0x1C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0075, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0076, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0077, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0x6C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0078, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0xC6,0x6C,0x38,0x10,0x38,0x6C,0xC6,0x82,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0079, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7E,0x06,0x06,0x0C,0xF8,0x00}},
+  { 0x007a, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xCC,0x18,0x30,0x60,0xC0,0xC6,0xFE,0x00,0x00,0x00,0x00,0x00}},
+  { 0x007b, 0x00, {0x00,0x00,0x0E,0x18,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x18,0x0E,0x00,0x00,0x00,0x00,0x00}},
+  { 0x007c, 0x00, {0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00}},
+  { 0x007d, 0x00, {0x00,0x00,0x70,0x18,0x18,0x18,0x18,0x0E,0x18,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00,0x00}},
+  { 0x007e, 0x00, {0x00,0x76,0xD4,0x9C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2302, 0x00, {0x00,0x00,0x00,0x00,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xC6,0xC6,0xFE,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00c7, 0x00, {0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xC0,0xC0,0xC0,0xC2,0x66,0x3C,0x0C,0x06,0x7C,0x00,0x00,0x00}},
+  { 0x00fc, 0x00, {0x00,0x00,0x00,0xCC,0xCC,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00e9, 0x00, {0x00,0x0C,0x18,0x30,0x00,0x00,0x7C,0xC6,0xC6,0xFE,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00e2, 0x00, {0x00,0x10,0x38,0x6C,0x00,0x00,0x78,0x0C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00e4, 0x00, {0x00,0x00,0x00,0xCC,0xCC,0x00,0x78,0x0C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00e0, 0x00, {0x00,0x60,0x30,0x18,0x0C,0x00,0x78,0x0C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00e5, 0x00, {0x00,0x38,0x6C,0x6C,0x38,0x00,0x78,0x0C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00e7, 0x00, {0x00,0x00,0x00,0x00,0x3C,0x66,0x62,0x60,0x60,0x66,0x64,0x3C,0x0C,0x06,0x3C,0x00,0x00,0x00,0x00}},
+  { 0x00ea, 0x00, {0x00,0x10,0x38,0x6C,0x44,0x00,0x7C,0xC6,0xC6,0xFE,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00eb, 0x00, {0x00,0x00,0x00,0xC6,0xC6,0x00,0x7C,0xC6,0xC6,0xFE,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00e8, 0x00, {0x00,0x60,0x30,0x18,0x0C,0x00,0x7C,0xC6,0xC6,0xFE,0xC0,0xC0,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00ef, 0x00, {0x00,0x00,0x00,0x66,0x66,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00ee, 0x00, {0x00,0x18,0x3C,0x66,0x42,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00ec, 0x00, {0x00,0x60,0x30,0x18,0x08,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00c4, 0x00, {0xC6,0xC6,0x10,0x38,0x6C,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00c5, 0x00, {0x38,0x28,0x38,0x10,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00c9, 0x00, {0x18,0x30,0x60,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00e6, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0xCC,0x76,0x36,0x36,0x7E,0xD8,0xD8,0x6E,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00c6, 0x00, {0x00,0x00,0x3E,0x6C,0xCC,0xCC,0xFE,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCE,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00f4, 0x00, {0x00,0x10,0x38,0x6C,0x44,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00f6, 0x00, {0x00,0x00,0x00,0xC6,0xC6,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00f2, 0x00, {0x00,0x60,0x30,0x18,0x0C,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00fb, 0x00, {0x00,0x30,0x78,0xCC,0x84,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00f9, 0x00, {0x00,0x60,0x30,0x18,0x0C,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00ff, 0x00, {0x00,0x00,0x00,0xC6,0xC6,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7E,0x06,0x06,0x0C,0x78,0x00}},
+  { 0x00d6, 0x00, {0xC6,0xC6,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00dc, 0x00, {0xC6,0xC6,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00a2, 0x00, {0x08,0x08,0x3C,0x6E,0xCA,0xC8,0xC8,0xC8,0xC8,0xC8,0xC8,0xCA,0x6E,0x3C,0x08,0x08,0x00,0x00,0x00}},
+  { 0x00a3, 0x00, {0x00,0x38,0x6C,0x64,0x64,0x60,0xF0,0x60,0x60,0x60,0x60,0x62,0xE6,0xFC,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00a5, 0x00, {0x00,0x00,0x66,0x66,0x66,0x66,0x3C,0x18,0x7E,0x18,0x7E,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00}},
+  { 0x20a7, 0x00, {0x00,0xF8,0xCC,0xCC,0xFC,0xF8,0xC4,0xCC,0xDE,0xCC,0xCC,0xCC,0xCC,0xC6,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0192, 0x00, {0x00,0x0E,0x1B,0x19,0x18,0x18,0x18,0x7E,0x18,0x18,0x18,0x18,0x18,0x98,0xD8,0x70,0x00,0x00,0x00}},
+  { 0x00e1, 0x00, {0x00,0x18,0x30,0x60,0xC0,0x00,0x78,0x0C,0x0C,0x7C,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00ed, 0x00, {0x00,0x0C,0x18,0x30,0x20,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00f3, 0x00, {0x00,0x18,0x30,0x60,0x40,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00fa, 0x00, {0x00,0x18,0x30,0x60,0x40,0x00,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00f1, 0x00, {0x00,0x00,0x36,0xD4,0x88,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00d1, 0x00, {0x36,0xCC,0x00,0xC6,0xE6,0xF6,0xDE,0xCE,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00aa, 0x00, {0x00,0x3C,0x6C,0x6C,0x6C,0x3E,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00ba, 0x00, {0x00,0x38,0x6C,0x6C,0x6C,0x38,0x00,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00bf, 0x00, {0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x30,0x60,0xC0,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2310, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00ac, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x06,0x06,0x06,0x06,0x06,0x06,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00bd, 0x00, {0x00,0xC0,0xC0,0xC0,0xC2,0xC6,0x0C,0x18,0x30,0x60,0xC0,0x9C,0x06,0x0C,0x18,0x3E,0x00,0x00,0x00}},
+  { 0x00bc, 0x00, {0x00,0xC0,0xC0,0xC0,0xC2,0xC6,0x0C,0x18,0x30,0x66,0xCE,0x9A,0x32,0x3E,0x06,0x06,0x00,0x00,0x00}},
+  { 0x00a1, 0x00, {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x3C,0x3C,0x3C,0x3C,0x18,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00ab, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x6C,0xD8,0xD8,0x6C,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00bb, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0xD8,0x6C,0x36,0x36,0x6C,0xD8,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2591, 0x00, {0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11}},
+  { 0x2592, 0x00, {0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55}},
+  { 0x2593, 0x00, {0xDD,0x77,0xDD,0x77,0xDD,0x77,0xDD,0x77,0xDD,0x77,0xDD,0x77,0xDD,0x77,0xDD,0x77,0xDD,0x77,0xDD}},
+  { 0x2502, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}},
+  { 0x2524, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}},
+  { 0x2561, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xF8,0x18,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}},
+  { 0x2562, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xF6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}},
+  { 0x2556, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}},
+  { 0x2555, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x18,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}},
+  { 0x2563, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xF6,0x06,0xF6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}},
+  { 0x2551, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}},
+  { 0x2557, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x06,0xF6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}},
+  { 0x255d, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xF6,0x06,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x255c, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x255b, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xF8,0x18,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2510, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}},
+  { 0x2514, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2534, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x252c, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}},
+  { 0x251c, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}},
+  { 0x2500, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x253c, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}},
+  { 0x255e, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0x18,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}},
+  { 0x255f, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}},
+  { 0x255a, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2554, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}},
+  { 0x2569, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xF7,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2566, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0xF7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}},
+  { 0x2560, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}},
+  { 0x2550, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x256c, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xF7,0x00,0xF7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}},
+  { 0x2567, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2568, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2564, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}},
+  { 0x2565, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}},
+  { 0x2559, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2558, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0x18,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2552, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x18,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}},
+  { 0x2553, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}},
+  { 0x256b, 0x00, {0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xFF,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36}},
+  { 0x256a, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0x18,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}},
+  { 0x2518, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x250c, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}},
+  { 0x2588, 0x00, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}},
+  { 0x2584, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}},
+  { 0x258c, 0x00, {0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0}},
+  { 0x2590, 0x00, {0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F}},
+  { 0x2580, 0x00, {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x03b1, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x76,0xDC,0xD8,0xD8,0xD8,0xD8,0xDC,0x76,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00df, 0x00, {0x00,0x00,0x78,0xCC,0xCC,0xCC,0xCC,0xD8,0xCC,0xC6,0xC6,0xC6,0xC6,0xCC,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0393, 0x00, {0x00,0x00,0xFE,0xC6,0xC6,0xC6,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00}},
+  { 0x03c0, 0x00, {0x00,0x00,0x00,0x00,0xFE,0x6C,0x6C,0x6C,0x6C,0x6C,0x6C,0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x03a3, 0x00, {0x00,0x00,0x00,0xFE,0xC6,0xC2,0x60,0x30,0x18,0x30,0x60,0xC2,0xC6,0xFE,0x00,0x00,0x00,0x00,0x00}},
+  { 0x03c3, 0x00, {0x00,0x00,0x00,0x00,0x00,0x7E,0xD8,0xD8,0xD8,0xD8,0xD8,0xD8,0xD8,0x70,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00b5, 0x00, {0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x7C,0x60,0x60,0xC0,0x00,0x00,0x00,0x00}},
+  { 0x03c4, 0x00, {0x00,0x00,0x00,0x00,0x76,0xDC,0x98,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00}},
+  { 0x03a6, 0x00, {0x00,0x00,0x00,0x7E,0x18,0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x7E,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0398, 0x00, {0x00,0x00,0x00,0x38,0x6C,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x6C,0x38,0x00,0x00,0x00,0x00,0x00}},
+  { 0x03a9, 0x00, {0x00,0x00,0x38,0x6C,0xC6,0xC6,0xC6,0xC6,0x6C,0x6C,0x6C,0x6C,0x6C,0xEE,0x00,0x00,0x00,0x00,0x00}},
+  { 0x03b4, 0x00, {0x00,0x00,0x1E,0x20,0x30,0x18,0x0C,0x3E,0x66,0x66,0x66,0x66,0x66,0x3C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x221e, 0x00, {0x00,0x00,0x00,0x00,0x00,0x7E,0xDB,0xDB,0xDB,0xDB,0xDB,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x03c6, 0x00, {0x00,0x00,0x00,0x03,0x06,0x7E,0xDB,0xDB,0xDB,0xDB,0xD3,0x7E,0x60,0xC0,0x00,0x00,0x00,0x00,0x00}},
+  { 0x03b5, 0x00, {0x00,0x00,0x1C,0x30,0x60,0x60,0x60,0x7C,0x60,0x60,0x60,0x60,0x30,0x1C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2229, 0x00, {0x00,0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2261, 0x00, {0x00,0x00,0x00,0x00,0x00,0xFE,0x00,0x00,0xFE,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00b1, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2265, 0x00, {0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x7E,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2264, 0x00, {0x00,0x00,0x00,0x06,0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x06,0x00,0x7E,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2320, 0x00, {0x00,0x00,0x0E,0x1B,0x1B,0x1B,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}},
+  { 0x2321, 0x00, {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xD8,0xD8,0xD8,0x70,0x00,0x00}},
+  { 0x00f7, 0x00, {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x7E,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2248, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x76,0xDC,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00b0, 0x00, {0x00,0x38,0x6C,0x6C,0x6C,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x2219, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00b7, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x221a, 0x00, {0x00,0x0F,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0xEC,0x6C,0x6C,0x6C,0x3C,0x1C,0x00,0x00,0x00,0x00,0x00}},
+  { 0x207f, 0x00, {0x00,0xD8,0x6C,0x6C,0x6C,0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00b2, 0x00, {0x00,0x70,0xD8,0x10,0x30,0x60,0xC8,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x25a0, 0x00, {0x00,0x00,0x00,0x00,0x00,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,0x7C,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x00a0, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
+  { 0x0000, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} //EOL
+};
+
+// Get available Unicode glyphs narrow fonts(8*19 pixels) size.
+UINT32 mNarrowFontSize =  sizeof (gUsStdNarrowGlyphData);
+
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/ComponentName.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/ComponentName.c
new file mode 100644
index 000000000000..80eb4ff9a870
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/ComponentName.c
@@ -0,0 +1,163 @@
+/** @file
+ *
+ *  Component Name Protocol implementation for the MMC DXE driver
+ *
+ *  Copyright (c) 2011, 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 "Mmc.h"
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gMmcComponentName = {
+  MmcGetDriverName,
+  MmcGetControllerName,
+  "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gMmcComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) MmcGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) MmcGetControllerName,
+  "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE
+mMmcDriverNameTable[] = {
+  {"eng;en", L"MMC/SD Card Interface Driver"},
+  {NULL,  NULL}
+};
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This                  A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param  Language              A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+  @param  DriverName            A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+MmcGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mMmcDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gMmcComponentName)
+           );
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This                  A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param  ControllerHandle      The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+  @param  ChildHandle           The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+  @param  Language              A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+  @param  ControllerName        A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+MmcGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  )
+{
+  return EFI_UNSUPPORTED;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Diagnostics.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Diagnostics.c
new file mode 100644
index 000000000000..f0ff16708b67
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Diagnostics.c
@@ -0,0 +1,256 @@
+/** @file
+ *
+ *  Diagnostics Protocol implementation for the MMC DXE driver
+ *
+ *  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 <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseLib.h>
+
+#include "Mmc.h"
+
+#define DIAGNOSTIC_LOGBUFFER_MAXCHAR  1024
+
+CHAR16* mLogBuffer = NULL;
+UINTN   mLogRemainChar = 0;
+
+CHAR16*
+DiagnosticInitLog (
+  UINTN MaxBufferChar
+  )
+{
+  mLogRemainChar = MaxBufferChar;
+  mLogBuffer = AllocatePool ((UINTN)MaxBufferChar * sizeof (CHAR16));
+  return mLogBuffer;
+}
+
+UINTN
+DiagnosticLog (
+  CONST CHAR16* Str
+  )
+{
+  UINTN len = StrLen (Str);
+  if (len < mLogRemainChar) {
+    StrCpyS (mLogBuffer, mLogRemainChar, Str);
+    mLogRemainChar -= len;
+    mLogBuffer += len;
+    return len;
+  } else {
+    return 0;
+  }
+}
+
+VOID
+GenerateRandomBuffer (
+  VOID* Buffer,
+  UINTN BufferSize
+  )
+{
+  UINT64  i;
+  UINT64* Buffer64 = (UINT64*)Buffer;
+
+  for (i = 0; i < (BufferSize >> 3); i++) {
+    *Buffer64 = i | (~i << 32);
+    Buffer64++;
+  }
+}
+
+BOOLEAN
+CompareBuffer (
+  VOID  *BufferA,
+  VOID  *BufferB,
+  UINTN BufferSize
+  )
+{
+  UINTN i;
+  UINT64* BufferA64 = (UINT64*)BufferA;
+  UINT64* BufferB64 = (UINT64*)BufferB;
+
+  for (i = 0; i < (BufferSize >> 3); i++) {
+    if (*BufferA64 != *BufferB64) {
+      DEBUG ((DEBUG_ERROR, "CompareBuffer: Error at %i", i));
+      DEBUG ((DEBUG_ERROR, "(0x%lX) != (0x%lX)\n", *BufferA64, *BufferB64));
+      return FALSE;
+    }
+    BufferA64++;
+    BufferB64++;
+  }
+  return TRUE;
+}
+
+EFI_STATUS
+MmcReadWriteDataTest (
+  MMC_HOST_INSTANCE *MmcHostInstance,
+  EFI_LBA           Lba,
+  UINTN             BufferSize
+  )
+{
+  VOID                        *BackBuffer;
+  VOID                        *WriteBuffer;
+  VOID                        *ReadBuffer;
+  EFI_STATUS                  Status;
+
+  // Check if a Media is Present
+  if (!MmcHostInstance->BlockIo.Media->MediaPresent) {
+    DiagnosticLog (L"ERROR: No Media Present\n");
+    return EFI_NO_MEDIA;
+  }
+
+  if (MmcHostInstance->State != MmcTransferState) {
+    DiagnosticLog (L"ERROR: Not ready for Transfer state\n");
+    return EFI_NOT_READY;
+  }
+
+  BackBuffer = AllocatePool (BufferSize);
+  WriteBuffer = AllocatePool (BufferSize);
+  ReadBuffer = AllocatePool (BufferSize);
+
+  // Read (and save) buffer at a specific location
+  Status = MmcReadBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,BackBuffer);
+  if (Status != EFI_SUCCESS) {
+    DiagnosticLog (L"ERROR: Fail to Read Block (1)\n");
+    return Status;
+  }
+
+  // Write buffer at the same location
+  GenerateRandomBuffer (WriteBuffer,BufferSize);
+  Status = MmcWriteBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,WriteBuffer);
+  if (Status != EFI_SUCCESS) {
+    DiagnosticLog (L"ERROR: Fail to Write Block (1)\n");
+    return Status;
+  }
+
+  // Read the buffer at the same location
+  Status = MmcReadBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,ReadBuffer);
+  if (Status != EFI_SUCCESS) {
+    DiagnosticLog (L"ERROR: Fail to Read Block (2)\n");
+    return Status;
+  }
+
+  // Check that is conform
+  if (!CompareBuffer (ReadBuffer,WriteBuffer,BufferSize)) {
+    DiagnosticLog (L"ERROR: Fail to Read/Write Block (1)\n");
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Restore content at the original location
+  Status = MmcWriteBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,BackBuffer);
+  if (Status != EFI_SUCCESS) {
+    DiagnosticLog (L"ERROR: Fail to Write Block (2)\n");
+    return Status;
+  }
+
+  // Read the restored content
+  Status = MmcReadBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,ReadBuffer);
+  if (Status != EFI_SUCCESS) {
+    DiagnosticLog (L"ERROR: Fail to Read Block (3)\n");
+    return Status;
+  }
+
+  // Check the content is correct
+  if (!CompareBuffer (ReadBuffer,BackBuffer,BufferSize)) {
+    DiagnosticLog (L"ERROR: Fail to Read/Write Block (2)\n");
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+MmcDriverDiagnosticsRunDiagnostics (
+  IN  EFI_DRIVER_DIAGNOSTICS_PROTOCOL               *This,
+  IN  EFI_HANDLE                                    ControllerHandle,
+  IN  EFI_HANDLE                                    ChildHandle  OPTIONAL,
+  IN  EFI_DRIVER_DIAGNOSTIC_TYPE                    DiagnosticType,
+  IN  CHAR8                                         *Language,
+  OUT EFI_GUID                                      **ErrorType,
+  OUT UINTN                                         *BufferSize,
+  OUT CHAR16                                        **Buffer
+  )
+{
+  LIST_ENTRY              *CurrentLink;
+  MMC_HOST_INSTANCE       *MmcHostInstance;
+  EFI_STATUS              Status;
+
+  if ((Language         == NULL) ||
+      (ErrorType        == NULL) ||
+      (Buffer           == NULL) ||
+      (ControllerHandle == NULL) ||
+      (BufferSize       == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check Language is supported (i.e. is "en-*" - only English is supported)
+  if (AsciiStrnCmp (Language, "en", 2) != 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+  Status = EFI_SUCCESS;
+  *ErrorType  = NULL;
+  *BufferSize = DIAGNOSTIC_LOGBUFFER_MAXCHAR;
+  *Buffer = DiagnosticInitLog (DIAGNOSTIC_LOGBUFFER_MAXCHAR);
+
+  DiagnosticLog (L"MMC Driver Diagnostics\n");
+
+  // Find the MMC Host instance on which we have been asked to run diagnostics
+  MmcHostInstance = NULL;
+  CurrentLink = mMmcHostPool.ForwardLink;
+  while (CurrentLink != NULL && CurrentLink != &mMmcHostPool && (Status == EFI_SUCCESS)) {
+    MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink);
+    ASSERT(MmcHostInstance != NULL);
+    if (MmcHostInstance->MmcHandle == ControllerHandle) {
+      break;
+    }
+    CurrentLink = CurrentLink->ForwardLink;
+  }
+
+  // If we didn't find the controller, return EFI_UNSUPPORTED
+  if ((MmcHostInstance == NULL)
+      || (MmcHostInstance->MmcHandle != ControllerHandle)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  // LBA=1 Size=BlockSize
+  DiagnosticLog (L"MMC Driver Diagnostics - Test: First Block\n");
+  Status = MmcReadWriteDataTest (MmcHostInstance, 1, MmcHostInstance->BlockIo.Media->BlockSize);
+
+  // LBA=2 Size=BlockSize
+  DiagnosticLog (L"MMC Driver Diagnostics - Test: Second Block\n");
+  Status = MmcReadWriteDataTest (MmcHostInstance, 2, MmcHostInstance->BlockIo.Media->BlockSize);
+
+  // LBA=10 Size=BlockSize
+  DiagnosticLog (L"MMC Driver Diagnostics - Test: Any Block\n");
+  Status = MmcReadWriteDataTest (MmcHostInstance, MmcHostInstance->BlockIo.Media->LastBlock >> 1, MmcHostInstance->BlockIo.Media->BlockSize);
+
+  // LBA=LastBlock Size=BlockSize
+  DiagnosticLog (L"MMC Driver Diagnostics - Test: Last Block\n");
+  Status = MmcReadWriteDataTest (MmcHostInstance, MmcHostInstance->BlockIo.Media->LastBlock, MmcHostInstance->BlockIo.Media->BlockSize);
+
+  // LBA=1 Size=2*BlockSize
+  DiagnosticLog (L"MMC Driver Diagnostics - Test: First Block / 2 BlockSSize\n");
+  Status = MmcReadWriteDataTest (MmcHostInstance, 1, 2 * MmcHostInstance->BlockIo.Media->BlockSize);
+
+  return Status;
+}
+
+//
+// EFI Driver Diagnostics 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_DRIVER_DIAGNOSTICS2_PROTOCOL gMmcDriverDiagnostics2 = {
+  (EFI_DRIVER_DIAGNOSTICS2_RUN_DIAGNOSTICS) MmcDriverDiagnosticsRunDiagnostics,
+  "en"
+};
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.c
new file mode 100644
index 000000000000..d90cf3018251
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.c
@@ -0,0 +1,458 @@
+/** @file
+ *
+ *  Main file of the MMC Dxe driver. The driver entrypoint is defined into this file.
+ *
+ *  Copyright (c) 2011-2013, ARM Limited. All rights reserved.
+ *
+ *  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 <Protocol/DevicePath.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DevicePathLib.h>
+
+#include "Mmc.h"
+
+EFI_BLOCK_IO_MEDIA mMmcMediaTemplate = {
+  SIGNATURE_32('m','m','c','o'),            // MediaId
+  TRUE,                                     // RemovableMedia
+  FALSE,                                    // MediaPresent
+  FALSE,                                    // LogicalPartition
+  FALSE,                                    // ReadOnly
+  FALSE,                                    // WriteCaching
+  512,                                      // BlockSize
+  4,                                        // IoAlign
+  0,                                        // Pad
+  0                                         // LastBlock
+};
+
+//
+// This device structure is serviced as a header.
+// Its next field points to the first root bridge device node.
+//
+LIST_ENTRY  mMmcHostPool;
+
+/**
+  Event triggered by the timer to check if any cards have been removed
+  or if new ones have been plugged in
+**/
+
+EFI_EVENT gCheckCardsEvent;
+
+/**
+  Initialize the MMC Host Pool to support multiple MMC devices
+**/
+VOID
+InitializeMmcHostPool (
+  VOID
+  )
+{
+  InitializeListHead (&mMmcHostPool);
+}
+
+/**
+  Insert a new Mmc Host controller to the pool
+**/
+VOID
+InsertMmcHost (
+  IN MMC_HOST_INSTANCE      *MmcHostInstance
+  )
+{
+  InsertTailList (&mMmcHostPool, &(MmcHostInstance->Link));
+}
+
+/*
+  Remove a new Mmc Host controller to the pool
+*/
+VOID
+RemoveMmcHost (
+  IN MMC_HOST_INSTANCE      *MmcHostInstance
+  )
+{
+  RemoveEntryList (&(MmcHostInstance->Link));
+}
+
+MMC_HOST_INSTANCE* CreateMmcHostInstance (
+  IN EFI_MMC_HOST_PROTOCOL* MmcHost
+  )
+{
+  EFI_STATUS          Status;
+  MMC_HOST_INSTANCE*  MmcHostInstance;
+  EFI_DEVICE_PATH_PROTOCOL    *NewDevicePathNode;
+  EFI_DEVICE_PATH_PROTOCOL    *DevicePath;
+
+  MmcHostInstance = AllocateZeroPool (sizeof (MMC_HOST_INSTANCE));
+  if (MmcHostInstance == NULL) {
+    return NULL;
+  }
+
+  MmcHostInstance->Signature = MMC_HOST_INSTANCE_SIGNATURE;
+
+  MmcHostInstance->State = MmcHwInitializationState;
+
+  MmcHostInstance->BlockIo.Media = AllocateCopyPool (sizeof(EFI_BLOCK_IO_MEDIA), &mMmcMediaTemplate);
+  if (MmcHostInstance->BlockIo.Media == NULL) {
+    goto FREE_INSTANCE;
+  }
+
+  MmcHostInstance->BlockIo.Revision = EFI_BLOCK_IO_INTERFACE_REVISION;
+  MmcHostInstance->BlockIo.Reset = MmcReset;
+  MmcHostInstance->BlockIo.ReadBlocks = MmcReadBlocks;
+  MmcHostInstance->BlockIo.WriteBlocks = MmcWriteBlocks;
+  MmcHostInstance->BlockIo.FlushBlocks = MmcFlushBlocks;
+
+  MmcHostInstance->MmcHost = MmcHost;
+
+  // Create DevicePath for the new MMC Host
+  Status = MmcHost->BuildDevicePath (MmcHost, &NewDevicePathNode);
+  if (EFI_ERROR (Status)) {
+    goto FREE_MEDIA;
+  }
+
+  DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocatePool (END_DEVICE_PATH_LENGTH);
+  if (DevicePath == NULL) {
+    goto FREE_MEDIA;
+  }
+
+  SetDevicePathEndNode (DevicePath);
+  MmcHostInstance->DevicePath = AppendDevicePathNode (DevicePath, NewDevicePathNode);
+
+  // Publish BlockIO protocol interface
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                &MmcHostInstance->MmcHandle,
+                &gEfiBlockIoProtocolGuid,&MmcHostInstance->BlockIo,
+                &gEfiDevicePathProtocolGuid,MmcHostInstance->DevicePath,
+                NULL
+                );
+  if (EFI_ERROR(Status)) {
+    goto FREE_DEVICE_PATH;
+  }
+
+  return MmcHostInstance;
+
+FREE_DEVICE_PATH:
+  FreePool(DevicePath);
+
+FREE_MEDIA:
+  FreePool(MmcHostInstance->BlockIo.Media);
+
+FREE_INSTANCE:
+  FreePool(MmcHostInstance);
+
+  return NULL;
+}
+
+EFI_STATUS DestroyMmcHostInstance (
+  IN MMC_HOST_INSTANCE* MmcHostInstance
+  )
+{
+  EFI_STATUS Status;
+
+  // Uninstall Protocol Interfaces
+  Status = gBS->UninstallMultipleProtocolInterfaces (
+        MmcHostInstance->MmcHandle,
+        &gEfiBlockIoProtocolGuid,&(MmcHostInstance->BlockIo),
+        &gEfiDevicePathProtocolGuid,MmcHostInstance->DevicePath,
+        NULL
+        );
+  ASSERT_EFI_ERROR (Status);
+
+  // Free Memory allocated for the instance
+  if (MmcHostInstance->BlockIo.Media) {
+    FreePool(MmcHostInstance->BlockIo.Media);
+  }
+  if (MmcHostInstance->CardInfo.ECSDData) {
+    FreePages (MmcHostInstance->CardInfo.ECSDData, EFI_SIZE_TO_PAGES (sizeof (ECSD)));
+  }
+  FreePool (MmcHostInstance);
+
+  return Status;
+}
+
+/**
+  This function checks if the controller implement the Mmc Host and the Device Path Protocols
+**/
+EFI_STATUS
+EFIAPI
+MmcDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  )
+{
+  EFI_STATUS                      Status;
+  //EFI_DEVICE_PATH_PROTOCOL        *ParentDevicePath;
+  EFI_MMC_HOST_PROTOCOL           *MmcHost;
+  EFI_DEV_PATH_PTR                Node;
+
+  //
+  // Check RemainingDevicePath validation
+  //
+  if (RemainingDevicePath != NULL) {
+    //
+    // Check if RemainingDevicePath is the End of Device Path Node,
+    // if yes, go on checking other conditions
+    //
+    if (!IsDevicePathEnd (RemainingDevicePath)) {
+      //
+      // If RemainingDevicePath isn't the End of Device Path Node,
+      // check its validation
+      //
+      Node.DevPath = RemainingDevicePath;
+      if (Node.DevPath->Type != HARDWARE_DEVICE_PATH ||
+        Node.DevPath->SubType != HW_VENDOR_DP      ||
+        DevicePathNodeLength(Node.DevPath) != sizeof(VENDOR_DEVICE_PATH)) {
+          return EFI_UNSUPPORTED;
+      }
+    }
+  }
+
+  //
+  // Check if Mmc Host protocol is installed by platform
+  //
+  Status = gBS->OpenProtocol (
+                Controller,
+                &gRaspberryPiMmcHostProtocolGuid,
+                (VOID **) &MmcHost,
+                This->DriverBindingHandle,
+                Controller,
+                EFI_OPEN_PROTOCOL_BY_DRIVER
+                );
+  if (Status == EFI_ALREADY_STARTED) {
+    return EFI_SUCCESS;
+  }
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Close the Mmc Host used to perform the supported test
+  //
+  gBS->CloseProtocol (
+      Controller,
+      &gRaspberryPiMmcHostProtocolGuid,
+      This->DriverBindingHandle,
+      Controller
+      );
+
+  return EFI_SUCCESS;
+}
+
+/**
+
+**/
+EFI_STATUS
+EFIAPI
+MmcDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_STATUS              Status;
+  MMC_HOST_INSTANCE       *MmcHostInstance;
+  EFI_MMC_HOST_PROTOCOL   *MmcHost;
+
+  //
+  // Check RemainingDevicePath validation
+  //
+  if (RemainingDevicePath != NULL) {
+    //
+    // Check if RemainingDevicePath is the End of Device Path Node,
+    // if yes, return EFI_SUCCESS
+    //
+    if (IsDevicePathEnd (RemainingDevicePath)) {
+      return EFI_SUCCESS;
+    }
+  }
+
+  //
+  // Get the Mmc Host protocol
+  //
+  Status = gBS->OpenProtocol (
+                Controller,
+                &gRaspberryPiMmcHostProtocolGuid,
+                (VOID **) &MmcHost,
+                This->DriverBindingHandle,
+                Controller,
+                EFI_OPEN_PROTOCOL_BY_DRIVER
+                );
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_ALREADY_STARTED) {
+      return EFI_SUCCESS;
+    }
+    return Status;
+  }
+
+  MmcHostInstance = CreateMmcHostInstance(MmcHost);
+  if (MmcHostInstance != NULL) {
+    // Add the handle to the pool
+    InsertMmcHost (MmcHostInstance);
+
+    MmcHostInstance->Initialized = FALSE;
+
+    // Detect card presence now
+    CheckCardsCallback (NULL, NULL);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+
+**/
+EFI_STATUS
+EFIAPI
+MmcDriverBindingStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL   *This,
+  IN  EFI_HANDLE                    Controller,
+  IN  UINTN                         NumberOfChildren,
+  IN  EFI_HANDLE                    *ChildHandleBuffer
+  )
+{
+  EFI_STATUS          Status = EFI_SUCCESS;
+  LIST_ENTRY          *CurrentLink;
+  MMC_HOST_INSTANCE   *MmcHostInstance;
+
+  MMC_TRACE("MmcDriverBindingStop()");
+
+  // For each MMC instance
+  CurrentLink = mMmcHostPool.ForwardLink;
+  while (CurrentLink != NULL && CurrentLink != &mMmcHostPool && (Status == EFI_SUCCESS)) {
+    MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink);
+    ASSERT(MmcHostInstance != NULL);
+
+    // Close gRaspberryPiMmcHostProtocolGuid
+    Status = gBS->CloseProtocol (
+                Controller,
+                &gRaspberryPiMmcHostProtocolGuid,(VOID **) &MmcHostInstance->MmcHost,
+                This->DriverBindingHandle
+                );
+
+    // Remove MMC Host Instance from the pool
+    RemoveMmcHost (MmcHostInstance);
+
+    // Destroy MmcHostInstance
+    DestroyMmcHostInstance (MmcHostInstance);
+  }
+
+  return Status;
+}
+
+VOID
+EFIAPI
+CheckCardsCallback (
+  IN  EFI_EVENT   Event,
+  IN  VOID        *Context
+  )
+{
+  LIST_ENTRY          *CurrentLink;
+  MMC_HOST_INSTANCE   *MmcHostInstance;
+  EFI_STATUS          Status;
+
+  CurrentLink = mMmcHostPool.ForwardLink;
+  while (CurrentLink != NULL && CurrentLink != &mMmcHostPool) {
+    MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink);
+    ASSERT(MmcHostInstance != NULL);
+
+    if (MmcHostInstance->MmcHost->IsCardPresent (MmcHostInstance->MmcHost) == !MmcHostInstance->Initialized) {
+      MmcHostInstance->State = MmcHwInitializationState;
+      MmcHostInstance->BlockIo.Media->MediaPresent = !MmcHostInstance->Initialized;
+      MmcHostInstance->Initialized = !MmcHostInstance->Initialized;
+
+      if (MmcHostInstance->BlockIo.Media->MediaPresent) {
+        InitializeMmcDevice (MmcHostInstance);
+      }
+
+      Status = gBS->ReinstallProtocolInterface (
+                    (MmcHostInstance->MmcHandle),
+                    &gEfiBlockIoProtocolGuid,
+                    &(MmcHostInstance->BlockIo),
+                    &(MmcHostInstance->BlockIo)
+                    );
+
+      if (EFI_ERROR(Status)) {
+        Print(L"MMC Card: Error reinstalling BlockIo interface\n");
+      }
+    }
+
+    CurrentLink = CurrentLink->ForwardLink;
+  }
+}
+
+
+EFI_DRIVER_BINDING_PROTOCOL gMmcDriverBinding = {
+  MmcDriverBindingSupported,
+  MmcDriverBindingStart,
+  MmcDriverBindingStop,
+  0xa,
+  NULL,
+  NULL
+};
+
+/**
+
+**/
+EFI_STATUS
+EFIAPI
+MmcDxeInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+
+  //
+  // Initializes MMC Host pool
+  //
+  InitializeMmcHostPool ();
+
+  //
+  // Install driver model protocol(s).
+  //
+  Status = EfiLibInstallDriverBindingComponentName2 (
+           ImageHandle,
+           SystemTable,
+           &gMmcDriverBinding,
+           ImageHandle,
+           &gMmcComponentName,
+           &gMmcComponentName2
+           );
+  ASSERT_EFI_ERROR (Status);
+
+  // Install driver diagnostics
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                &ImageHandle,
+                &gEfiDriverDiagnostics2ProtocolGuid,&gMmcDriverDiagnostics2,
+                NULL
+                );
+  ASSERT_EFI_ERROR (Status);
+
+  // Use a timer to detect if a card has been plugged in or removed
+  Status = gBS->CreateEvent (
+                EVT_NOTIFY_SIGNAL | EVT_TIMER,
+                TPL_CALLBACK,
+                CheckCardsCallback,
+                NULL,
+                &gCheckCardsEvent);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->SetTimer(
+                gCheckCardsEvent,
+                TimerPeriodic,
+                (UINT64)(10*1000*200)); // 200 ms
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.h b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.h
new file mode 100644
index 000000000000..a18f4a82d903
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.h
@@ -0,0 +1,533 @@
+/** @file
+ *
+ *  Main Header file for the MMC DXE driver
+ *
+ *  Copyright (c) 2011-2015, 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 __MMC_H
+#define __MMC_H
+
+#include <Uefi.h>
+
+#include <Protocol/DiskIo.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/PiMmcHost.h>
+
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#define MMC_TRACE(txt)  DEBUG((DEBUG_BLKIO, "MMC: " txt "\n"))
+
+#define MMC_IOBLOCKS_READ       0
+#define MMC_IOBLOCKS_WRITE      1
+
+#define MMC_OCR_POWERUP             0x80000000
+
+#define MMC_OCR_ACCESS_MASK         0x3     /* bit[30-29] */
+#define MMC_OCR_ACCESS_BYTE         0x1     /* bit[29] */
+#define MMC_OCR_ACCESS_SECTOR       0x2     /* bit[30] */
+
+#define MMC_CSD_GET_CCC(Response)    (Response[2] >> 20)
+#define MMC_CSD_GET_TRANSPEED(Response)    (Response[3] & 0xFF)
+#define MMC_CSD_GET_READBLLEN(Response)    ((Response[2] >> 16) & 0xF)
+#define MMC_CSD_GET_WRITEBLLEN(Response)  ((Response[0] >> 22) & 0xF)
+#define MMC_CSD_GET_FILEFORMAT(Response)  ((Response[0] >> 10) & 0x3)
+#define MMC_CSD_GET_FILEFORMATGRP(Response)  ((Response[0] >> 15) & 0x1)
+#define MMC_CSD_GET_DEVICESIZE(csd)         (((Response[1] >> 30) & 0x3) | ((Response[2] & 0x3FF) << 2))
+#define HC_MMC_CSD_GET_DEVICESIZE(Response)    ((Response[1] >> 16) | ((Response[2] & 0x3F) << 16));
+#define MMC_CSD_GET_DEVICESIZEMULT(csd)     ((Response[1] >> 15) & 0x7)
+
+#define MMC_R0_READY_FOR_DATA               (1 << 8)
+
+#define MMC_R0_CURRENTSTATE(Response)       ((Response[0] >> 9) & 0xF)
+
+#define MMC_R0_STATE_IDLE       0
+#define MMC_R0_STATE_READY      1
+#define MMC_R0_STATE_IDENT      2
+#define MMC_R0_STATE_STDBY      3
+#define MMC_R0_STATE_TRAN       4
+#define MMC_R0_STATE_DATA       5
+#define MMC_R0_STATE_RECV       6
+#define MMC_R0_STATE_PROG       7
+#define MMC_R0_STATE_DIS        8
+
+
+#define EMMC_CMD6_ARG_ACCESS(x)             (((x) & 0x3) << 24)
+#define EMMC_CMD6_ARG_INDEX(x)              (((x) & 0xFF) << 16)
+#define EMMC_CMD6_ARG_VALUE(x)              (((x) & 0xFF) << 8)
+#define EMMC_CMD6_ARG_CMD_SET(x)            (((x) & 0x7) << 0)
+
+#define SWITCH_CMD_DATA_LENGTH              64
+#define SD_HIGH_SPEED_SUPPORTED             0x200
+#define SD_DEFAULT_SPEED                    25000000
+#define SD_HIGH_SPEED                       50000000
+#define SWITCH_CMD_SUCCESS_MASK             0xf
+
+#define BUSWIDTH_4                          4
+
+typedef enum {
+  UNKNOWN_CARD,
+  MMC_CARD,              //MMC card
+  MMC_CARD_HIGH,         //MMC Card with High capacity
+  EMMC_CARD,             //eMMC 4.41 card
+  SD_CARD,               //SD 1.1 card
+  SD_CARD_2,             //SD 2.0 or above standard card
+  SD_CARD_2_HIGH         //SD 2.0 or above high capacity card
+} CARD_TYPE;
+
+typedef struct {
+  UINT32  Reserved0:   7; // 0
+  UINT32  V170_V195:   1; // 1.70V - 1.95V
+  UINT32  V200_V260:   7; // 2.00V - 2.60V
+  UINT32  V270_V360:   9; // 2.70V - 3.60V
+  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
+} OCR;
+
+typedef struct {
+  UINT8   SD_SPEC:               4; // SD Memory Card - Spec. Version [59:56]
+  UINT8   SCR_STRUCTURE:         4; // SCR Structure [63:60]
+  UINT8   SD_BUS_WIDTHS:         4; // DAT Bus widths supported [51:48]
+  UINT8   DATA_STAT_AFTER_ERASE: 1; // Data Status after erases [55]
+  UINT8   SD_SECURITY:           3; // CPRM Security Support [54:52]
+  UINT8   EX_SECURITY_1:         1; // Extended Security Support [43]
+  UINT8   SD_SPEC4:              1; // Spec. Version 4.00 or higher [42]
+  UINT8   RESERVED_1:            2; // Reserved [41:40]
+  UINT8   SD_SPEC3:              1; // Spec. Version 3.00 or higher [47]
+  UINT8   EX_SECURITY_2:         3; // Extended Security Support [46:44]
+  UINT8   CMD_SUPPORT:           4; // Command Support bits [35:32]
+  UINT8   RESERVED_2:            4; // Reserved [39:36]
+  UINT32  RESERVED_3;               // Manufacturer Usage [31:0]
+} SCR;
+
+typedef struct {
+  UINT32  NOT_USED;   // 1 [0:0]
+  UINT32  CRC;        // CRC7 checksum [7:1]
+
+  UINT32  MDT;        // Manufacturing date [19:8]
+  UINT32  RESERVED_1; // 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]
+} CID;
+
+typedef struct {
+  UINT8   NOT_USED:           1; // Not used, always 1 [0:0]
+  UINT8   CRC:                7; // CRC [7:1]
+
+  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:         2; // Reserved [121:120]
+  UINT8   SPEC_VERS:          4; // System specification version [125:122]
+  UINT8   CSD_STRUCTURE:      2; // CSD structure [127:126]
+} CSD;
+
+typedef struct {
+  UINT8   RESERVED_1[16];                     // Reserved [15:0]
+  UINT8   SECURE_REMOVAL_TYPE;                // Secure Removal Type [16:16]
+  UINT8   PRODUCT_STATE_AWARENESS_ENABLEMENT; // Product state awareness enablement [17:17]
+  UINT8   MAX_PRE_LOADING_DATA_SIZE[4];       // MAX pre loading data size [21:18]
+  UINT8   PRE_LOADING_DATA_SIZE[4];           // Pre loading data size [25:22]
+  UINT8   FFU_STATUS;                         // FFU Status [26:26]
+  UINT8   RESERVED_2[2];                      // Reserved [28:27]
+  UINT8   MODE_OPERATION_CODES;               // Mode operation codes [29:29]
+  UINT8   MODE_CONFIG;                        // Mode config [30:30]
+  UINT8   RESERVED_3;                         // Reserved [31:31]
+  UINT8   FLUSH_CACHE;                        // Flushing of the cache [32:32]
+  UINT8   CACHE_CTRL;                         // Control to turn the cache ON/OFF [33:33]
+  UINT8   POWER_OFF_NOTIFICATION;             // Power Off Notification [34:34]
+  UINT8   PACKED_FAILURE_INDEX;               // Packed command failure index [35:35]
+  UINT8   PACKED_COMMAND_STATUS;              // Packed command status [36:36]
+  UINT8   CONTEXT_CONF[15];                   // Context configuration [51:37]
+  UINT8   EXT_PARTITIONS_ATTRIBUTE[2];        // Extended partitions attribute [53:52]
+  UINT8   EXCEPTION_EVENTS_STATUS[2];         // Exception events status [55:54]
+  UINT8   EXCEPTION_EVENTS_CTRL[2];           // Exception events control [57:56]
+  UINT8   DYNCAP_NEEDED;                      // Number of addressed group to be released [58:58]
+  UINT8   CLASS_6_CTRL;                       // Class 6 commands control [59:59]
+  UINT8   INI_TIMEOUT_EMU;                    // 1st initialization after disabling sector size emulation [60:60]
+  UINT8   DATA_SECTOR_SIZE;                   // Sector size [61:61]
+  UINT8   USE_NATIVE_SECTOR;                  // Sector size emulation [62:62]
+  UINT8   NATIVE_SECTOR_SIZE;                 // Native sector size [63:63]
+  UINT8   VENDOR_SPECIFIC_FIELD[64];          // Vendor specific fields [127:64]
+  UINT8   RESERVED_4[2];                      // Reserved [129:128]
+  UINT8   PROGRAM_CID_CSD_DDR_SUPPORT;        // Program CID/CSD in DDR mode support [130:130]
+  UINT8   PERIODIC_WAKEUP;                    // Periodic wake-up [131:131]
+  UINT8   TCASE_SUPPORT;                      // Package case temperature is controlled [132:132]
+  UINT8   PRODUCTION_STATE_AWARENESS;         // Production state awareness [133:133]
+  UINT8   SECTOR_BAD_BLK_MGMNT;               // Bad block management mode [134:134]
+  UINT8   RESERVED_5;                         // Reserved [135:135]
+  UINT8   ENH_START_ADDR[4];                  // Enhanced user data start address [139:136]
+  UINT8   ENH_SIZE_MULT[3];                   // Enhanced user data area size [142:140]
+  UINT8   GP_SIZE_MULT[12];                   // General purpose partition size [154:143]
+  UINT8   PARTITION_SETTING_COMPLETED;        // Partitioning setting [155:155]
+  UINT8   PARTITIONS_ATTRIBUTE;               // Partitions attribute [156:156]
+  UINT8   MAX_ENH_SIZE_MULT[3];               // Max enhanced area size [159:157]
+  UINT8   PARTITIONING_SUPPORT;               // Partitioning [160:160]
+  UINT8   HPI_MGMT;                           // HPI management [161:161]
+  UINT8   RST_N_FUNCTION;                     // H/W reset function [162:162]
+  UINT8   BKOPS_EN;                           // Enable background operations handshake [163:163]
+  UINT8   BKOPS_START;                        // Manually start background operations [164:164]
+  UINT8   SANITIZE_START;                     // Start sanitize operation [165:165]
+  UINT8   WR_REL_PARAM;                       // Write reliability parameter register [166:166]
+  UINT8   WR_REL_SET;                         // Write reliability setting register [167:167]
+  UINT8   RPMB_SIZE_MULT;                     // RPMB size [168:168]
+  UINT8   FW_CONFIG;                          // FW configuration [169:169]
+  UINT8   RESERVED_6;                         // Reserved [170:170]
+  UINT8   USER_WP;                            // User area write protection register [171:171]
+  UINT8   RESERVED_7;                         // Reserved [172:172]
+  UINT8   BOOT_WP;                            // Boot area write protection register [173:173]
+  UINT8   BOOT_WP_STATUS;                     // Boot write protection register [174:174]
+  UINT8   ERASE_GROUP_DEF;                    // High-density erase group definition [175:175]
+  UINT8   RESERVED_8;                         // Reserved [176:176]
+  UINT8   BOOT_BUS_CONDITIONS;                // Boot bus conditions [177:177]
+  UINT8   BOOT_CONFIG_PROT;                   // Boot config protection [178:178]
+  UINT8   PARTITION_CONFIG;                   // Partition config [179:179]
+  UINT8   RESERVED_9;                         // Reserved [180:180]
+  UINT8   ERASED_MEM_CONT;                    // Erased memory content [181:181]
+  UINT8   RESERVED_10;                        // Reserved [182:182]
+  UINT8   BUS_WIDTH;                          // Bus width mode [183:183]
+  UINT8   RESERVED_11;                        // Reserved [184:184]
+  UINT8   HS_TIMING;                          // High-speed interface timing [185:185]
+  UINT8   RESERVED_12;                        // Reserved [186:186]
+  UINT8   POWER_CLASS;                        // Power class [187:187]
+  UINT8   RESERVED_13;                        // Reserved [188:188]
+  UINT8   CMD_SET_REV;                        // Command set revision [189:189]
+  UINT8   RESERVED_14;                        // Reserved [190:190]
+  UINT8   CMD_SET;                            // Command set [191:191]
+  UINT8   EXT_CSD_REV;                        // Extended CSD revision [192:192]
+  UINT8   RESERVED_15;                        // Reserved [193:193]
+  UINT8   CSD_STRUCTURE;                      // CSD Structure [194:194]
+  UINT8   RESERVED_16;                        // Reserved [195:195]
+  UINT8   DEVICE_TYPE;                        // Device type [196:196]
+  UINT8   DRIVER_STRENGTH;                    // I/O Driver strength [197:197]
+  UINT8   OUT_OF_INTERRUPT_TIME;              // Out-of-interrupt busy timing [198:198]
+  UINT8   PARTITION_SWITCH_TIME;              // Partition switching timing [199:199]
+  UINT8   PWR_CL_52_195;                      // Power class for 52MHz at 1.95V 1 R [200:200]
+  UINT8   PWR_CL_26_195;                      // Power class for 26MHz at 1.95V 1 R [201:201]
+  UINT8   PWR_CL_52_360;                      // Power class for 52MHz at 3.6V 1 R [202:202]
+  UINT8   PWR_CL_26_360;                      // Power class for 26MHz at 3.6V 1 R [203:203]
+  UINT8   RESERVED_17;                        // Reserved [204:204]
+  UINT8   MIN_PERF_R_4_26;                    // Minimum read performance for 4bit at 26MHz [205:205]
+  UINT8   MIN_PERF_W_4_26;                    // Minimum write performance for 4bit at 26MHz [206:206]
+  UINT8   MIN_PERF_R_8_26_4_52;               // Minimum read performance for 8bit at 26MHz, for 4bit at 52MHz [207:207]
+  UINT8   MIN_PERF_W_8_26_4_52;               // Minimum write performance for 8bit at 26MHz, for 4bit at 52MHz [208:208]
+  UINT8   MIN_PERF_R_8_52;                    // Minimum read performance for 8bit at 52MHz [209:209]
+  UINT8   MIN_PERF_W_8_52;                    // Minimum write performance for 8bit at 52MHz [210:210]
+  UINT8   RESERVED_18;                        // Reserved [211:211]
+  UINT32  SECTOR_COUNT;                       // Sector count [215:212]
+  UINT8   SLEEP_NOTIFICATION_TIME;            // Sleep notification timout [216:216]
+  UINT8   S_A_TIMEOUT;                        // Sleep/awake timeout [217:217]
+  UINT8   PRODUCTION_STATE_AWARENESS_TIMEOUT; // Production state awareness timeout [218:218]
+  UINT8   S_C_VCCQ;                           // Sleep current (VCCQ) [219:219]
+  UINT8   S_C_VCC;                            // Sleep current (VCC) [220:220]
+  UINT8   HC_WP_GRP_SIZE;                     // High-capacity write protect group size [221:221]
+  UINT8   REL_WR_SECTOR_C;                    // Reliable write sector count [222:222]
+  UINT8   ERASE_TIMEOUT_MULT;                 // High-capacity erase timeout [223:223]
+  UINT8   HC_ERASE_GRP_SIZE;                  // High-capacity erase unit size [224:224]
+  UINT8   ACC_SIZE;                           // Access size [225:225]
+  UINT8   BOOT_SIZE_MULTI;                    // Boot partition size [226:226]
+  UINT8   RESERVED_19;                        // Reserved [227:227]
+  UINT8   BOOT_INFO;                          // Boot information [228:228]
+  UINT8   SECURE_TRIM_MULT;                   // Secure TRIM Multiplier [229:229]
+  UINT8   SECURE_ERASE_MULT;                  // Secure Erase Multiplier [230:230]
+  UINT8   SECURE_FEATURE_SUPPORT;             // Secure Feature Support [231:231]
+  UINT8   TRIM_MULT;                          // TRIM Multiplier [232:232]
+  UINT8   RESERVED_20;                        // Reserved [233:233]
+  UINT8   MIN_PREF_DDR_R_8_52;                // Minimum read performance for 8bit at 52MHz in DDR mode [234:234]
+  UINT8   MIN_PREF_DDR_W_8_52;                // Minimum write performance for 8bit at 52MHz in DDR mode [235:235]
+  UINT8   PWR_CL_200_130;                     // Power class for 200MHz at VCCQ=1.3V, VCC=3.6V [236:236]
+  UINT8   PWR_CL_200_195;                     // Power class for 200MHz at VCCQ=1.95V, VCC=3.6V [237:237]
+  UINT8   PWR_CL_DDR_52_195;                  // Power class for 52MHz, DDR at 1.95V [238:238]
+  UINT8   PWR_CL_DDR_52_360;                  // Power class for 52Mhz, DDR at 3.6V [239:239]
+  UINT8   RESERVED_21;                        // Reserved [240:240]
+  UINT8   INI_TIMEOUT_AP;                     // 1st initialization time after partitioning [241:241]
+  UINT8   CORRECTLY_PRG_SECTORS_NUM[4];       // Number of correctly programmed sectors [245:242]
+  UINT8   BKOPS_STATUS;                       // Background operations status [246:246]
+  UINT8   POWER_OFF_LONG_TIME;                // Power off notification (long) timeout [247:247]
+  UINT8   GENERIC_CMD6_TIME;                  // Generic CMD6 timeout [248:248]
+  UINT8   CACHE_SIZE[4];                      // Cache size [252:249]
+  UINT8   PWR_CL_DDR_200_360;                 // Power class for 200MHz, DDR at VCC=3.6V [253:253]
+  UINT8   FIRMWARE_VERSION[8];                // Firmware version [261:254]
+  UINT8   DEVICE_VERSION[2];                  // Device version [263:262]
+  UINT8   OPTIMAL_TRIM_UNIT_SIZE;             // Optimal trim unit size [264:264]
+  UINT8   OPTIMAL_WRITE_SIZE;                 // Optimal write size [265:265]
+  UINT8   OPTIMAL_READ_SIZE;                  // Optimal read size [266:266]
+  UINT8   PRE_EOL_INFO;                       // Pre EOL information [267:267]
+  UINT8   DEVICE_LIFE_TIME_EST_TYP_A;         // Device life time estimation type A [268:268]
+  UINT8   DEVICE_LIFE_TIME_EST_TYP_B;         // Device life time estimation type B [269:269]
+  UINT8   VENDOR_PROPRIETARY_HEALTH_REPORT[32];         // Vendor proprietary health report [301:270]
+  UINT8   NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED[4]; // Number of FW sectors correctly programmed [305:302]
+  UINT8   RESERVED_22[181];                   // Reserved [486:306]
+  UINT8   FFU_ARG[4];                         // FFU argument [490:487]
+  UINT8   OPERATION_CODE_TIMEOUT;             // Operation codes timeout [491:491]
+  UINT8   FFU_FEATURES;                       // FFU features [492:492]
+  UINT8   SUPPORTED_MODES;                    // Supported modes [493:493]
+  UINT8   EXT_SUPPORT;                        // Extended partitions attribute support [494:494]
+  UINT8   LARGE_UNIT_SIZE_M1;                 // Large unit size [495:495]
+  UINT8   CONTEXT_CAPABILITIES;               // Context management capabilities [496:496]
+  UINT8   TAG_RES_SIZE;                       // Tag resource size [497:497]
+  UINT8   TAG_UNIT_SIZE;                      // Tag unit size [498:498]
+  UINT8   DATA_TAG_SUPPORT;                   // Data tag support [499:499]
+  UINT8   MAX_PACKED_WRITES;                  // Max packed write commands [500:500]
+  UINT8   MAX_PACKED_READS;                   // Max packed read commands [501:501]
+  UINT8   BKOPS_SUPPORT;                      // Background operations support [502:502]
+  UINT8   HPI_FEATURES;                       // HPI features [503:503]
+  UINT8   S_CMD_SET;                          // Supported command sets [504:504]
+  UINT8   EXT_SECURITY_ERR;                   // Extended security commands error [505:505]
+  UINT8   RESERVED_23[6];                     // Reserved [511:506]
+} ECSD;
+
+typedef struct  {
+  UINT16    RCA;
+  CARD_TYPE CardType;
+  OCR       OCRData;
+  CID       CIDData;
+  CSD       CSDData;
+  ECSD      *ECSDData;                         // MMC V4 extended card specific
+} CARD_INFO;
+
+typedef struct _MMC_HOST_INSTANCE {
+  UINTN                     Signature;
+  LIST_ENTRY                Link;
+  EFI_HANDLE                MmcHandle;
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
+
+  MMC_STATE                 State;
+  EFI_BLOCK_IO_PROTOCOL     BlockIo;
+  CARD_INFO                 CardInfo;
+  EFI_MMC_HOST_PROTOCOL     *MmcHost;
+
+  BOOLEAN                   Initialized;
+} MMC_HOST_INSTANCE;
+
+#define MMC_HOST_INSTANCE_SIGNATURE                 SIGNATURE_32('m', 'm', 'c', 'h')
+#define MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS(a)     CR (a, MMC_HOST_INSTANCE, BlockIo, MMC_HOST_INSTANCE_SIGNATURE)
+#define MMC_HOST_INSTANCE_FROM_LINK(a)              CR (a, MMC_HOST_INSTANCE, Link, MMC_HOST_INSTANCE_SIGNATURE)
+
+
+EFI_STATUS
+EFIAPI
+MmcGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  );
+
+EFI_STATUS
+EFIAPI
+MmcGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  );
+
+extern EFI_COMPONENT_NAME_PROTOCOL  gMmcComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gMmcComponentName2;
+
+extern EFI_DRIVER_DIAGNOSTICS2_PROTOCOL gMmcDriverDiagnostics2;
+
+extern LIST_ENTRY mMmcHostPool;
+
+/**
+  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
+MmcReset (
+  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
+MmcReadBlocks (
+  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
+MmcWriteBlocks (
+  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
+MmcFlushBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL  *This
+  );
+
+EFI_STATUS
+MmcNotifyState (
+  IN MMC_HOST_INSTANCE      *MmcHostInstance,
+  IN MMC_STATE               State
+  );
+
+EFI_STATUS
+InitializeMmcDevice (
+  IN  MMC_HOST_INSTANCE     *MmcHost
+  );
+
+VOID
+EFIAPI
+CheckCardsCallback (
+  IN  EFI_EVENT   Event,
+  IN  VOID        *Context
+  );
+
+VOID
+PrintCSD (
+  IN UINT32* Csd
+  );
+
+VOID
+PrintRCA (
+  IN UINT32 Rca
+  );
+
+VOID
+PrintOCR (
+  IN UINT32 Ocr
+  );
+
+VOID
+PrintResponseR1 (
+  IN  UINT32 Response
+  );
+
+VOID
+PrintCID (
+  IN UINT32* Cid
+  );
+
+#endif
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcBlockIo.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcBlockIo.c
new file mode 100644
index 000000000000..54cdbde95292
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcBlockIo.c
@@ -0,0 +1,473 @@
+/** @file
+ *
+ *  Copyright (c) 2011-2015, 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 <Library/BaseMemoryLib.h>
+
+#include "Mmc.h"
+
+#define MMCI0_BLOCKLEN 512
+#define MMCI0_TIMEOUT  1000
+
+STATIC
+EFI_STATUS
+R1TranAndReady(UINT32 *Response)
+{
+  if ((*Response & MMC_R0_READY_FOR_DATA) != 0 &&
+      MMC_R0_CURRENTSTATE(Response) == MMC_R0_STATE_TRAN) {
+    return EFI_SUCCESS;
+  }
+
+  return EFI_NOT_READY;
+}
+
+STATIC
+EFI_STATUS
+ValidateWrittenBlockCount(
+  IN  MMC_HOST_INSTANCE *MmcHostInstance,
+  IN  UINTN Count,
+  OUT UINTN *TransferredBlocks
+  )
+{
+  UINT32 R1;
+  UINT8 Data[4];
+  EFI_STATUS Status;
+  UINT32 BlocksWritten;
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  if (MmcHostInstance->CardInfo.CardType == MMC_CARD ||
+      MmcHostInstance->CardInfo.CardType == MMC_CARD_HIGH ||
+      MmcHostInstance->CardInfo.CardType == EMMC_CARD) {
+    /*
+     * Not on MMC.
+     */
+    return EFI_SUCCESS;
+  }
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD55,
+                                 MmcHostInstance->CardInfo.RCA << 16);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(%u): error: %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  }
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_ACMD22, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(%u): error: %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  }
+
+  MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, &R1);
+  Status = R1TranAndReady(&R1);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Read Data
+  Status = MmcHost->ReadBlockData (MmcHost, 0, sizeof(Data),
+                                   (VOID *) Data);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(%u): error: %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  }
+
+  /*
+   * Big Endian.
+   */
+  BlocksWritten = ((UINT32) Data[0] << 24) |
+    ((UINT32) Data[1] << 16) |
+    ((UINT32) Data[2] << 8) |
+    ((UINT32) Data[3] << 0);
+  if (BlocksWritten != Count) {
+    DEBUG ((DEBUG_ERROR, "%a(%u): expected %u != gotten %u\n",
+            __FUNCTION__, __LINE__, Count, BlocksWritten));
+    if (BlocksWritten == 0) {
+      return EFI_DEVICE_ERROR;
+    }
+  }
+
+  *TransferredBlocks = BlocksWritten;
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+WaitUntilTran(
+  IN MMC_HOST_INSTANCE *MmcHostInstance
+  )
+{
+  INTN Timeout;
+  UINT32 Response[1];
+  EFI_STATUS Status = EFI_SUCCESS;
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  Timeout = MMCI0_TIMEOUT;
+  while(Timeout--) {
+    /*
+     * We expect CMD13 to timeout while card is programming,
+     * because the card holds DAT0 low (busy).
+     */
+    Status = MmcHost->SendCommand (MmcHost, MMC_CMD13,
+       MmcHostInstance->CardInfo.RCA << 16);
+    if (EFI_ERROR(Status) && Status != EFI_TIMEOUT) {
+      DEBUG ((DEBUG_ERROR, "%a(%u) CMD13 failed: %r\n",
+              __FUNCTION__, __LINE__, Status));
+      break;
+    }
+
+    if (Status == EFI_SUCCESS) {
+      MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
+      Status = R1TranAndReady(Response);
+      if (!EFI_ERROR(Status)) {
+        break;
+      }
+    }
+
+    gBS->Stall(1000);
+  }
+
+  if (0 == Timeout) {
+    DEBUG ((DEBUG_ERROR, "%a(%u) card is busy\n", __FUNCTION__, __LINE__));
+    return EFI_NOT_READY;
+  }
+
+  return Status;
+}
+
+EFI_STATUS
+MmcNotifyState (
+  IN MMC_HOST_INSTANCE *MmcHostInstance,
+  IN MMC_STATE State
+  )
+{
+  MmcHostInstance->State = State;
+  return MmcHostInstance->MmcHost->NotifyState (MmcHostInstance->MmcHost, State);
+}
+
+EFI_STATUS
+EFIAPI
+MmcReset (
+  IN EFI_BLOCK_IO_PROTOCOL    *This,
+  IN BOOLEAN                  ExtendedVerification
+  )
+{
+  MMC_HOST_INSTANCE       *MmcHostInstance;
+
+  MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
+
+  if (MmcHostInstance->MmcHost == NULL) {
+    // Nothing to do
+    return EFI_SUCCESS;
+  }
+
+  // If a card is not present then clear all media settings
+  if (!MmcHostInstance->MmcHost->IsCardPresent (MmcHostInstance->MmcHost)) {
+    MmcHostInstance->BlockIo.Media->MediaPresent = FALSE;
+    MmcHostInstance->BlockIo.Media->LastBlock    = 0;
+    MmcHostInstance->BlockIo.Media->BlockSize    = 512;  // Should be zero but there is a bug in DiskIo
+    MmcHostInstance->BlockIo.Media->ReadOnly     = FALSE;
+
+    // Indicate that the driver requires initialization
+    MmcHostInstance->State = MmcHwInitializationState;
+
+    return EFI_SUCCESS;
+  }
+
+  // Implement me. Either send a CMD0 (could not work for some MMC host) or just turn off/turn
+  //      on power and restart Identification mode
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+MmcDetectCard (
+  EFI_MMC_HOST_PROTOCOL     *MmcHost
+  )
+{
+  if (!MmcHost->IsCardPresent (MmcHost)) {
+    return EFI_NO_MEDIA;
+  } else {
+    return EFI_SUCCESS;
+  }
+}
+
+EFI_STATUS
+MmcStopTransmission (
+  EFI_MMC_HOST_PROTOCOL     *MmcHost
+  )
+{
+  EFI_STATUS              Status;
+  UINT32                  Response[4];
+  // Command 12 - Stop transmission (ends read or write)
+  // Normally only needed for streaming transfers or after error.
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD12, 0);
+  if (!EFI_ERROR (Status)) {
+    MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1b, Response);
+  }
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+MmcTransferBlock (
+  IN EFI_BLOCK_IO_PROTOCOL    *This,
+  IN UINTN                    Cmd,
+  IN UINTN                    Transfer,
+  IN UINT32                   MediaId,
+  IN EFI_LBA                  Lba,
+  IN UINTN                    BufferSize,
+  OUT VOID                    *Buffer,
+  OUT UINTN                   *TransferredSize
+  )
+{
+  EFI_STATUS              Status;
+  MMC_HOST_INSTANCE       *MmcHostInstance;
+  EFI_MMC_HOST_PROTOCOL   *MmcHost;
+  UINTN                   CmdArg;
+
+  MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
+  MmcHost = MmcHostInstance->MmcHost;
+
+  //Set command argument based on the card access mode (Byte mode or Block mode)
+  if ((MmcHostInstance->CardInfo.OCRData.AccessMode & MMC_OCR_ACCESS_MASK) ==
+      MMC_OCR_ACCESS_SECTOR) {
+    CmdArg = Lba;
+  } else {
+    CmdArg = Lba * This->Media->BlockSize;
+  }
+
+  Status = MmcHost->SendCommand (MmcHost, Cmd, CmdArg);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(MMC_CMD%d): Error %r\n", __func__,
+            MMC_INDX(Cmd), Status));
+    return Status;
+  }
+
+  if (Transfer == MMC_IOBLOCKS_READ) {
+    Status = MmcHost->ReadBlockData (MmcHost, Lba, BufferSize, Buffer);
+  } else {
+    Status = MmcHost->WriteBlockData (MmcHost, Lba, BufferSize, Buffer);
+    if (!EFI_ERROR (Status)) {
+      Status = MmcNotifyState (MmcHostInstance, MmcProgrammingState);
+      if (EFI_ERROR (Status)) {
+        DEBUG ((DEBUG_ERROR, "%a() : Error MmcProgrammingState\n", __func__));
+        return Status;
+      }
+    }
+  }
+
+  if (EFI_ERROR (Status) ||
+      BufferSize > This->Media->BlockSize) {
+    /*
+     * CMD12 needs to be set for multiblock (to transition from
+     * RECV to PROG) or for errors.
+     */
+    EFI_STATUS Status2 = MmcStopTransmission (MmcHost);
+    if (EFI_ERROR (Status2)) {
+      DEBUG ((DEBUG_ERROR, "MmcIoBlocks() : CMD12 error on Status %r: %r\n",
+              Status, Status2));
+      return Status2;
+    }
+
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_BLKIO, "%a(): Error %a Block Data and Status = %r\n",
+              __func__, Transfer == MMC_IOBLOCKS_READ ? "Read" : "Write",
+              Status));
+      return Status;
+    }
+
+    ASSERT (Cmd == MMC_CMD25 || Cmd == MMC_CMD18);
+  }
+
+  //
+  // For reads, should be already in TRAN. For writes, wait
+  // until programming finishes.
+  //
+  Status = WaitUntilTran(MmcHostInstance);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "WaitUntilTran after write failed\n"));
+    return Status;
+  }
+
+  Status = MmcNotifyState (MmcHostInstance, MmcTransferState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIoBlocks() : Error MmcTransferState\n"));
+    return Status;
+  }
+
+  if (Transfer != MMC_IOBLOCKS_READ) {
+    UINTN BlocksWritten = 0;
+
+    Status = ValidateWrittenBlockCount (MmcHostInstance,
+                                        BufferSize /
+                                        This->Media->BlockSize,
+                                        &BlocksWritten);
+    *TransferredSize = BlocksWritten *
+      This->Media->BlockSize;
+  } else {
+    *TransferredSize = BufferSize;
+  }
+
+  return Status;
+}
+
+EFI_STATUS
+MmcIoBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL    *This,
+  IN UINTN                    Transfer,
+  IN UINT32                   MediaId,
+  IN EFI_LBA                  Lba,
+  IN UINTN                    BufferSize,
+  OUT VOID                    *Buffer
+  )
+{
+  EFI_STATUS              Status;
+  UINTN                   Cmd;
+  MMC_HOST_INSTANCE       *MmcHostInstance;
+  EFI_MMC_HOST_PROTOCOL   *MmcHost;
+  UINTN                   BytesRemainingToBeTransfered;
+  UINTN                   BlockCount;
+  UINTN                   ConsumeSize;
+
+  BlockCount = 1;
+  MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
+  ASSERT (MmcHostInstance != NULL);
+  MmcHost = MmcHostInstance->MmcHost;
+  ASSERT (MmcHost);
+
+  if (This->Media->MediaId != MediaId) {
+    return EFI_MEDIA_CHANGED;
+  }
+
+  if ((MmcHost == NULL) || (Buffer == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check if a Card is Present
+  if (!MmcHostInstance->BlockIo.Media->MediaPresent) {
+    return EFI_NO_MEDIA;
+  }
+
+  if (PcdGet32(PcdMmcDisableMulti) == 0 &&
+      MMC_HOST_HAS_ISMULTIBLOCK(MmcHost) &&
+      MmcHost->IsMultiBlock(MmcHost)) {
+    BlockCount = (BufferSize + This->Media->BlockSize - 1) / This->Media->BlockSize;
+  }
+
+  // All blocks must be within the device
+  if ((Lba + (BufferSize / This->Media->BlockSize)) > (This->Media->LastBlock + 1)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((Transfer == MMC_IOBLOCKS_WRITE) && (This->Media->ReadOnly == TRUE)) {
+    return EFI_WRITE_PROTECTED;
+  }
+
+  // Reading 0 Byte is valid
+  if (BufferSize == 0) {
+    return EFI_SUCCESS;
+  }
+
+  // The buffer size must be an exact multiple of the block size
+  if ((BufferSize % This->Media->BlockSize) != 0) {
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  // Check the alignment
+  if ((This->Media->IoAlign > 2) && (((UINTN)Buffer & (This->Media->IoAlign - 1)) != 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  BytesRemainingToBeTransfered = BufferSize;
+  while (BytesRemainingToBeTransfered > 0) {
+    Status = WaitUntilTran(MmcHostInstance);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "WaitUntilTran before IO failed"));
+      return Status;
+    }
+
+    if (Transfer == MMC_IOBLOCKS_READ) {
+      if (BlockCount == 1) {
+        // Read a single block
+        Cmd = MMC_CMD17;
+      } else {
+        // Read multiple blocks
+        Cmd = MMC_CMD18;
+      }
+    } else {
+      if (BlockCount == 1) {
+        // Write a single block
+        Cmd = MMC_CMD24;
+      } else {
+        // Write multiple blocks
+        Cmd = MMC_CMD25;
+      }
+    }
+
+    ConsumeSize = BlockCount * This->Media->BlockSize;
+    if (BytesRemainingToBeTransfered < ConsumeSize) {
+      ConsumeSize = BytesRemainingToBeTransfered;
+    }
+
+    Status = MmcTransferBlock (This, Cmd, Transfer, MediaId, Lba, ConsumeSize, Buffer, &ConsumeSize);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a(): Failed to transfer block and Status:%r\n", __func__, Status));
+      return Status;
+    }
+
+    BytesRemainingToBeTransfered -= ConsumeSize;
+    if (BytesRemainingToBeTransfered > 0) {
+      Lba    += BlockCount;
+      Buffer = (UINT8 *)Buffer + ConsumeSize;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+MmcReadBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL    *This,
+  IN UINT32                   MediaId,
+  IN EFI_LBA                  Lba,
+  IN UINTN                    BufferSize,
+  OUT VOID                    *Buffer
+  )
+{
+  return MmcIoBlocks (This, MMC_IOBLOCKS_READ, MediaId, Lba, BufferSize, Buffer);
+}
+
+EFI_STATUS
+EFIAPI
+MmcWriteBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL    *This,
+  IN UINT32                   MediaId,
+  IN EFI_LBA                  Lba,
+  IN UINTN                    BufferSize,
+  IN VOID                     *Buffer
+  )
+{
+  return MmcIoBlocks (This, MMC_IOBLOCKS_WRITE, MediaId, Lba, BufferSize, Buffer);
+}
+
+EFI_STATUS
+EFIAPI
+MmcFlushBlocks (
+  IN EFI_BLOCK_IO_PROTOCOL  *This
+  )
+{
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDebug.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDebug.c
new file mode 100644
index 000000000000..ea53700caaf8
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDebug.c
@@ -0,0 +1,169 @@
+/** @file
+ *
+ *  Copyright (c) 2011-2013, 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 "Mmc.h"
+
+#if !defined(MDEPKG_NDEBUG)
+CONST CHAR8* mStrUnit[] = { "100kbit/s", "1Mbit/s", "10Mbit/s", "100MBit/s",
+                            "Unknown", "Unknown", "Unknown", "Unknown" };
+CONST CHAR8* mStrValue[] = { "1.0", "1.2", "1.3", "1.5", "2.0", "2.5",
+                             "3.0", "3.5", "4.0", "4.5", "5.0", "5.5",
+                             "6.0", "7.0", "8.0" };
+#endif
+
+VOID
+PrintCID (
+  IN UINT32* Cid
+  )
+{
+  DEBUG ((DEBUG_ERROR, "- PrintCID\n"));
+  DEBUG ((DEBUG_ERROR, "\t- Manufacturing date: %d/%d\n", (Cid[0] >> 8) & 0xF, (Cid[0] >> 12) & 0xFF));
+  DEBUG ((DEBUG_ERROR, "\t- Product serial number: 0x%X%X\n", Cid[1] & 0xFFFFFF, (Cid[0] >> 24) & 0xFF));
+  DEBUG ((DEBUG_ERROR, "\t- Product revision: %d\n", Cid[1] >> 24));
+  //DEBUG ((DEBUG_ERROR, "\t- Product name: %s\n", (char*)(Cid + 2)));
+  DEBUG ((DEBUG_ERROR, "\t- OEM ID: %c%c\n", (Cid[3] >> 8) & 0xFF, (Cid[3] >> 16) & 0xFF));
+}
+
+
+VOID
+PrintCSD (
+  IN UINT32* Csd
+  )
+{
+  UINTN Value;
+
+  if (((Csd[2] >> 30) & 0x3) == 0) {
+    DEBUG ((DEBUG_ERROR, "- PrintCSD Version 1.01-1.10/Version 2.00/Standard Capacity\n"));
+  } else if (((Csd[2] >> 30) & 0x3) == 1) {
+    DEBUG ((DEBUG_ERROR, "- PrintCSD Version 2.00/High Capacity\n"));
+  } else {
+    DEBUG ((DEBUG_ERROR, "- PrintCSD Version Higher than v3.3\n"));
+  }
+
+  DEBUG ((DEBUG_ERROR, "\t- Supported card command class: 0x%X\n", MMC_CSD_GET_CCC (Csd)));
+  DEBUG ((DEBUG_ERROR, "\t- Max Speed: %a * %a\n",mStrValue[(MMC_CSD_GET_TRANSPEED (Csd) >> 3) & 0xF],mStrUnit[MMC_CSD_GET_TRANSPEED (Csd) & 7]));
+  DEBUG ((DEBUG_ERROR, "\t- Maximum Read Data Block: %d\n",2 << (MMC_CSD_GET_READBLLEN (Csd)-1)));
+  DEBUG ((DEBUG_ERROR, "\t- Maximum Write Data Block: %d\n",2 << (MMC_CSD_GET_WRITEBLLEN (Csd)-1)));
+
+  if (!MMC_CSD_GET_FILEFORMATGRP (Csd)) {
+    Value = MMC_CSD_GET_FILEFORMAT (Csd);
+    if (Value == 0) {
+      DEBUG ((DEBUG_ERROR, "\t- Format (0): Hard disk-like file system with partition table\n"));
+    } else if (Value == 1) {
+      DEBUG ((DEBUG_ERROR, "\t- Format (1): DOS FAT (floppy-like) with boot sector only (no partition table)\n"));
+    } else if (Value == 2) {
+      DEBUG ((DEBUG_ERROR, "\t- Format (2): Universal File Format\n"));
+    } else {
+      DEBUG ((DEBUG_ERROR, "\t- Format (3): Others/Unknown\n"));
+    }
+  } else {
+    DEBUG ((DEBUG_ERROR, "\t- Format: Reserved\n"));
+  }
+}
+
+VOID
+PrintRCA (
+  IN UINT32 Rca
+  )
+{
+  DEBUG ((DEBUG_ERROR, "- PrintRCA: 0x%X\n", Rca));
+  DEBUG ((DEBUG_ERROR, "\t- Status: 0x%X\n", Rca & 0xFFFF));
+  DEBUG ((DEBUG_ERROR, "\t- RCA: 0x%X\n", (Rca >> 16) & 0xFFFF));
+}
+
+VOID
+PrintOCR (
+  IN UINT32 Ocr
+  )
+{
+  UINTN MinV;
+  UINTN MaxV;
+  UINTN Volts;
+  UINTN Loop;
+
+  MinV  = 36;  // 3.6
+  MaxV  = 20;  // 2.0
+  Volts = 20;  // 2.0
+
+  // The MMC register bits [23:8] indicate the working range of the card
+  for (Loop = 8; Loop < 24; Loop++) {
+    if (Ocr & (1 << Loop)) {
+      if (MinV > Volts) {
+        MinV = Volts;
+      }
+      if (MaxV < Volts) {
+        MaxV = Volts + 1;
+      }
+    }
+    Volts++;
+  }
+
+  DEBUG ((DEBUG_ERROR, "- PrintOCR Ocr (0x%X)\n",Ocr));
+  DEBUG ((DEBUG_ERROR, "\t- Card operating voltage: %d.%d to %d.%d\n", MinV/10, MinV % 10, MaxV/10, MaxV % 10));
+  if (((Ocr >> 29) & 3) == 0) {
+    DEBUG ((DEBUG_ERROR, "\t- AccessMode: Byte Mode\n"));
+  } else {
+    DEBUG ((DEBUG_ERROR, "\t- AccessMode: Block Mode (0x%X)\n", ((Ocr >> 29) & 3)));
+  }
+
+  if (Ocr & MMC_OCR_POWERUP) {
+    DEBUG ((DEBUG_ERROR, "\t- PowerUp\n"));
+  } else {
+    DEBUG ((DEBUG_ERROR, "\t- Voltage Not Supported\n"));
+  }
+}
+
+VOID
+PrintResponseR1 (
+  IN  UINT32 Response
+  )
+{
+  DEBUG ((DEBUG_INFO, "Response: 0x%X\n", Response));
+  if (Response & MMC_R0_READY_FOR_DATA) {
+    DEBUG ((DEBUG_INFO, "\t- READY_FOR_DATA\n"));
+  }
+
+  switch ((Response >> 9) & 0xF) {
+  case 0:
+    DEBUG ((DEBUG_INFO, "\t- State: Idle\n"));
+    break;
+  case 1:
+    DEBUG ((DEBUG_INFO, "\t- State: Ready\n"));
+    break;
+  case 2:
+    DEBUG ((DEBUG_INFO, "\t- State: Ident\n"));
+    break;
+  case 3:
+    DEBUG ((DEBUG_INFO, "\t- State: StandBy\n"));
+    break;
+  case 4:
+    DEBUG ((DEBUG_INFO, "\t- State: Tran\n"));
+    break;
+  case 5:
+    DEBUG ((DEBUG_INFO, "\t- State: Data\n"));
+    break;
+  case 6:
+    DEBUG ((DEBUG_INFO, "\t- State: Rcv\n"));
+    break;
+  case 7:
+    DEBUG ((DEBUG_INFO, "\t- State: Prg\n"));
+    break;
+  case 8:
+    DEBUG ((DEBUG_INFO, "\t- State: Dis\n"));
+    break;
+  default:
+    DEBUG ((DEBUG_INFO, "\t- State: Reserved\n"));
+    break;
+  }
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDxe.inf b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDxe.inf
new file mode 100644
index 000000000000..fd91f47aeaf0
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDxe.inf
@@ -0,0 +1,58 @@
+#/** @file
+#
+#  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+#  Copyright (c) 2011-2015, 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.
+#
+#**/
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = PiMmcDxe
+  FILE_GUID                      = b6f44cc0-9e45-11df-be21-0002a5f5f51b
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = MmcDxeInitialize
+
+[Sources.common]
+  ComponentName.c
+  Mmc.c
+  MmcBlockIo.c
+  MmcIdentification.c
+  MmcDebug.c
+  Diagnostics.c
+
+[Packages]
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  BaseLib
+  UefiLib
+  UefiDriverEntryPoint
+  BaseMemoryLib
+
+[Protocols]
+  gEfiDiskIoProtocolGuid
+  gEfiBlockIoProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiDriverDiagnostics2ProtocolGuid
+  gRaspberryPiMmcHostProtocolGuid
+
+[Pcd]
+  gRaspberryPiTokenSpaceGuid.PcdMmcForce1Bit
+  gRaspberryPiTokenSpaceGuid.PcdMmcForceDefaultSpeed
+  gRaspberryPiTokenSpaceGuid.PcdMmcSdDefaultSpeedMHz
+  gRaspberryPiTokenSpaceGuid.PcdMmcSdHighSpeedMHz
+  gRaspberryPiTokenSpaceGuid.PcdMmcDisableMulti
+
+[Depex]
+  TRUE
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcIdentification.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcIdentification.c
new file mode 100644
index 000000000000..ef819fb753c6
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcIdentification.c
@@ -0,0 +1,993 @@
+/** @file
+ *
+ *  Copyright (c) 2011-2015, 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 <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/TimerLib.h>
+
+#include "Mmc.h"
+
+typedef union {
+  UINT32 Raw;
+  OCR    Ocr;
+} OCR_RESPONSE;
+
+#define MAX_RETRY_COUNT         1000
+#define CMD_RETRY_COUNT         20
+#define RCA_SHIFT_OFFSET        16
+#define EMMC_CARD_SIZE          512
+#define EMMC_ECSD_SIZE_OFFSET   53
+
+#define EXTCSD_BUS_WIDTH        183
+#define EXTCSD_HS_TIMING        185
+
+#define EMMC_TIMING_BACKWARD    0
+#define EMMC_TIMING_HS          1
+#define EMMC_TIMING_HS200       2
+#define EMMC_TIMING_HS400       3
+
+#define EMMC_BUS_WIDTH_1BIT     0
+#define EMMC_BUS_WIDTH_4BIT     1
+#define EMMC_BUS_WIDTH_8BIT     2
+#define EMMC_BUS_WIDTH_DDR_4BIT 5
+#define EMMC_BUS_WIDTH_DDR_8BIT 6
+
+#define EMMC_SWITCH_ERROR       (1 << 7)
+
+#define SD_BUS_WIDTH_1BIT       (1 << 0)
+#define SD_BUS_WIDTH_4BIT       (1 << 2)
+
+#define SD_CCC_SWITCH           (1 << 10)
+
+#define DEVICE_STATE(x)         (((x) >> 9) & 0xf)
+typedef enum _EMMC_DEVICE_STATE {
+  EMMC_IDLE_STATE = 0,
+  EMMC_READY_STATE,
+  EMMC_IDENT_STATE,
+  EMMC_STBY_STATE,
+  EMMC_TRAN_STATE,
+  EMMC_DATA_STATE,
+  EMMC_RCV_STATE,
+  EMMC_PRG_STATE,
+  EMMC_DIS_STATE,
+  EMMC_BTST_STATE,
+  EMMC_SLP_STATE
+} EMMC_DEVICE_STATE;
+
+UINT32 mEmmcRcaCount = 0;
+
+STATIC
+EFI_STATUS
+EFIAPI
+EmmcGetDeviceState (
+  IN  MMC_HOST_INSTANCE    *MmcHostInstance,
+  OUT EMMC_DEVICE_STATE    *State
+  )
+{
+  EFI_MMC_HOST_PROTOCOL *Host;
+  EFI_STATUS Status;
+  UINT32     Data, RCA;
+
+  if (State == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Host = MmcHostInstance->MmcHost;
+  RCA = MmcHostInstance->CardInfo.RCA << RCA_SHIFT_OFFSET;
+  Status = Host->SendCommand (Host, MMC_CMD13, RCA);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcGetDeviceState(): Failed to get card status, Status=%r.\n", Status));
+    return Status;
+  }
+  Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R1, &Data);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcGetDeviceState(): Failed to get response of CMD13, Status=%r.\n", Status));
+    return Status;
+  }
+  if (Data & EMMC_SWITCH_ERROR) {
+    DEBUG ((DEBUG_ERROR, "EmmcGetDeviceState(): Failed to switch expected mode, Status=%r.\n", Status));
+    return EFI_DEVICE_ERROR;
+  }
+  *State = DEVICE_STATE(Data);
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+EmmcSetEXTCSD (
+  IN MMC_HOST_INSTANCE     *MmcHostInstance,
+  UINT32                   ExtCmdIndex,
+  UINT32                   Value
+  )
+{
+  EFI_MMC_HOST_PROTOCOL *Host;
+  EMMC_DEVICE_STATE     State;
+  EFI_STATUS Status;
+  UINT32     Argument;
+
+  Host  = MmcHostInstance->MmcHost;
+  Argument = EMMC_CMD6_ARG_ACCESS(3) | EMMC_CMD6_ARG_INDEX(ExtCmdIndex) |
+             EMMC_CMD6_ARG_VALUE(Value) | EMMC_CMD6_ARG_CMD_SET(1);
+  Status = Host->SendCommand (Host, MMC_CMD6, Argument);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcSetEXTCSD(): Failed to send CMD6, Status=%r.\n", Status));
+    return Status;
+  }
+  // Make sure device exiting prog mode
+  do {
+    Status = EmmcGetDeviceState (MmcHostInstance, &State);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "EmmcSetEXTCSD(): Failed to get device state, Status=%r.\n", Status));
+      return Status;
+    }
+  } while (State == EMMC_PRG_STATE);
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+EmmcIdentificationMode (
+  IN MMC_HOST_INSTANCE     *MmcHostInstance,
+  IN OCR_RESPONSE           Response
+  )
+{
+  EFI_MMC_HOST_PROTOCOL *Host;
+  EFI_BLOCK_IO_MEDIA    *Media;
+  EFI_STATUS Status;
+  EMMC_DEVICE_STATE     State;
+  UINT32     RCA;
+
+  Host  = MmcHostInstance->MmcHost;
+  Media = MmcHostInstance->BlockIo.Media;
+
+  // Fetch card identity register
+  Status = Host->SendCommand (Host, MMC_CMD2, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Failed to send CMD2, Status=%r.\n", Status));
+    return Status;
+  }
+
+  Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R2, (UINT32 *)&(MmcHostInstance->CardInfo.CIDData));
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): CID retrieval error, Status=%r.\n", Status));
+    return Status;
+  }
+
+  // Assign a relative address value to the card
+  MmcHostInstance->CardInfo.RCA = ++mEmmcRcaCount; // TODO: might need a more sophisticated way of doing this
+  RCA = MmcHostInstance->CardInfo.RCA << RCA_SHIFT_OFFSET;
+  Status = Host->SendCommand (Host, MMC_CMD3, RCA);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): RCA set error, Status=%r.\n", Status));
+    return Status;
+  }
+
+  // Fetch card specific data
+  Status = Host->SendCommand (Host, MMC_CMD9, RCA);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Failed to send CMD9, Status=%r.\n", Status));
+    return Status;
+  }
+
+  Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R2, (UINT32 *)&(MmcHostInstance->CardInfo.CSDData));
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): CSD retrieval error, Status=%r.\n", Status));
+    return Status;
+  }
+
+  // Select the card
+  Status = Host->SendCommand (Host, MMC_CMD7, RCA);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Card selection error, Status=%r.\n", Status));
+  }
+
+  if (MMC_HOST_HAS_SETIOS(Host)) {
+    // Set 1-bit bus width
+    Status = Host->SetIos (Host, 0, 1, EMMCBACKWARD);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Set 1-bit bus width error, Status=%r.\n", Status));
+      return Status;
+    }
+
+    // Set 1-bit bus width for EXTCSD
+    Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_BUS_WIDTH, EMMC_BUS_WIDTH_1BIT);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Set extcsd bus width error, Status=%r.\n", Status));
+      return Status;
+    }
+  }
+
+  // Fetch ECSD
+  MmcHostInstance->CardInfo.ECSDData = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (ECSD)));
+  if (MmcHostInstance->CardInfo.ECSDData == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  Status = Host->SendCommand (Host, MMC_CMD8, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): ECSD fetch error, Status=%r.\n", Status));
+  }
+
+  Status = Host->ReadBlockData (Host, 0, 512, (UINT32 *)MmcHostInstance->CardInfo.ECSDData);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): ECSD read error, Status=%r.\n", Status));
+    goto FreePageExit;
+  }
+
+  // Make sure device exiting data mode
+  do {
+    Status = EmmcGetDeviceState (MmcHostInstance, &State);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Failed to get device state, Status=%r.\n", Status));
+      goto FreePageExit;
+    }
+  } while (State == EMMC_DATA_STATE);
+
+  // Set up media
+  Media->BlockSize = EMMC_CARD_SIZE; // 512-byte support is mandatory for eMMC cards
+  Media->MediaId = MmcHostInstance->CardInfo.CIDData.PSN;
+  Media->ReadOnly = MmcHostInstance->CardInfo.CSDData.PERM_WRITE_PROTECT;
+  Media->LogicalBlocksPerPhysicalBlock = 1;
+  Media->IoAlign = 4;
+  // Compute last block using bits [215:212] of the ECSD
+  Media->LastBlock = MmcHostInstance->CardInfo.ECSDData->SECTOR_COUNT - 1; // eMMC isn't supposed to report this for
+  // Cards <2GB in size, but the model does.
+
+  // Setup card type
+  MmcHostInstance->CardInfo.CardType = EMMC_CARD;
+  return EFI_SUCCESS;
+
+FreePageExit:
+  FreePages (MmcHostInstance->CardInfo.ECSDData, EFI_SIZE_TO_PAGES (sizeof (ECSD)));
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+InitializeEmmcDevice (
+  IN  MMC_HOST_INSTANCE   *MmcHostInstance
+  )
+{
+  EFI_MMC_HOST_PROTOCOL *Host;
+  EFI_STATUS Status = EFI_SUCCESS;
+  ECSD       *ECSDData;
+  UINT32     BusClockFreq, Idx, BusMode;
+  UINT32     BusWidth = 8;
+  UINT32     TimingMode[4] = {EMMCHS52DDR1V2, EMMCHS52DDR1V8, EMMCHS52, EMMCHS26};
+
+  Host  = MmcHostInstance->MmcHost;
+  ECSDData = MmcHostInstance->CardInfo.ECSDData;
+  if (ECSDData->DEVICE_TYPE == EMMCBACKWARD){
+    return EFI_SUCCESS;
+  }
+
+  if (PcdGet32(PcdMmcForceDefaultSpeed)) {
+    DEBUG((DEBUG_WARN, "Forcing default speed mode\n"));
+    return EFI_SUCCESS;
+  }
+
+  if (PcdGet32(PcdMmcForce1Bit)) {
+    DEBUG((DEBUG_WARN, "Forcing 1 bit mode\n"));
+    BusWidth = 1;
+  }
+
+  if (!MMC_HOST_HAS_SETIOS(Host)) {
+    DEBUG((DEBUG_ERROR, "Controller doesn't support speed / bus width change\n"));
+    return EFI_SUCCESS;
+  }
+
+  Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_HS_TIMING, EMMC_TIMING_HS);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "InitializeEmmcDevice(): Failed to switch high speed mode, Status:%r.\n", Status));
+    return Status;
+  }
+
+  for (Idx = 0; Idx < 4; Idx++) {
+    switch (TimingMode[Idx]) {
+    case EMMCHS52DDR1V2:
+    case EMMCHS52DDR1V8:
+    case EMMCHS52:
+      BusClockFreq = 52000000;
+      break;
+    case EMMCHS26:
+      BusClockFreq = 26000000;
+      break;
+    default:
+      return EFI_UNSUPPORTED;
+    }
+    Status = Host->SetIos (Host, BusClockFreq, BusWidth, TimingMode[Idx]);
+    if (!EFI_ERROR (Status)) {
+      switch (TimingMode[Idx]) {
+      case EMMCHS52DDR1V2:
+      case EMMCHS52DDR1V8:
+        BusMode = EMMC_BUS_WIDTH_DDR_8BIT;
+        break;
+      case EMMCHS52:
+      case EMMCHS26:
+        BusMode = EMMC_BUS_WIDTH_8BIT;
+        break;
+      default:
+        return EFI_UNSUPPORTED;
+      }
+      Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_BUS_WIDTH, BusMode);
+      if (EFI_ERROR (Status)) {
+        DEBUG ((DEBUG_ERROR, "InitializeEmmcDevice(): Failed to set EXTCSD bus width, Status:%r\n", Status));
+      }
+      return Status;
+    }
+  }
+  return Status;
+}
+
+STATIC
+UINT32
+SdSwitchCmdArgument (
+  IN     UINT32  AccessMode,
+  IN     UINT32  CommandSystem,
+  IN     UINT32  DriveStrength,
+  IN     UINT32  PowerLimit,
+  IN     BOOLEAN Mode
+  )
+{
+  return (AccessMode & 0xF) | ((PowerLimit & 0xF) << 4) |               \
+    ((DriveStrength & 0xF) << 8) | ((DriveStrength & 0xF) << 12) |      \
+    (Mode ? BIT31 : 0);
+}
+
+STATIC
+EFI_STATUS
+SdSelect (
+  IN  MMC_HOST_INSTANCE *MmcHostInstance
+  )
+{
+  /*
+   * Moves a card from standby to transfer state.
+   */
+  EFI_STATUS Status;
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD7,
+                                 MmcHostInstance->CardInfo.RCA << 16);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a: error: %r\n",
+           __FUNCTION__, Status));
+  }
+
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+SdDeselect (
+  IN  MMC_HOST_INSTANCE *MmcHostInstance
+  )
+{
+  /*
+   * Moves a card from transfer to standby.
+   */
+  EFI_STATUS Status;
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD7, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a: error: %r\n",
+           __FUNCTION__, Status));
+  }
+
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+SdGetCsd(
+  IN  MMC_HOST_INSTANCE *MmcHostInstance,
+  IN  UINT32 *Response,
+  IN  BOOLEAN Print
+  )
+{
+  EFI_STATUS Status;
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD9,
+                                 MmcHostInstance->CardInfo.RCA << 16);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a(%u): error: %r\n", __FUNCTION__,
+           __LINE__, Status));
+    return Status;
+  }
+
+  Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_CSD,
+                                     Response);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a(%u): error %r\n", __FUNCTION__,
+           __LINE__, Status));
+    return Status;
+  }
+
+  if (Print) {
+    PrintCSD (Response);
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+SdSet4Bit(
+  IN  MMC_HOST_INSTANCE *MmcHostInstance
+  )
+{
+  UINT32 CmdArg;
+  EFI_STATUS Status;
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  if (PcdGet32(PcdMmcForce1Bit)) {
+    DEBUG((DEBUG_WARN, "Forcing 1 bit mode\n"));
+    return EFI_SUCCESS;
+  }
+
+  if (!MMC_HOST_HAS_SETIOS(MmcHost)) {
+    DEBUG((DEBUG_WARN, "Controller doesn't support bus width change\n"));
+    return EFI_SUCCESS;
+  }
+
+  CmdArg = MmcHostInstance->CardInfo.RCA << 16;
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD55, CmdArg);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(%u): error: %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  }
+
+  /* Width: 4 */
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD6, 2);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(%u): error %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  }
+
+  Status = MmcHost->SetIos (MmcHost, 0, BUSWIDTH_4, EMMCBACKWARD);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(%u): error: %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+SdSetSpeed(
+  IN  MMC_HOST_INSTANCE *MmcHostInstance,
+  IN  BOOLEAN CccSwitch
+  )
+{
+  UINT32 Speed;
+  UINT32 CmdArg;
+  EFI_STATUS Status;
+  UINT32 Buffer[16];
+  UINT32 Response[4];
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  if (!MMC_HOST_HAS_SETIOS(MmcHost)) {
+    DEBUG((DEBUG_WARN, "Controller doesn't support speed change\n"));
+    return EFI_SUCCESS;
+  }
+
+  Speed = PcdGet32(PcdMmcSdDefaultSpeedMHz) * 1000000;
+  if (Speed == 0) {
+    Speed = SD_DEFAULT_SPEED;
+  } else {
+    DEBUG((DEBUG_INFO, "Using default speed override %u Hz\n",
+           Speed));
+  }
+
+  /*
+   * First set base speed. We'll then try HS.
+   */
+  Status = MmcHost->SetIos (MmcHost, Speed, 0, EMMCBACKWARD);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a: error setting speed %u: %r\n",
+           __FUNCTION__, Speed, Status));
+    return Status;
+  }
+
+  if (PcdGet32(PcdMmcForceDefaultSpeed)) {
+    DEBUG((DEBUG_WARN, "Forcing default speed mode\n"));
+    return EFI_SUCCESS;
+  }
+
+  if (!CccSwitch) {
+    return EFI_SUCCESS;
+  }
+
+  /* Query. */
+  CmdArg = SdSwitchCmdArgument(0xf, 0xf, 0xf, 0xf, FALSE);
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD6, CmdArg);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a(%u): error: %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  } else {
+    Status = MmcHost->ReadBlockData (MmcHost, 0, SWITCH_CMD_DATA_LENGTH,
+                                     Buffer);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "%a(%u): error: %r\n",
+              __FUNCTION__, __LINE__, Status));
+      return Status;
+    }
+  }
+
+  if (!(Buffer[3] & SD_HIGH_SPEED_SUPPORTED)) {
+    DEBUG((DEBUG_ERROR, "%a: High Speed not supported by Card\n"));
+    return EFI_SUCCESS;
+  }
+
+  /* Switch to high speed. */
+  CmdArg = SdSwitchCmdArgument(1, 0xf, 0xf, 0xf, TRUE);
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD6, CmdArg);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a(%u): error: %r\n",
+            __FUNCTION__, __LINE__, Status));
+    return Status;
+  } else {
+    Status = MmcHost->ReadBlockData (MmcHost, 0,
+                                     SWITCH_CMD_DATA_LENGTH, Buffer);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "%a(%u): error: %r\n",
+              __FUNCTION__, __LINE__, Status));
+      return Status;
+    }
+
+    if ((Buffer[4] & SWITCH_CMD_SUCCESS_MASK) != 0x1) {
+      DEBUG((DEBUG_ERROR, "Problem switching SD card into HS mode\n"));
+      DEBUG((DEBUG_ERROR, "%08x %08x %08x %08x\n",
+             Buffer[0], Buffer[1], Buffer[2], Buffer[3]));
+      DEBUG((DEBUG_ERROR, "%08x %08x %08x %08x\n",
+             Buffer[4], Buffer[5], Buffer[6], Buffer[8]));
+      return Status;
+    }
+  }
+
+  DEBUG((DEBUG_ERROR, "Dumping CSD after high-speed switch\n"));
+  SdDeselect(MmcHostInstance);
+  SdGetCsd(MmcHostInstance, Response, TRUE);
+  SdSelect(MmcHostInstance);
+
+  Speed = PcdGet32(PcdMmcSdHighSpeedMHz) * 1000000;
+  if (Speed == 0) {
+    Speed = SD_HIGH_SPEED;
+  } else {
+    DEBUG((DEBUG_INFO, "Using high speed override %u Hz\n",
+           Speed));
+  }
+
+  Status = MmcHost->SetIos (MmcHost, Speed, 0, EMMCBACKWARD);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "%a: error setting speed %u: %r\n",
+           __FUNCTION__, Speed, Status));
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+InitializeSdMmcDevice (
+  IN  MMC_HOST_INSTANCE *MmcHostInstance
+  )
+{
+  UINT32        Response[4];
+  UINT32        Buffer[128];
+  UINTN         BlockSize;
+  UINTN         CardSize;
+  UINTN         NumBlocks;
+  BOOLEAN       CccSwitch;
+  SCR           Scr;
+  EFI_STATUS    Status;
+  EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost;
+
+  Status = SdGetCsd (MmcHostInstance, Response, TRUE);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (MMC_CSD_GET_CCC(Response) & SD_CCC_SWITCH) {
+    CccSwitch = TRUE;
+  } else {
+    CccSwitch = FALSE;
+  }
+
+  if (MmcHostInstance->CardInfo.CardType == SD_CARD_2_HIGH) {
+    CardSize = HC_MMC_CSD_GET_DEVICESIZE (Response);
+    NumBlocks = ((CardSize + 1) * 1024);
+    BlockSize = 1 << MMC_CSD_GET_READBLLEN (Response);
+  } else {
+    CardSize = MMC_CSD_GET_DEVICESIZE (Response);
+    NumBlocks = (CardSize + 1) * (1 << (MMC_CSD_GET_DEVICESIZEMULT (Response) + 2));
+    BlockSize = 1 << MMC_CSD_GET_READBLLEN (Response);
+  }
+
+  // For >=2G card, BlockSize may be 1K, but the transfer size is 512 bytes.
+  if (BlockSize > 512) {
+    NumBlocks = MultU64x32 (NumBlocks, BlockSize / 512);
+    BlockSize = 512;
+  }
+
+  MmcHostInstance->BlockIo.Media->LastBlock    = (NumBlocks - 1);
+  MmcHostInstance->BlockIo.Media->BlockSize    = BlockSize;
+  MmcHostInstance->BlockIo.Media->ReadOnly     = MmcHost->IsReadOnly (MmcHost);
+  MmcHostInstance->BlockIo.Media->MediaPresent = TRUE;
+  MmcHostInstance->BlockIo.Media->MediaId++;
+
+  Status = SdSelect(MmcHostInstance);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD55,
+                                 MmcHostInstance->CardInfo.RCA << 16);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a (MMC_CMD55): Error and Status = %r\n", __FUNCTION__, Status));
+    return Status;
+  }
+  Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a (MMC_CMD55): Error and Status = %r\n", __FUNCTION__, Status));
+    return Status;
+  }
+  if ((Response[0] & MMC_STATUS_APP_CMD) == 0) {
+    return EFI_SUCCESS;
+  }
+
+  /* SCR */
+  Status = MmcHost->SendCommand (MmcHost, MMC_ACMD51, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(MMC_ACMD51): Error and Status = %r\n", __func__, Status));
+    return Status;
+  } else {
+    Status = MmcHost->ReadBlockData (MmcHost, 0, 8, Buffer);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a(MMC_ACMD51): ReadBlockData Error and Status = %r\n", __func__, Status));
+      return Status;
+    }
+    CopyMem (&Scr, Buffer, 8);
+    if (Scr.SD_SPEC == 2) {
+      if (Scr.SD_SPEC3 == 1) {
+        if (Scr.SD_SPEC4 == 1) {
+          DEBUG ((DEBUG_INFO, "Found SD Card for Spec Version 4.xx\n"));
+        } else {
+          DEBUG ((DEBUG_INFO, "Found SD Card for Spec Version 3.0x\n"));
+        }
+      } else {
+        if (Scr.SD_SPEC4 == 0) {
+          DEBUG ((DEBUG_INFO, "Found SD Card for Spec Version 2.0\n"));
+       } else {
+         DEBUG ((DEBUG_ERROR, "Found invalid SD Card\n"));
+        }
+      }
+    } else {
+      if ((Scr.SD_SPEC3 == 0) && (Scr.SD_SPEC4 == 0)) {
+        if (Scr.SD_SPEC == 1) {
+          DEBUG ((DEBUG_INFO, "Found SD Card for Spec Version 1.10\n"));
+        } else {
+          DEBUG ((DEBUG_INFO, "Found SD Card for Spec Version 1.0\n"));
+        }
+      } else {
+        DEBUG ((DEBUG_ERROR, "Found invalid SD Card\n"));
+      }
+    }
+  }
+
+  Status = SdSetSpeed(MmcHostInstance, CccSwitch);
+  if (EFI_ERROR(Status)) {
+    return Status;
+  }
+
+  if (Scr.SD_BUS_WIDTHS & SD_BUS_WIDTH_4BIT) {
+    Status = SdSet4Bit(MmcHostInstance);
+    if (EFI_ERROR(Status)) {
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+MmcIdentificationMode (
+  IN MMC_HOST_INSTANCE     *MmcHostInstance
+  )
+{
+  EFI_STATUS              Status;
+  UINT32                  Response[4];
+  UINTN                   Timeout;
+  UINTN                   CmdArg;
+  BOOLEAN                 IsHCS;
+  EFI_MMC_HOST_PROTOCOL   *MmcHost;
+  OCR_RESPONSE            OcrResponse;
+
+  MmcHost = MmcHostInstance->MmcHost;
+  CmdArg = 0;
+  IsHCS = FALSE;
+
+  if (MmcHost == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // We can get into this function if we restart the identification mode
+  if (MmcHostInstance->State == MmcHwInitializationState) {
+    // Initialize the MMC Host HW
+    Status = MmcNotifyState (MmcHostInstance, MmcHwInitializationState);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Error MmcHwInitializationState, Status=%r.\n", Status));
+      return Status;
+    }
+  }
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD0, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(MMC_CMD0): Error, Status=%r.\n", Status));
+    return Status;
+  }
+  Status = MmcNotifyState (MmcHostInstance, MmcIdleState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Error MmcIdleState, Status=%r.\n", Status));
+    return Status;
+  }
+
+  // Send CMD1 to get OCR (MMC)
+  // This command only valid for MMC and eMMC
+  Timeout = MAX_RETRY_COUNT;
+  do {
+    Status = MmcHost->SendCommand (MmcHost, MMC_CMD1, EMMC_CMD1_CAPACITY_GREATER_THAN_2GB);
+    if (EFI_ERROR (Status))
+      break;
+    Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, (UINT32 *)&OcrResponse);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status));
+      return Status;
+    }
+    Timeout--;
+  } while (!OcrResponse.Ocr.PowerUp && (Timeout > 0));
+  if (Status == EFI_SUCCESS) {
+    if (!OcrResponse.Ocr.PowerUp) {
+      DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(MMC_CMD1): Card initialisation failure, Status=%r.\n", Status));
+      return EFI_DEVICE_ERROR;
+    }
+    OcrResponse.Ocr.PowerUp = 0;
+    if (OcrResponse.Raw == EMMC_CMD1_CAPACITY_GREATER_THAN_2GB) {
+      MmcHostInstance->CardInfo.OCRData.AccessMode = BIT1;
+    }
+    else {
+      MmcHostInstance->CardInfo.OCRData.AccessMode = 0x0;
+    }
+    // Check whether MMC or eMMC
+    if (OcrResponse.Raw == EMMC_CMD1_CAPACITY_GREATER_THAN_2GB ||
+        OcrResponse.Raw == EMMC_CMD1_CAPACITY_LESS_THAN_2GB) {
+      return EmmcIdentificationMode (MmcHostInstance, OcrResponse);
+    }
+  }
+
+  // Are we using SDIO ?
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD5, 0);
+  if (Status == EFI_SUCCESS) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(MMC_CMD5): Error - SDIO not supported, Status=%r.\n", Status));
+    return EFI_UNSUPPORTED;
+  }
+
+  // Check which kind of card we are using. Ver2.00 or later SD Memory Card (PL180 is SD v1.1)
+  CmdArg = (0x0UL << 12 | BIT8 | 0xCEUL << 0);
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD8, CmdArg);
+  if (Status == EFI_SUCCESS) {
+    DEBUG ((DEBUG_ERROR, "Card is SD2.0 => Supports high capacity\n"));
+    IsHCS = TRUE;
+    Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R7, Response);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive response to CMD8, Status=%r.\n", Status));
+      return Status;
+    }
+    PrintResponseR1 (Response[0]);
+    // Check if it is valid response
+    if (Response[0] != CmdArg) {
+      DEBUG ((DEBUG_ERROR, "The Card is not usable\n"));
+      return EFI_UNSUPPORTED;
+    }
+  } else {
+    DEBUG ((DEBUG_ERROR, "Not a SD2.0 Card\n"));
+  }
+
+  // We need to wait for the MMC or SD card is ready => (gCardInfo.OCRData.PowerUp == 1)
+  Timeout = MAX_RETRY_COUNT;
+  while (Timeout > 0) {
+    // SD Card or MMC Card ? CMD55 indicates to the card that the next command is an application specific command
+    Status = MmcHost->SendCommand (MmcHost, MMC_CMD55, 0);
+    if (Status == EFI_SUCCESS) {
+      DEBUG ((DEBUG_INFO, "Card should be SD\n"));
+      if (IsHCS) {
+        MmcHostInstance->CardInfo.CardType = SD_CARD_2;
+      } else {
+        MmcHostInstance->CardInfo.CardType = SD_CARD;
+      }
+
+      // Note: The first time CmdArg will be zero
+      CmdArg = ((UINTN *) &(MmcHostInstance->CardInfo.OCRData))[0];
+      if (IsHCS) {
+        CmdArg |= BIT30;
+      }
+      Status = MmcHost->SendCommand (MmcHost, MMC_ACMD41, CmdArg);
+      if (!EFI_ERROR (Status)) {
+        Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, Response);
+        if (EFI_ERROR (Status)) {
+          DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status));
+          return Status;
+        }
+        ((UINT32 *) &(MmcHostInstance->CardInfo.OCRData))[0] = Response[0];
+      }
+    } else {
+      DEBUG ((DEBUG_INFO, "Card should be MMC\n"));
+      MmcHostInstance->CardInfo.CardType = MMC_CARD;
+
+      Status = MmcHost->SendCommand (MmcHost, MMC_CMD1, 0x800000);
+      if (!EFI_ERROR (Status)) {
+        Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, Response);
+        if (EFI_ERROR (Status)) {
+          DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status));
+          return Status;
+        }
+        ((UINT32 *) &(MmcHostInstance->CardInfo.OCRData))[0] = Response[0];
+      }
+    }
+
+    if (!EFI_ERROR (Status)) {
+      if (!MmcHostInstance->CardInfo.OCRData.PowerUp) {
+        gBS->Stall (1);
+        Timeout--;
+      } else {
+        if ((MmcHostInstance->CardInfo.CardType == SD_CARD_2) && (MmcHostInstance->CardInfo.OCRData.AccessMode & BIT1)) {
+          MmcHostInstance->CardInfo.CardType = SD_CARD_2_HIGH;
+          DEBUG ((DEBUG_ERROR, "High capacity card.\n"));
+        }
+        break;  // The MMC/SD card is ready. Continue the Identification Mode
+      }
+    } else {
+      gBS->Stall (1);
+      Timeout--;
+    }
+  }
+
+  if (Timeout == 0) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(): No Card\n"));
+    return EFI_NO_MEDIA;
+  } else {
+    PrintOCR (Response[0]);
+  }
+
+  Status = MmcNotifyState (MmcHostInstance, MmcReadyState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Error MmcReadyState\n"));
+    return Status;
+  }
+
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD2, 0);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(MMC_CMD2): Error\n"));
+    return Status;
+  }
+  Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_CID, Response);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive CID, Status=%r.\n", Status));
+    return Status;
+  }
+
+  PrintCID (Response);
+
+  Status = MmcHost->NotifyState (MmcHost, MmcIdentificationState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Error MmcIdentificationState\n"));
+    return Status;
+  }
+
+  //
+  // Note, SD specifications say that "if the command execution causes a state change, it
+  // will be visible to the host in the response to the next command"
+  // The status returned for this CMD3 will be 2 - identification
+  //
+  CmdArg = 1;
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD3, CmdArg);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(MMC_CMD3): Error\n"));
+    return Status;
+  }
+
+  Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_RCA, Response);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive RCA, Status=%r.\n", Status));
+    return Status;
+  }
+  PrintRCA (Response[0]);
+
+  // For MMC card, RCA is assigned by CMD3 while CMD3 dumps the RCA for SD card
+  if (MmcHostInstance->CardInfo.CardType != MMC_CARD) {
+    MmcHostInstance->CardInfo.RCA = Response[0] >> 16;
+  } else {
+    MmcHostInstance->CardInfo.RCA = CmdArg;
+  }
+  Status = MmcNotifyState (MmcHostInstance, MmcStandByState);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Error MmcStandByState\n"));
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+InitializeMmcDevice (
+  IN  MMC_HOST_INSTANCE   *MmcHostInstance
+  )
+{
+  EFI_STATUS              Status;
+  EFI_MMC_HOST_PROTOCOL   *MmcHost;
+  UINTN                   BlockCount;
+
+  BlockCount = 1;
+  MmcHost = MmcHostInstance->MmcHost;
+
+  Status = MmcIdentificationMode (MmcHostInstance);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "InitializeMmcDevice(): Error in Identification Mode, Status=%r\n", Status));
+    return Status;
+  }
+
+  Status = MmcNotifyState (MmcHostInstance, MmcTransferState);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "InitializeMmcDevice(): Error MmcTransferState, Status=%r\n", Status));
+    return Status;
+  }
+
+  if (MmcHostInstance->CardInfo.CardType != EMMC_CARD) {
+    Status = InitializeSdMmcDevice (MmcHostInstance);
+  } else {
+    Status = InitializeEmmcDevice (MmcHostInstance);
+  }
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Set Block Length
+  Status = MmcHost->SendCommand (MmcHost, MMC_CMD16, MmcHostInstance->BlockIo.Media->BlockSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "InitializeMmcDevice(MMC_CMD16): Error MmcHostInstance->BlockIo.Media->BlockSize: %d and Error = %r\n",
+                        MmcHostInstance->BlockIo.Media->BlockSize, Status));
+    return Status;
+  }
+
+  // Block Count (not used). Could return an error for SD card
+  if (MmcHostInstance->CardInfo.CardType == MMC_CARD) {
+    Status = MmcHost->SendCommand (MmcHost, MMC_CMD23, BlockCount);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "InitializeMmcDevice(MMC_CMD23): Error, Status=%r\n", Status));
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PlatformSmbiosDxe/PlatformSmbiosDxe.c b/Platform/Broadcom/Bcm283x/Drivers/PlatformSmbiosDxe/PlatformSmbiosDxe.c
new file mode 100644
index 000000000000..36b953d9c5f4
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PlatformSmbiosDxe/PlatformSmbiosDxe.c
@@ -0,0 +1,915 @@
+/** @file
+ *
+ *  Static SMBIOS Table for ARM platform
+ *  Derived from EmulatorPkg package
+ *
+ *  Note SMBIOS 2.7.1 Required structures:
+ *  BIOS Information (Type 0)
+ *  System Information (Type 1)
+ *  Board Information (Type 2)
+ *  System Enclosure (Type 3)
+ *  Processor Information (Type 4) - CPU Driver
+ *  Cache Information (Type 7) - For cache that is external to processor
+ *  System Slots (Type 9) - If system has slots
+ *  Physical Memory Array (Type 16)
+ *  Memory Device (Type 17) - For each socketed system-memory Device
+ *  Memory Array Mapped Address (Type 19) - One per contiguous block per Physical Memroy Array
+ *  System Boot Information (Type 32)
+ *
+ *  Copyright (c) 2017-2018, Andrey Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2013, Linaro.org
+ *  Copyright (c) 2012, Apple Inc. All rights reserved.<BR>
+ *  Copyright (c) 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 <Base.h>
+#include <Protocol/Smbios.h>
+#include <Protocol/RaspberryPiFirmware.h>
+#include <IndustryStandard/SmBios.h>
+#include <IndustryStandard/RpiFirmware.h>
+#include <Guid/SmBios.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/PrintLib.h>
+#include <Utils.h>
+
+STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol;
+
+/***********************************************************************
+        SMBIOS data definition  TYPE0  BIOS Information
+************************************************************************/
+SMBIOS_TABLE_TYPE0 mBIOSInfoType0 = {
+  { EFI_SMBIOS_TYPE_BIOS_INFORMATION, sizeof (SMBIOS_TABLE_TYPE0), 0 },
+  1,                    // Vendor String
+  2,                    // BiosVersion String
+  0x0,                  // BiosSegment
+  3,                    // BiosReleaseDate String
+  0x1F,                 // BiosSize
+  {                     // BiosCharacteristics
+    0,    //  Reserved                          :2;  ///< Bits 0-1.
+    0,    //  Unknown                           :1;
+    0,    //  BiosCharacteristicsNotSupported   :1;
+    0,    //  IsaIsSupported                    :1;
+    0,    //  McaIsSupported                    :1;
+    0,    //  EisaIsSupported                   :1;
+    0,    //  PciIsSupported                    :1;
+    0,    //  PcmciaIsSupported                 :1;
+    0,    //  PlugAndPlayIsSupported            :1;
+    0,    //  ApmIsSupported                    :1;
+    0,    //  BiosIsUpgradable                  :1;
+    0,    //  BiosShadowingAllowed              :1;
+    0,    //  VlVesaIsSupported                 :1;
+    0,    //  EscdSupportIsAvailable            :1;
+    0,    //  BootFromCdIsSupported             :1;
+    1,    //  SelectableBootIsSupported         :1;
+    0,    //  RomBiosIsSocketed                 :1;
+    0,    //  BootFromPcmciaIsSupported         :1;
+    0,    //  EDDSpecificationIsSupported       :1;
+    0,    //  JapaneseNecFloppyIsSupported      :1;
+    0,    //  JapaneseToshibaFloppyIsSupported  :1;
+    0,    //  Floppy525_360IsSupported          :1;
+    0,    //  Floppy525_12IsSupported           :1;
+    0,    //  Floppy35_720IsSupported           :1;
+    0,    //  Floppy35_288IsSupported           :1;
+    0,    //  PrintScreenIsSupported            :1;
+    0,    //  Keyboard8042IsSupported           :1;
+    0,    //  SerialIsSupported                 :1;
+    0,    //  PrinterIsSupported                :1;
+    0,    //  CgaMonoIsSupported                :1;
+    0,    //  NecPc98                           :1;
+    0     //  ReservedForVendor                 :32; ///< Bits 32-63. Bits 32-47 reserved for BIOS vendor
+    ///< and bits 48-63 reserved for System Vendor.
+  },
+  {       // BIOSCharacteristicsExtensionBytes[]
+    0x01, //  AcpiIsSupported                   :1;
+          //  UsbLegacyIsSupported              :1;
+          //  AgpIsSupported                    :1;
+          //  I2OBootIsSupported                :1;
+          //  Ls120BootIsSupported              :1;
+          //  AtapiZipDriveBootIsSupported      :1;
+          //  Boot1394IsSupported               :1;
+          //  SmartBatteryIsSupported           :1;
+          //  BIOSCharacteristicsExtensionBytes[1]
+    0x0e, //  BiosBootSpecIsSupported              :1;
+          //  FunctionKeyNetworkBootIsSupported    :1;
+          //  TargetContentDistributionEnabled     :1;
+          //  UefiSpecificationSupported           :1;
+          //  VirtualMachineSupported              :1;
+          //  ExtensionByte2Reserved               :3;
+  },
+  0xFF,                    // SystemBiosMajorRelease
+  0xFF,                    // SystemBiosMinorRelease
+  0xFF,                    // EmbeddedControllerFirmwareMajorRelease
+  0xFF,                    // EmbeddedControllerFirmwareMinorRelease
+};
+
+CHAR8 *mBIOSInfoType0Strings[] = {
+  "https://github.com/andreiw/RaspberryPiPkg",             // Vendor String
+  "Raspberry Pi 64-bit UEFI (" __DATE__ " " __TIME__ ")",  // BiosVersion String
+  __DATE__,
+  NULL
+};
+
+/***********************************************************************
+        SMBIOS data definition  TYPE1  System Information
+************************************************************************/
+SMBIOS_TABLE_TYPE1 mSysInfoType1 = {
+  { EFI_SMBIOS_TYPE_SYSTEM_INFORMATION, sizeof (SMBIOS_TABLE_TYPE1), 0 },
+  1,    // Manufacturer String
+  2,    // ProductName String
+  3,    // Version String
+  4,    // SerialNumber String
+  { 0x25EF0280, 0xEC82, 0x42B0, { 0x8F, 0xB6, 0x10, 0xAD, 0xCC, 0xC6, 0x7C, 0x02 } },
+  SystemWakeupTypePowerSwitch,
+  5,    // SKUNumber String
+  6,    // Family String
+};
+
+#define PROD_BASE     8
+#define PROD_KNOWN   13
+#define PROD_UNKNOWN 11
+CHAR8 *ProductNames[] = {
+  /* 8 */ "3",
+  /* 9 */ "Zero",
+  /* 10 */ "CM3",
+  /* 11 */ "???",
+  /* 12 */ "Zero W",
+  /* 13 */ "3B+"
+};
+
+#define MANU_UNKNOWN 0
+#define MANU_KNOWN   4
+#define MANU_BASE    1
+CHAR8 *ManufNames[] = {
+  "???",
+  /* 0 */ "Sony",
+  /* 1 */ "Egoman",
+  /* 2 */ "Embest",
+  /* 3 */ "Sony Japan",
+  /* 4 */ "Embest"
+};
+
+CHAR8 mSysInfoManufName[sizeof("Sony Japan")];
+CHAR8 mSysInfoProductName[sizeof("64-bit Raspberry Pi XXXXXX (rev. xxxxxxxx)")];
+CHAR8 mSysInfoSerial[sizeof(UINT64) * 2 + 1];
+CHAR8 mSysInfoSKU[sizeof(UINT64) * 2 + 1];
+
+CHAR8  *mSysInfoType1Strings[] = {
+  mSysInfoManufName,
+  mSysInfoProductName,
+  mSysInfoProductName,
+  mSysInfoSerial,
+  mSysInfoSKU,
+  "edk2",
+  NULL
+};
+
+/***********************************************************************
+        SMBIOS data definition  TYPE2  Board Information
+************************************************************************/
+SMBIOS_TABLE_TYPE2  mBoardInfoType2 = {
+  { EFI_SMBIOS_TYPE_BASEBOARD_INFORMATION, sizeof (SMBIOS_TABLE_TYPE2), 0 },
+  1,    // Manufacturer String
+  2,    // ProductName String
+  3,    // Version String
+  4,    // SerialNumber String
+  5,    // AssetTag String
+  {     // FeatureFlag
+    1,    //  Motherboard           :1;
+    0,    //  RequiresDaughterCard  :1;
+    0,    //  Removable             :1;
+    0,    //  Replaceable           :1;
+    0,    //  HotSwappable          :1;
+    0,    //  Reserved              :3;
+  },
+  6,    // LocationInChassis String
+  0,                        // ChassisHandle;
+  BaseBoardTypeMotherBoard, // BoardType;
+  0,                        // NumberOfContainedObjectHandles;
+  { 0 }                     // ContainedObjectHandles[1];
+};
+CHAR8  *mBoardInfoType2Strings[] = {
+  mSysInfoManufName,
+  mSysInfoProductName,
+  mSysInfoProductName,
+  mSysInfoSerial,
+  "None",
+  mSysInfoSKU,
+  NULL
+};
+
+/***********************************************************************
+        SMBIOS data definition  TYPE3  Enclosure Information
+************************************************************************/
+SMBIOS_TABLE_TYPE3  mEnclosureInfoType3 = {
+  { EFI_SMBIOS_TYPE_SYSTEM_ENCLOSURE, sizeof (SMBIOS_TABLE_TYPE3), 0 },
+  1,                        // Manufacturer String
+  MiscChassisEmbeddedPc,    // Type;
+  2,                        // Version String
+  3,                        // SerialNumber String
+  4,                        // AssetTag String
+  ChassisStateSafe,         // BootupState;
+  ChassisStateSafe,         // PowerSupplyState;
+  ChassisStateSafe,         // ThermalState;
+  ChassisSecurityStatusNone,// SecurityStatus;
+  { 0, 0, 0, 0 },           // OemDefined[4];
+  0,    // Height;
+  0,    // NumberofPowerCords;
+  0,    // ContainedElementCount;
+  0,    // ContainedElementRecordLength;
+  { { 0 } },    // ContainedElements[1];
+};
+CHAR8  *mEnclosureInfoType3Strings[] = {
+  mSysInfoManufName,
+  mSysInfoProductName,
+  mSysInfoSerial,
+  "None",
+  NULL
+};
+
+/***********************************************************************
+        SMBIOS data definition  TYPE4  Processor Information
+************************************************************************/
+SMBIOS_TABLE_TYPE4 mProcessorInfoType4 = {
+  { EFI_SMBIOS_TYPE_PROCESSOR_INFORMATION, sizeof (SMBIOS_TABLE_TYPE4), 0},
+  1,                    // Socket String
+  CentralProcessor,       // ProcessorType;                                   ///< The enumeration value from PROCESSOR_TYPE_DATA.
+  ProcessorFamilyIndicatorFamily2, // ProcessorFamily;        ///< The enumeration value from PROCESSOR_FAMILY2_DATA.
+  2,                    // ProcessorManufacture String;
+  {                     // ProcessorId;
+    {  // PROCESSOR_SIGNATURE
+      0, //  ProcessorSteppingId:4;
+      0, //  ProcessorModel:     4;
+      0, //  ProcessorFamily:    4;
+      0, //  ProcessorType:      2;
+      0, //  ProcessorReserved1: 2;
+      0, //  ProcessorXModel:    4;
+      0, //  ProcessorXFamily:   8;
+      0, //  ProcessorReserved2: 4;
+    },
+
+    {  // PROCESSOR_FEATURE_FLAGS
+      0, //  ProcessorFpu       :1;
+      0, //  ProcessorVme       :1;
+      0, //  ProcessorDe        :1;
+      0, //  ProcessorPse       :1;
+      0, //  ProcessorTsc       :1;
+      0, //  ProcessorMsr       :1;
+      0, //  ProcessorPae       :1;
+      0, //  ProcessorMce       :1;
+      0, //  ProcessorCx8       :1;
+      0, //  ProcessorApic      :1;
+      0, //  ProcessorReserved1 :1;
+      0, //  ProcessorSep       :1;
+      0, //  ProcessorMtrr      :1;
+      0, //  ProcessorPge       :1;
+      0, //  ProcessorMca       :1;
+      0, //  ProcessorCmov      :1;
+      0, //  ProcessorPat       :1;
+      0, //  ProcessorPse36     :1;
+      0, //  ProcessorPsn       :1;
+      0, //  ProcessorClfsh     :1;
+      0, //  ProcessorReserved2 :1;
+      0, //  ProcessorDs        :1;
+      0, //  ProcessorAcpi      :1;
+      0, //  ProcessorMmx       :1;
+      0, //  ProcessorFxsr      :1;
+      0, //  ProcessorSse       :1;
+      0, //  ProcessorSse2      :1;
+      0, //  ProcessorSs        :1;
+      0, //  ProcessorReserved3 :1;
+      0, //  ProcessorTm        :1;
+      0, //  ProcessorReserved4 :2;
+    }
+  },
+  3,                    // ProcessorVersion String;
+  {                     // Voltage;
+    1,  // ProcessorVoltageCapability5V        :1;
+    1,  // ProcessorVoltageCapability3_3V      :1;
+    1,  // ProcessorVoltageCapability2_9V      :1;
+    0,  // ProcessorVoltageCapabilityReserved  :1; ///< Bit 3, must be zero.
+    0,  // ProcessorVoltageReserved            :3; ///< Bits 4-6, must be zero.
+    0   // ProcessorVoltageIndicateLegacy      :1;
+  },
+  0,                      // ExternalClock;
+  0,                      // MaxSpeed;
+  0,                      // CurrentSpeed;
+  0x41,                   // Status;
+  ProcessorUpgradeOther,  // ProcessorUpgrade;      ///< The enumeration value from PROCESSOR_UPGRADE.
+  0,                      // L1CacheHandle;
+  0,                      // L2CacheHandle;
+  0,                      // L3CacheHandle;
+  4,                      // SerialNumber;
+  5,                      // AssetTag;
+  6,                      // PartNumber;
+  4,                      // CoreCount;
+  4,                      // EnabledCoreCount;
+  4,                      // ThreadCount;
+  0x6C,                   // ProcessorCharacteristics;
+  ProcessorFamilyARM,     // ARM Processor Family;
+};
+
+CHAR8 *mProcessorInfoType4Strings[] = {
+  "Socket",
+  "ARM",
+  "BCM2837 ARMv8",
+  "1.0",
+  "1.0",
+  "1.0",
+  NULL
+};
+
+/***********************************************************************
+        SMBIOS data definition  TYPE7  Cache Information
+************************************************************************/
+SMBIOS_TABLE_TYPE7  mCacheInfoType7 = {
+  { EFI_SMBIOS_TYPE_CACHE_INFORMATION, sizeof (SMBIOS_TABLE_TYPE7), 0 },
+  1,                        // SocketDesignation String
+  0x018A,                                       // Cache Configuration
+  0x00FF,                                       // Maximum Size 256k
+  0x00FF,                                       // Install Size 256k
+  {                         // Supported SRAM Type
+    0,  //Other             :1
+    0,  //Unknown           :1
+    0,  //NonBurst          :1
+    1,  //Burst             :1
+    0,  //PiplelineBurst    :1
+    1,  //Synchronous       :1
+    0,  //Asynchronous      :1
+    0       //Reserved          :9
+  },
+  {                         // Current SRAM Type
+    0,  //Other             :1
+    0,  //Unknown           :1
+    0,  //NonBurst          :1
+    1,  //Burst             :1
+    0,  //PiplelineBurst    :1
+    1,  //Synchronous       :1
+    0,  //Asynchronous      :1
+    0       //Reserved          :9
+  },
+  0,                                            // Cache Speed unknown
+  CacheErrorMultiBit,           // Error Correction Multi
+  CacheTypeUnknown,                     // System Cache Type
+  CacheAssociativity2Way        // Associativity
+};
+CHAR8  *mCacheInfoType7Strings[] = {
+  "Cache1",
+  NULL
+};
+
+/***********************************************************************
+        SMBIOS data definition  TYPE9  System Slot Information
+************************************************************************/
+SMBIOS_TABLE_TYPE9  mSysSlotInfoType9 = {
+  { EFI_SMBIOS_TYPE_SYSTEM_SLOTS, sizeof (SMBIOS_TABLE_TYPE9), 0 },
+  1,    // SlotDesignation String
+  SlotTypeOther,          // SlotType;                 ///< The enumeration value from MISC_SLOT_TYPE.
+  SlotDataBusWidthOther,  // SlotDataBusWidth;         ///< The enumeration value from MISC_SLOT_DATA_BUS_WIDTH.
+  SlotUsageAvailable,    // CurrentUsage;             ///< The enumeration value from MISC_SLOT_USAGE.
+  SlotLengthOther,    // SlotLength;               ///< The enumeration value from MISC_SLOT_LENGTH.
+  0,    // SlotID;
+  {    // SlotCharacteristics1;
+    1,  // CharacteristicsUnknown  :1;
+    0,  // Provides50Volts         :1;
+    0,  // Provides33Volts         :1;
+    0,  // SharedSlot              :1;
+    0,  // PcCard16Supported       :1;
+    0,  // CardBusSupported        :1;
+    0,  // ZoomVideoSupported      :1;
+    0,  // ModemRingResumeSupported:1;
+  },
+  {     // SlotCharacteristics2;
+    0,  // PmeSignalSupported      :1;
+    0,  // HotPlugDevicesSupported :1;
+    0,  // SmbusSignalSupported    :1;
+    0,  // Reserved                :5;  ///< Set to 0.
+  },
+  0,    // SegmentGroupNum;
+  0,    // BusNum;
+  0,    // DevFuncNum;
+};
+CHAR8  *mSysSlotInfoType9Strings[] = {
+  "SD Card",
+  NULL
+};
+
+/***********************************************************************
+        SMBIOS data definition  TYPE16  Physical Memory ArrayInformation
+************************************************************************/
+SMBIOS_TABLE_TYPE16 mPhyMemArrayInfoType16 = {
+  { EFI_SMBIOS_TYPE_PHYSICAL_MEMORY_ARRAY, sizeof (SMBIOS_TABLE_TYPE16), 0 },
+  MemoryArrayLocationSystemBoard, // Location;                       ///< The enumeration value from MEMORY_ARRAY_LOCATION.
+  MemoryArrayUseSystemMemory,     // Use;                            ///< The enumeration value from MEMORY_ARRAY_USE.
+  MemoryErrorCorrectionUnknown,   // MemoryErrorCorrection;          ///< The enumeration value from MEMORY_ERROR_CORRECTION.
+  0x40000000,                     // MaximumCapacity;
+  0xFFFE,                         // MemoryErrorInformationHandle;
+  1,                              // NumberOfMemoryDevices;
+  0x40000000ULL,                  // ExtendedMaximumCapacity;
+};
+CHAR8 *mPhyMemArrayInfoType16Strings[] = {
+  NULL
+};
+
+/***********************************************************************
+        SMBIOS data definition  TYPE17  Memory Device Information
+************************************************************************/
+SMBIOS_TABLE_TYPE17 mMemDevInfoType17 = {
+  { EFI_SMBIOS_TYPE_MEMORY_DEVICE, sizeof (SMBIOS_TABLE_TYPE17), 0 },
+  0,          // MemoryArrayHandle; // Should match SMBIOS_TABLE_TYPE16.Handle, initialized at runtime, refer to PhyMemArrayInfoUpdateSmbiosType16()
+  0xFFFE,     // MemoryErrorInformationHandle;
+  0xFFFF,     // TotalWidth;
+  0xFFFF,     // DataWidth;
+  0x0400,     // Size; // When bit 15 is 0: Size in MB
+              // When bit 15 is 1: Size in KB, and continues in ExtendedSize
+  MemoryFormFactorUnknown, // FormFactor;                     ///< The enumeration value from MEMORY_FORM_FACTOR.
+  0xff,       // DeviceSet;
+  1,          // DeviceLocator String
+  2,          // BankLocator String
+  MemoryTypeDram,         // MemoryType;                     ///< The enumeration value from MEMORY_DEVICE_TYPE.
+  {           // TypeDetail;
+    0,  // Reserved        :1;
+    0,  // Other           :1;
+    1,  // Unknown         :1;
+    0,  // FastPaged       :1;
+    0,  // StaticColumn    :1;
+    0,  // PseudoStatic    :1;
+    0,  // Rambus          :1;
+    0,  // Synchronous     :1;
+    0,  // Cmos            :1;
+    0,  // Edo             :1;
+    0,  // WindowDram      :1;
+    0,  // CacheDram       :1;
+    0,  // Nonvolatile     :1;
+    0,  // Registered      :1;
+    0,  // Unbuffered      :1;
+    0,  // Reserved1       :1;
+  },
+  0,          // Speed;
+  3,          // Manufacturer String
+  0,          // SerialNumber String
+  0,          // AssetTag String
+  0,          // PartNumber String
+  0,          // Attributes;
+  0,          // ExtendedSize;
+  0,          // ConfiguredMemoryClockSpeed;
+};
+CHAR8 *mMemDevInfoType17Strings[] = {
+  "OS Virtual Memory",
+  "malloc",
+  "OSV",
+  NULL
+};
+
+/***********************************************************************
+        SMBIOS data definition  TYPE19  Memory Array Mapped Address Information
+************************************************************************/
+SMBIOS_TABLE_TYPE19 mMemArrMapInfoType19 = {
+  { EFI_SMBIOS_TYPE_MEMORY_ARRAY_MAPPED_ADDRESS, sizeof (SMBIOS_TABLE_TYPE19), 0 },
+  0x00000000, // StartingAddress;
+  0x00000000, // EndingAddress;
+  0,          // MemoryArrayHandle;
+  1,          // PartitionWidth;
+  0,          // ExtendedStartingAddress;
+  0,          // ExtendedEndingAddress;
+};
+CHAR8 *mMemArrMapInfoType19Strings[] = {
+  NULL
+};
+
+/***********************************************************************
+        SMBIOS data definition  TYPE32  Boot Information
+************************************************************************/
+SMBIOS_TABLE_TYPE32 mBootInfoType32 = {
+  { EFI_SMBIOS_TYPE_SYSTEM_BOOT_INFORMATION, sizeof (SMBIOS_TABLE_TYPE32), 0 },
+  { 0, 0, 0, 0, 0, 0 },         // Reserved[6];
+  BootInformationStatusNoError  // BootStatus
+};
+
+CHAR8 *mBootInfoType32Strings[] = {
+  NULL
+};
+
+
+/**
+
+   Create SMBIOS record.
+
+   Converts a fixed SMBIOS structure and an array of pointers to strings into
+   an SMBIOS record where the strings are cat'ed on the end of the fixed record
+   and terminated via a double NULL and add to SMBIOS table.
+
+   SMBIOS_TABLE_TYPE32 gSmbiosType12 = {
+   { EFI_SMBIOS_TYPE_SYSTEM_CONFIGURATION_OPTIONS, sizeof (SMBIOS_TABLE_TYPE12), 0 },
+   1 // StringCount
+   };
+
+   CHAR8 *gSmbiosType12Strings[] = {
+   "Not Found",
+   NULL
+   };
+
+   ...
+
+   LogSmbiosData (
+   (EFI_SMBIOS_TABLE_HEADER*)&gSmbiosType12,
+   gSmbiosType12Strings
+   );
+
+   @param  Template    Fixed SMBIOS structure, required.
+   @param  StringPack  Array of strings to convert to an SMBIOS string pack.
+   NULL is OK.
+   @param  DataSmbiosHande  The new SMBIOS record handle .
+   NULL is OK.
+**/
+
+EFI_STATUS
+EFIAPI
+LogSmbiosData (
+               IN  EFI_SMBIOS_TABLE_HEADER *Template,
+               IN  CHAR8                   **StringPack,
+               OUT EFI_SMBIOS_HANDLE       *DataSmbiosHande
+               )
+{
+  EFI_STATUS                Status;
+  EFI_SMBIOS_PROTOCOL       *Smbios;
+  EFI_SMBIOS_HANDLE         SmbiosHandle;
+  EFI_SMBIOS_TABLE_HEADER   *Record;
+  UINTN                     Index;
+  UINTN                     StringSize;
+  UINTN                     Size;
+  CHAR8                     *Str;
+
+  //
+  // Locate Smbios protocol.
+  //
+  Status = gBS->LocateProtocol (&gEfiSmbiosProtocolGuid, NULL, (VOID **)&Smbios);
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Calculate the size of the fixed record and optional string pack
+
+  Size = Template->Length;
+  if (StringPack == NULL) {
+    // At least a double null is required
+    Size += 2;
+  } else {
+    for (Index = 0; StringPack[Index] != NULL; Index++) {
+      StringSize = AsciiStrSize (StringPack[Index]);
+      Size += StringSize;
+    }
+    if (StringPack[0] == NULL) {
+      // At least a double null is required
+      Size += 1;
+    }
+
+    // Don't forget the terminating double null
+    Size += 1;
+  }
+
+  // Copy over Template
+  Record = (EFI_SMBIOS_TABLE_HEADER *)AllocateZeroPool (Size);
+  if (Record == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  CopyMem (Record, Template, Template->Length);
+
+  // Append string pack
+  Str = ((CHAR8 *)Record) + Record->Length;
+
+  for (Index = 0; StringPack[Index] != NULL; Index++) {
+    StringSize = AsciiStrSize (StringPack[Index]);
+    CopyMem (Str, StringPack[Index], StringSize);
+    Str += StringSize;
+  }
+
+  *Str = 0;
+  SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
+  Status = Smbios->Add (
+                        Smbios,
+                        gImageHandle,
+                        &SmbiosHandle,
+                        Record
+                        );
+
+  if ((Status == EFI_SUCCESS) && (DataSmbiosHande != NULL)) {
+    *DataSmbiosHande = SmbiosHandle;
+  }
+
+  ASSERT_EFI_ERROR (Status);
+  FreePool (Record);
+  return Status;
+}
+
+/***********************************************************************
+        SMBIOS data update  TYPE0  BIOS Information
+************************************************************************/
+VOID
+BIOSInfoUpdateSmbiosType0 (
+                           VOID
+                           )
+{
+  LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER *)&mBIOSInfoType0, mBIOSInfoType0Strings, NULL);
+}
+
+/***********************************************************************
+        SMBIOS data update  TYPE1  System Information
+************************************************************************/
+VOID
+I64ToHexString(
+               IN OUT CHAR8* TargetStringSz,
+               IN UINT32 TargetStringSize,
+               IN UINT64 Value
+               )
+{
+  static CHAR8 ItoH[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
+  UINT8 StringInx;
+  INT8 NibbleInx;
+
+  ZeroMem((void*)TargetStringSz, TargetStringSize);
+
+  //
+  // Convert each nibble to hex string, starting from
+  // the highest non-zero nibble.
+  //
+  StringInx = 0;
+  for (NibbleInx = sizeof(UINT64) * 2; NibbleInx > 0; --NibbleInx) {
+    UINT64 NibbleMask = (((UINT64)0xF) << ((NibbleInx - 1) * 4));
+    UINT8 Nibble = (UINT8)((Value & NibbleMask) >> ((NibbleInx - 1) * 4));
+
+    ASSERT(Nibble <= 0xF);
+
+    if (StringInx < (TargetStringSize-1)) {
+      TargetStringSz[StringInx++] = ItoH[Nibble];
+    } else {
+      break;
+    }
+  }
+}
+
+VOID
+SysInfoUpdateSmbiosType1 (
+                          VOID
+                          )
+{
+  UINT32 BoardRevision = 0;
+  EFI_STATUS Status = EFI_SUCCESS;
+  UINT64 BoardSerial = 0;
+  UINTN Prod = PROD_UNKNOWN;
+  UINTN Manu = MANU_UNKNOWN;
+
+  Status = mFwProtocol->GetModelRevision(&BoardRevision);
+  if (EFI_ERROR(Status)) {
+    DEBUG ((DEBUG_ERROR,
+            "Failed to get board model: %r\n",
+            Status));
+  } else {
+    Prod = X(BoardRevision, 4, 11);
+  }
+
+  if (Prod > PROD_KNOWN) {
+    Prod = PROD_UNKNOWN;
+  }
+  Prod -= PROD_BASE;
+  AsciiSPrint(mSysInfoProductName,
+              sizeof(mSysInfoProductName),
+              "64-bit Raspberry Pi %a (rev. %x)",
+              ProductNames[Prod], BoardRevision);
+
+  Manu = X(BoardRevision, 16, 19);
+  if (Manu > MANU_KNOWN) {
+    Manu = MANU_UNKNOWN;
+  } else {
+    Manu += MANU_BASE;
+  }
+  AsciiSPrint(mSysInfoManufName,
+              sizeof(mSysInfoManufName),
+              "%a", ManufNames[Manu]);
+
+  I64ToHexString(mSysInfoSKU,
+                 sizeof(mSysInfoSKU),
+                 BoardRevision);
+
+  Status = mFwProtocol->GetSerial(&BoardSerial);
+  if (EFI_ERROR(Status)) {
+    DEBUG ((DEBUG_ERROR,
+            "Failed to get board serial: %r\n",
+            Status));
+  }
+
+  I64ToHexString(mSysInfoSerial,
+                 sizeof(mSysInfoSerial),
+                 BoardSerial);
+
+  DEBUG ((DEBUG_ERROR, "Board Serial Number: %a\n", mSysInfoSerial));
+
+  mSysInfoType1.Uuid.Data1= *(UINT32 *)"RPi3";
+  mSysInfoType1.Uuid.Data2=0x0;
+  mSysInfoType1.Uuid.Data3=0x0;
+  CopyMem(mSysInfoType1.Uuid.Data4,
+          &BoardSerial, sizeof(BoardSerial));
+
+  LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER *)&mSysInfoType1, mSysInfoType1Strings, NULL);
+}
+
+/***********************************************************************
+        SMBIOS data update  TYPE2  Board Information
+************************************************************************/
+VOID
+BoardInfoUpdateSmbiosType2 (
+                            VOID
+                            )
+{
+  LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER *)&mBoardInfoType2, mBoardInfoType2Strings, NULL);
+}
+
+/***********************************************************************
+        SMBIOS data update  TYPE3  Enclosure Information
+************************************************************************/
+VOID
+EnclosureInfoUpdateSmbiosType3 (
+                                VOID
+                                )
+{
+  LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER *)&mEnclosureInfoType3, mEnclosureInfoType3Strings, NULL);
+}
+
+/***********************************************************************
+        SMBIOS data update  TYPE4  Processor Information
+************************************************************************/
+VOID
+ProcessorInfoUpdateSmbiosType4 (
+                                IN UINTN MaxCpus
+                                )
+{
+  EFI_STATUS Status;
+  UINT32 Rate;
+
+  mProcessorInfoType4.CoreCount        = (UINT8) MaxCpus;
+  mProcessorInfoType4.EnabledCoreCount = (UINT8) MaxCpus;
+  mProcessorInfoType4.ThreadCount      = (UINT8) MaxCpus;
+
+  Status = mFwProtocol->GetMaxClockRate(RPI_FW_CLOCK_RATE_ARM, &Rate);
+  if (Status != EFI_SUCCESS) {
+    DEBUG ((DEBUG_ERROR, "Couldn't get the max CPU speed: %r\n", Status));
+  } else {
+    mProcessorInfoType4.MaxSpeed = Rate / 1000000;
+    DEBUG ((DEBUG_INFO, "Max CPU speed: %uHz\n", Rate));
+  }
+
+  Status = mFwProtocol->GetClockRate(RPI_FW_CLOCK_RATE_ARM, &Rate);
+  if (Status != EFI_SUCCESS) {
+    DEBUG ((DEBUG_ERROR, "Couldn't get the current CPU speed: %r\n", Status));
+  } else {
+    mProcessorInfoType4.CurrentSpeed = Rate / 1000000;
+    DEBUG ((DEBUG_INFO, "Current CPU speed: %uHz\n", Rate));
+  }
+
+  LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER *)&mProcessorInfoType4, mProcessorInfoType4Strings, NULL);
+}
+
+/***********************************************************************
+        SMBIOS data update  TYPE7  Cache Information
+************************************************************************/
+VOID
+CacheInfoUpdateSmbiosType7 (
+                            VOID
+                            )
+{
+  LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER *)&mCacheInfoType7, mCacheInfoType7Strings, NULL);
+}
+
+/***********************************************************************
+        SMBIOS data update  TYPE9  System Slot Information
+************************************************************************/
+VOID
+SysSlotInfoUpdateSmbiosType9 (
+                              VOID
+                              )
+{
+  LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER *)&mSysSlotInfoType9, mSysSlotInfoType9Strings, NULL);
+}
+
+/***********************************************************************
+        SMBIOS data update  TYPE16  Physical Memory Array Information
+************************************************************************/
+VOID
+PhyMemArrayInfoUpdateSmbiosType16 (
+                                   VOID
+                                   )
+{
+  EFI_SMBIOS_HANDLE MemArraySmbiosHande;
+
+  LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER *)&mPhyMemArrayInfoType16, mPhyMemArrayInfoType16Strings, &MemArraySmbiosHande);
+
+  //
+  // Update the memory device information
+  //
+  mMemDevInfoType17.MemoryArrayHandle = MemArraySmbiosHande;
+}
+
+/***********************************************************************
+        SMBIOS data update  TYPE17  Memory Device Information
+************************************************************************/
+VOID
+MemDevInfoUpdateSmbiosType17 (
+                              VOID
+                              )
+{
+  LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER *)&mMemDevInfoType17, mMemDevInfoType17Strings, NULL);
+}
+
+/***********************************************************************
+        SMBIOS data update  TYPE19  Memory Array Map Information
+************************************************************************/
+VOID
+MemArrMapInfoUpdateSmbiosType19 (
+                                 VOID
+                                 )
+{
+  EFI_STATUS Status;
+  UINT32 Base;
+  UINT32 Size;
+
+  Status = mFwProtocol->GetArmMem(&Base, &Size);
+  if (Status != EFI_SUCCESS) {
+    DEBUG ((DEBUG_ERROR, "Couldn't get the ARM memory size: %r\n", Status));
+  } else {
+    mMemArrMapInfoType19.StartingAddress = Base / 1024;
+    mMemArrMapInfoType19.EndingAddress = (Base + Size - 1) / 1024;
+  }
+
+  LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER *)&mMemArrMapInfoType19, mMemArrMapInfoType19Strings, NULL);
+}
+
+
+/***********************************************************************
+        SMBIOS data update  TYPE32  Boot Information
+************************************************************************/
+VOID
+BootInfoUpdateSmbiosType32 (
+                            VOID
+                            )
+{
+  LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER *)&mBootInfoType32, mBootInfoType32Strings, NULL);
+}
+
+/***********************************************************************
+        Driver Entry
+************************************************************************/
+EFI_STATUS
+EFIAPI
+PlatformSmbiosDriverEntryPoint (
+                                IN EFI_HANDLE        ImageHandle,
+                                IN EFI_SYSTEM_TABLE  *SystemTable
+                                )
+{
+  EFI_STATUS Status;
+
+  Status = gBS->LocateProtocol (&gRaspberryPiFirmwareProtocolGuid, NULL,
+                                (VOID **)&mFwProtocol);
+  ASSERT_EFI_ERROR (Status);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  BIOSInfoUpdateSmbiosType0();
+
+  SysInfoUpdateSmbiosType1();
+
+  BoardInfoUpdateSmbiosType2();
+
+  EnclosureInfoUpdateSmbiosType3();
+
+  ProcessorInfoUpdateSmbiosType4 (4);   //One example for creating and updating
+
+  CacheInfoUpdateSmbiosType7();
+
+  SysSlotInfoUpdateSmbiosType9();
+
+  PhyMemArrayInfoUpdateSmbiosType16();
+
+  MemDevInfoUpdateSmbiosType17();
+
+  MemArrMapInfoUpdateSmbiosType19();
+
+  BootInfoUpdateSmbiosType32();
+
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/PlatformSmbiosDxe/PlatformSmbiosDxe.inf b/Platform/Broadcom/Bcm283x/Drivers/PlatformSmbiosDxe/PlatformSmbiosDxe.inf
new file mode 100644
index 000000000000..ada4c3fc228d
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/PlatformSmbiosDxe/PlatformSmbiosDxe.inf
@@ -0,0 +1,56 @@
+#/** @file
+#
+#  SMBIOS Table for ARM platform
+#
+#  Copyright (c) 2017, Andrei Warkentin <andrey.warkentin at gmail.com>
+#  Copyright (c) Microsoft Corporation. All rights reserved.
+#  Copyright (c) 2013 Linaro.org
+#
+#  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                    = 0x00010005
+  BASE_NAME                      = PlatformSmbiosDxe
+  FILE_GUID                      = BAD0554E-22E9-4D83-9AFD-CC87727A1A45
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = PlatformSmbiosDriverEntryPoint
+
+[Sources]
+  PlatformSmbiosDxe.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  ArmPlatformPkg/ArmPlatformPkg.dec
+  ArmPkg/ArmPkg.dec
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+
+[LibraryClasses]
+  UefiBootServicesTableLib
+  MemoryAllocationLib
+  BaseMemoryLib
+  BaseLib
+  UefiLib
+  UefiDriverEntryPoint
+  DebugLib
+  PrintLib
+
+[Protocols]
+  gEfiSmbiosProtocolGuid           # PROTOCOL SOMETIMES_CONSUMED
+  gRaspberryPiFirmwareProtocolGuid ## CONSUMES
+[Guids]
+
+[Depex]
+  gEfiSmbiosProtocolGuid AND gRaspberryPiFirmwareProtocolGuid
+
+[Pcd]
+  gArmTokenSpaceGuid.PcdSystemMemorySize
diff --git a/Platform/Broadcom/Bcm283x/Drivers/RpiFdtDxe/RpiFdtDxe.c b/Platform/Broadcom/Bcm283x/Drivers/RpiFdtDxe/RpiFdtDxe.c
new file mode 100644
index 000000000000..f2b0117629fd
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/RpiFdtDxe/RpiFdtDxe.c
@@ -0,0 +1,370 @@
+/** @file
+ *
+ *  Copyright (c) 2017, Andrey Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2016, Linaro, Ltd. 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 <PiDxe.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <libfdt.h>
+
+#include <Protocol/RaspberryPiFirmware.h>
+
+#include <Guid/Fdt.h>
+
+STATIC VOID                             *mFdtImage;
+
+STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL   *mFwProtocol;
+
+STATIC
+VOID
+UpdateMacAddress (
+  VOID
+  )
+{
+  INTN          Node;
+  INTN          Retval;
+  EFI_STATUS    Status;
+  UINT8         MacAddress[6];
+
+  //
+  // Locate the node that the 'ethernet' alias refers to
+  //
+  Node = fdt_path_offset(mFdtImage, "ethernet");
+  if (Node < 0) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to locate 'ethernet' alias\n",
+      __FUNCTION__));
+    return;
+  }
+
+  //
+  // Get the MAC address from the firmware
+  //
+  Status = mFwProtocol->GetMacAddress (MacAddress);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to retrieve MAC address\n", __FUNCTION__));
+    return;
+  }
+
+  Retval = fdt_setprop (mFdtImage, Node, "mac-address", MacAddress,
+    sizeof MacAddress);
+  if (Retval != 0) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to create 'mac-address' property (%d)\n",
+      __FUNCTION__, Retval));
+      return;
+  }
+
+  DEBUG ((DEBUG_INFO,
+    "%a: setting MAC address to %02x:%02x:%02x:%02x:%02x:%02x\n",
+    __FUNCTION__, MacAddress[0], MacAddress[1], MacAddress[2], MacAddress[3],
+    MacAddress[4], MacAddress[5]));
+}
+
+STATIC
+VOID
+CleanMemoryNodes (
+  VOID
+  )
+{
+  INTN Node;
+  INT32 Retval;
+
+  Node = fdt_path_offset(mFdtImage, "/memory");
+  if (Node < 0) {
+    return;
+  }
+
+  /*
+   * Remove bogus memory nodes which can make the booted
+   * OS go crazy and ignore the UEFI map.
+   */
+  DEBUG ((DEBUG_INFO, "Removing bogus /memory\n"));
+  Retval = fdt_del_node(mFdtImage, Node);
+  if (Retval != 0) {
+    DEBUG ((DEBUG_ERROR, "Failed to remove /memory\n"));
+  }
+}
+
+STATIC
+VOID
+SanitizePSCI (
+  VOID
+  )
+{
+  INTN Node;
+  INTN Root;
+  INT32 Retval;
+
+  Root = fdt_path_offset(mFdtImage, "/");
+  ASSERT (Root >= 0);
+  if (Root < 0) {
+    return;
+  }
+
+  Node = fdt_path_offset(mFdtImage, "/psci");
+  if (Node < 0) {
+    Node = fdt_add_subnode(mFdtImage, Root, "psci");
+  }
+
+  ASSERT (Node >= 0);
+  if (Node < 0) {
+    DEBUG ((DEBUG_ERROR, "Couldn't find/create /psci\n"));
+    return;
+  }
+
+  Retval = fdt_setprop_string(mFdtImage, Node, "compatible",
+                               "arm,psci-1.0");
+  if (Retval != 0) {
+    DEBUG ((DEBUG_ERROR, "Couldn't set /psci compatible property\n"));
+    return;
+  }
+
+  Retval = fdt_setprop_string(mFdtImage, Node, "method", "smc");
+  if (Retval != 0) {
+    DEBUG ((DEBUG_ERROR, "Couldn't set /psci method property\n"));
+    return;
+  }
+
+  Root = fdt_path_offset(mFdtImage, "/cpus");
+  if (Root < 0) {
+    DEBUG ((DEBUG_ERROR, "No CPUs to update with PSCI enable-method?\n"));
+    return;
+  }
+
+  Node = fdt_first_subnode(mFdtImage, Root);
+  while (Node >= 0) {
+    if (fdt_setprop_string(mFdtImage, Node, "enable-method", "psci") != 0) {
+      DEBUG ((DEBUG_ERROR, "Failed to update enable-method for a CPU\n"));
+      return;
+    }
+
+    fdt_delprop(mFdtImage, Node, "cpu-release-addr");
+    Node = fdt_next_subnode(mFdtImage, Node);
+  }
+}
+
+STATIC
+VOID
+CleanSimpleFramebuffer (
+  VOID
+  )
+{
+  INTN Node;
+  INT32 Retval;
+
+  /*
+   * Should look for nodes by kind and remove aliases
+   * by matching against device.
+   */
+  Node = fdt_path_offset(mFdtImage, "display0");
+  if (Node < 0) {
+    return;
+  }
+
+  /*
+   * Remove bogus GPU-injected simple-framebuffer, which
+   * doesn't reflect the framebuffer built by UEFI.
+   */
+  DEBUG ((DEBUG_INFO, "Removing bogus display0\n"));
+  Retval = fdt_del_node(mFdtImage, Node);
+  if (Retval != 0) {
+    DEBUG ((DEBUG_ERROR, "Failed to remove display0\n"));
+    return;
+  }
+
+  Node =  fdt_path_offset(mFdtImage, "/aliases");
+  if (Node < 0) {
+    DEBUG ((DEBUG_ERROR, "Couldn't find /aliases to remove display0\n"));
+    return;
+  }
+
+  Retval = fdt_delprop(mFdtImage, Node, "display0");
+  if (Retval != 0) {
+    DEBUG ((DEBUG_ERROR, "Failed to remove display0 alias\n"));
+  }
+}
+
+#define MAX_CMDLINE_SIZE    512
+
+STATIC
+VOID
+UpdateBootArgs (
+  VOID
+  )
+{
+  INTN          Node;
+  INTN          Retval;
+  EFI_STATUS    Status;
+  CHAR8         *CommandLine;
+
+  //
+  // Locate the /chosen node
+  //
+  Node = fdt_path_offset(mFdtImage, "/chosen");
+  if (Node < 0) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to locate /chosen node\n",
+      __FUNCTION__));
+    return;
+  }
+
+  //
+  // If /chosen/bootargs already exists, we want to add a space character
+  // before adding the firmware supplied arguments. However, the RpiFirmware
+  // protocol expects a 32-bit aligned buffer. So let's allocate 4 bytes of
+  // slack, and skip the first 3 when passing this buffer into libfdt.
+  //
+  CommandLine = AllocatePool (MAX_CMDLINE_SIZE) + 4;
+  if (!CommandLine) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to allocate memory\n", __FUNCTION__));
+    return;
+  }
+
+  //
+  // Get the command line from the firmware
+  //
+  Status = mFwProtocol->GetCommandLine (MAX_CMDLINE_SIZE, CommandLine + 4);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to retrieve command line\n",
+      __FUNCTION__));
+    return;
+  }
+
+  if (AsciiStrLen (CommandLine + 4) == 0) {
+    DEBUG ((DEBUG_INFO, "%a: empty command line received\n", __FUNCTION__));
+    return;
+  }
+
+  CommandLine[3] = ' ';
+
+  Retval = fdt_appendprop_string (mFdtImage, Node, "bootargs", &CommandLine[3]);
+  if (Retval != 0) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to set /chosen/bootargs property (%d)\n",
+      __FUNCTION__, Retval));
+  }
+
+  DEBUG_CODE_BEGIN ();
+    CONST CHAR8    *Prop;
+    INT32         Length;
+    INT32         Index;
+
+    Node = fdt_path_offset (mFdtImage, "/chosen");
+    ASSERT (Node >= 0);
+
+    Prop = fdt_getprop (mFdtImage, Node, "bootargs", &Length);
+    ASSERT (Prop != NULL);
+
+    DEBUG ((DEBUG_INFO, "Command line set from firmware (length %d):\n'", Length));
+
+    for (Index = 0; Index < Length; Index++, Prop++) {
+      if (*Prop == '\0') {
+        continue;
+      }
+      DEBUG ((DEBUG_INFO, "%c", *Prop));
+    }
+
+    DEBUG ((DEBUG_INFO, "'\n"));
+  DEBUG_CODE_END ();
+
+  FreePool (CommandLine - 4);
+}
+
+
+/**
+  @param  ImageHandle   of the loaded driver
+  @param  SystemTable   Pointer to the System Table
+
+  @retval EFI_SUCCESS           Protocol registered
+  @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
+  @retval EFI_DEVICE_ERROR      Hardware problems
+
+**/
+EFI_STATUS
+EFIAPI
+RpiFdtDxeInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_STATUS Status;
+  VOID       *FdtImage;
+  UINTN      FdtSize;
+  INT32      Retval;
+  BOOLEAN    Internal;
+
+  Status = gBS->LocateProtocol (&gRaspberryPiFirmwareProtocolGuid, NULL,
+                  (VOID **)&mFwProtocol);
+  ASSERT_EFI_ERROR (Status);
+
+  Internal = FALSE;
+  FdtImage =  (VOID *) (UINTN) PcdGet32(PcdFdtBaseAddress);
+  Retval = fdt_check_header (FdtImage);
+  if (Retval == 0) {
+    /*
+     * Have FDT passed via config.txt.
+     */
+    FdtSize = fdt_totalsize (FdtImage);
+    DEBUG ((DEBUG_INFO, "DTB passed via config.txt of 0x%lx bytes\n", FdtSize));
+    Status = EFI_SUCCESS;
+  } else {
+    Internal = TRUE;
+    DEBUG ((DEBUG_INFO, "No/bad FDT at %p (%a), trying internal DTB...\n",
+            FdtImage, fdt_strerror (Retval)));
+    Status = GetSectionFromAnyFv (&gRaspberryPiFdtFileGuid, EFI_SECTION_RAW, 0,
+                                  &FdtImage, &FdtSize);
+    if (Status == EFI_SUCCESS) {
+      if (fdt_check_header (FdtImage) != 0) {
+        Status = EFI_INCOMPATIBLE_VERSION;
+      }
+    }
+  }
+
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed to locate device tree: %r\n", Status));
+    return Status;
+  }
+
+  /*
+   * Probably overkill.
+   */
+  FdtSize += EFI_PAGE_SIZE * 2;
+  Status = gBS->AllocatePages (AllocateAnyPages, EfiRuntimeServicesData,
+                               EFI_SIZE_TO_PAGES(FdtSize),
+                               (EFI_PHYSICAL_ADDRESS *) &mFdtImage);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Failed to allocate new device tree: %r\n", Status));
+    return Status;
+  }
+
+  Retval = fdt_open_into (FdtImage, mFdtImage, FdtSize);
+  ASSERT (Retval == 0);
+
+  SanitizePSCI ();
+  CleanMemoryNodes ();
+  CleanSimpleFramebuffer ();
+  UpdateMacAddress ();
+  if (Internal) {
+    /*
+     * A GPU-provided DTB already has the full command line.
+     */
+    UpdateBootArgs ();
+  }
+
+  DEBUG ((DEBUG_INFO, "Installed FDT is at %p\n", mFdtImage));
+  Status = gBS->InstallConfigurationTable (&gFdtTableGuid, mFdtImage);
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/RpiFdtDxe/RpiFdtDxe.inf b/Platform/Broadcom/Bcm283x/Drivers/RpiFdtDxe/RpiFdtDxe.inf
new file mode 100644
index 000000000000..a79e9ddcdc8a
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/RpiFdtDxe/RpiFdtDxe.inf
@@ -0,0 +1,53 @@
+#/** @file
+#
+#  Copyright (c) 2017, Andrei Warkentin <andrey.warkentin at gmail.com>
+#  Copyright (c) 2016, Linaro, Ltd. 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                    = 0x00010019
+  BASE_NAME                      = RpiFdtDxe
+  FILE_GUID                      = 8505280f-109e-437e-9fe4-1aa09c7074d9
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = RpiFdtDxeInitialize
+
+[Sources]
+  RpiFdtDxe.c
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  DxeServicesLib
+  FdtLib
+  MemoryAllocationLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+
+[Guids]
+  gFdtTableGuid
+  gRaspberryPiFdtFileGuid
+
+[Protocols]
+  gRaspberryPiFirmwareProtocolGuid              ## CONSUMES
+
+[Depex]
+  gRaspberryPiFirmwareProtocolGuid
+
+[FixedPcd]
+  gRaspberryPiTokenSpaceGuid.PcdFdtBaseAddress
diff --git a/Platform/Broadcom/Bcm283x/Drivers/RpiFirmwareDxe/RpiFirmwareDxe.c b/Platform/Broadcom/Bcm283x/Drivers/RpiFirmwareDxe/RpiFirmwareDxe.c
new file mode 100644
index 000000000000..50f3ed3f1e36
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/RpiFirmwareDxe/RpiFirmwareDxe.c
@@ -0,0 +1,1085 @@
+/** @file
+ *
+ *  Copyright (c) 2017-2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2016, Linaro, Ltd. 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 <PiDxe.h>
+
+#include <Library/ArmLib.h>
+#include <Library/DmaLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/SynchronizationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include <IndustryStandard/Bcm2836.h>
+#include <IndustryStandard/RpiFirmware.h>
+
+#include <Protocol/RaspberryPiFirmware.h>
+
+//
+// The number of statically allocated buffer pages
+//
+#define NUM_PAGES   1
+
+//
+// The number of iterations to perform when waiting for the mailbox
+// status to change
+//
+#define MAX_TRIES   0x100000
+
+STATIC VOID  *mDmaBuffer;
+STATIC VOID  *mDmaBufferMapping;
+STATIC UINTN mDmaBufferBusAddress;
+
+STATIC SPIN_LOCK mMailboxLock;
+
+STATIC
+BOOLEAN
+DrainMailbox (
+  VOID
+  )
+{
+  INTN    Tries;
+  UINT32  Val;
+
+  //
+  // Get rid of stale response data in the mailbox
+  //
+  Tries = 0;
+  do {
+    Val = MmioRead32 (BCM2836_MBOX_BASE_ADDRESS + BCM2836_MBOX_STATUS_OFFSET);
+    if (Val & (1U << BCM2836_MBOX_STATUS_EMPTY)) {
+      return TRUE;
+    }
+    ArmDataSynchronizationBarrier ();
+    MmioRead32 (BCM2836_MBOX_BASE_ADDRESS + BCM2836_MBOX_READ_OFFSET);
+  } while (++Tries < MAX_TRIES);
+
+  return FALSE;
+}
+
+STATIC
+BOOLEAN
+MailboxWaitForStatusCleared (
+  IN  UINTN   StatusMask
+  )
+{
+  INTN    Tries;
+  UINT32  Val;
+
+  //
+  // Get rid of stale response data in the mailbox
+  //
+  Tries = 0;
+  do {
+    Val = MmioRead32 (BCM2836_MBOX_BASE_ADDRESS + BCM2836_MBOX_STATUS_OFFSET);
+    if ((Val & StatusMask) == 0) {
+      return TRUE;
+    }
+    ArmDataSynchronizationBarrier ();
+  } while (++Tries < MAX_TRIES);
+
+  return FALSE;
+}
+
+STATIC
+EFI_STATUS
+MailboxTransaction (
+  IN    UINTN   Length,
+  IN    UINTN   Channel,
+  OUT   UINT32  *Result
+  )
+{
+  if (Channel >= BCM2836_MBOX_NUM_CHANNELS) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Get rid of stale response data in the mailbox
+  //
+  if (!DrainMailbox ()) {
+    DEBUG ((DEBUG_ERROR, "%a: timeout waiting for mailbox to drain\n",
+      __FUNCTION__));
+    return EFI_TIMEOUT;
+  }
+
+  //
+  // Wait for the 'output register full' bit to become clear
+  //
+  if (!MailboxWaitForStatusCleared (1U << BCM2836_MBOX_STATUS_FULL)) {
+    DEBUG ((DEBUG_ERROR, "%a: timeout waiting for outbox to become empty\n",
+      __FUNCTION__));
+    return EFI_TIMEOUT;
+  }
+
+  ArmDataSynchronizationBarrier ();
+
+  //
+  // Start the mailbox transaction
+  //
+  MmioWrite32 (BCM2836_MBOX_BASE_ADDRESS + BCM2836_MBOX_WRITE_OFFSET,
+    (UINT32)((UINTN)mDmaBufferBusAddress | Channel));
+
+  ArmDataSynchronizationBarrier ();
+
+  //
+  // Wait for the 'input register empty' bit to clear
+  //
+  if (!MailboxWaitForStatusCleared (1U << BCM2836_MBOX_STATUS_EMPTY)) {
+    DEBUG ((DEBUG_ERROR, "%a: timeout waiting for inbox to become full\n",
+      __FUNCTION__));
+    return EFI_TIMEOUT;
+  }
+
+  //
+  // Read back the result
+  //
+  ArmDataSynchronizationBarrier ();
+  *Result = MmioRead32 (BCM2836_MBOX_BASE_ADDRESS + BCM2836_MBOX_READ_OFFSET);
+  ArmDataSynchronizationBarrier ();
+
+  return EFI_SUCCESS;
+}
+
+#pragma pack(1)
+typedef struct {
+  UINT32    BufferSize;
+  UINT32    Response;
+} RPI_FW_BUFFER_HEAD;
+
+typedef struct {
+  UINT32    TagId;
+  UINT32    TagSize;
+  UINT32    TagValueSize;
+} RPI_FW_TAG_HEAD;
+
+typedef struct {
+  UINT32                      DeviceId;
+  UINT32                      PowerState;
+} RPI_FW_POWER_STATE_TAG;
+
+typedef struct {
+  RPI_FW_BUFFER_HEAD        BufferHead;
+  RPI_FW_TAG_HEAD           TagHead;
+  RPI_FW_POWER_STATE_TAG    TagBody;
+  UINT32                    EndTag;
+} RPI_FW_SET_POWER_STATE_CMD;
+#pragma pack()
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareSetPowerState (
+  IN  UINT32    DeviceId,
+  IN  BOOLEAN   PowerState,
+  IN  BOOLEAN   Wait
+  )
+{
+  RPI_FW_SET_POWER_STATE_CMD      *Cmd;
+  EFI_STATUS                        Status;
+  UINT32                            Result;
+
+  if (!AcquireSpinLockOrFail (&mMailboxLock)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to acquire spinlock\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Cmd = mDmaBuffer;
+  ZeroMem (Cmd, sizeof *Cmd);
+
+  Cmd->BufferHead.BufferSize  = sizeof *Cmd;
+  Cmd->BufferHead.Response    = 0;
+  Cmd->TagHead.TagId          = RPI_FW_SET_POWER_STATE;
+  Cmd->TagHead.TagSize        = sizeof Cmd->TagBody;
+  Cmd->TagHead.TagValueSize   = 0;
+  Cmd->TagBody.DeviceId       = DeviceId;
+  Cmd->TagBody.PowerState     = (PowerState ? RPI_FW_POWER_STATE_ENABLE : 0) |
+                                (Wait ? RPI_FW_POWER_STATE_WAIT : 0);
+  Cmd->EndTag                 = 0;
+
+  Status = MailboxTransaction (Cmd->BufferHead.BufferSize, RPI_FW_MBOX_CHANNEL, &Result);
+
+  ReleaseSpinLock (&mMailboxLock);
+
+  if (EFI_ERROR (Status) ||
+      Cmd->BufferHead.Response != RPI_FW_RESP_SUCCESS) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: mailbox transaction error: Status == %r, Response == 0x%x\n",
+      __FUNCTION__, Status, Cmd->BufferHead.Response));
+    Status = EFI_DEVICE_ERROR;
+  }
+
+  if (!EFI_ERROR (Status) &&
+      PowerState ^ (Cmd->TagBody.PowerState & RPI_FW_POWER_STATE_ENABLE)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to %sable power for device %d\n",
+      __FUNCTION__, PowerState ? "en" : "dis", DeviceId));
+      Status = EFI_DEVICE_ERROR;
+  }
+
+  return Status;
+}
+
+#pragma pack()
+typedef struct {
+  UINT32                    Base;
+  UINT32                    Size;
+} RPI_FW_ARM_MEMORY_TAG;
+
+typedef struct {
+  RPI_FW_BUFFER_HEAD        BufferHead;
+  RPI_FW_TAG_HEAD           TagHead;
+  RPI_FW_ARM_MEMORY_TAG     TagBody;
+  UINT32                    EndTag;
+} RPI_FW_GET_ARM_MEMORY_CMD;
+#pragma pack()
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareGetArmMemory (
+  OUT   UINT32 *Base,
+  OUT   UINT32 *Size
+  )
+{
+  RPI_FW_GET_ARM_MEMORY_CMD   *Cmd;
+  EFI_STATUS                  Status;
+  UINT32                      Result;
+
+  if (!AcquireSpinLockOrFail (&mMailboxLock)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to acquire spinlock\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Cmd = mDmaBuffer;
+  ZeroMem (Cmd, sizeof *Cmd);
+
+  Cmd->BufferHead.BufferSize  = sizeof *Cmd;
+  Cmd->BufferHead.Response    = 0;
+  Cmd->TagHead.TagId          = RPI_FW_GET_ARM_MEMSIZE;
+  Cmd->TagHead.TagSize        = sizeof Cmd->TagBody;
+  Cmd->TagHead.TagValueSize   = 0;
+  Cmd->EndTag                 = 0;
+
+  Status = MailboxTransaction (Cmd->BufferHead.BufferSize, RPI_FW_MBOX_CHANNEL, &Result);
+
+  ReleaseSpinLock (&mMailboxLock);
+
+  if (EFI_ERROR (Status) ||
+      Cmd->BufferHead.Response != RPI_FW_RESP_SUCCESS) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: mailbox transaction error: Status == %r, Response == 0x%x\n",
+      __FUNCTION__, Status, Cmd->BufferHead.Response));
+    return EFI_DEVICE_ERROR;
+  }
+
+  *Base = Cmd->TagBody.Base;
+  *Size = Cmd->TagBody.Size;
+  return EFI_SUCCESS;
+}
+
+#pragma pack()
+typedef struct {
+  UINT8                     MacAddress[6];
+  UINT32                    Padding;
+} RPI_FW_MAC_ADDR_TAG;
+
+typedef struct {
+  RPI_FW_BUFFER_HEAD        BufferHead;
+  RPI_FW_TAG_HEAD           TagHead;
+  RPI_FW_MAC_ADDR_TAG       TagBody;
+  UINT32                    EndTag;
+} RPI_FW_GET_MAC_ADDR_CMD;
+#pragma pack()
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareGetMacAddress (
+  OUT   UINT8   MacAddress[6]
+  )
+{
+  RPI_FW_GET_MAC_ADDR_CMD     *Cmd;
+  EFI_STATUS                  Status;
+  UINT32                      Result;
+
+  if (!AcquireSpinLockOrFail (&mMailboxLock)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to acquire spinlock\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Cmd = mDmaBuffer;
+  ZeroMem (Cmd, sizeof *Cmd);
+
+  Cmd->BufferHead.BufferSize  = sizeof *Cmd;
+  Cmd->BufferHead.Response    = 0;
+  Cmd->TagHead.TagId          = RPI_FW_GET_MAC_ADDRESS;
+  Cmd->TagHead.TagSize        = sizeof Cmd->TagBody;
+  Cmd->TagHead.TagValueSize   = 0;
+  Cmd->EndTag                 = 0;
+
+  Status = MailboxTransaction (Cmd->BufferHead.BufferSize, RPI_FW_MBOX_CHANNEL, &Result);
+
+  ReleaseSpinLock (&mMailboxLock);
+
+  if (EFI_ERROR (Status) ||
+      Cmd->BufferHead.Response != RPI_FW_RESP_SUCCESS) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: mailbox transaction error: Status == %r, Response == 0x%x\n",
+      __FUNCTION__, Status, Cmd->BufferHead.Response));
+    return EFI_DEVICE_ERROR;
+  }
+
+  CopyMem (MacAddress, Cmd->TagBody.MacAddress, sizeof Cmd->TagBody.MacAddress);
+  return EFI_SUCCESS;
+}
+
+#pragma pack()
+typedef struct {
+  UINT64                    Serial;
+} RPI_FW_SERIAL_TAG;
+
+typedef struct {
+  RPI_FW_BUFFER_HEAD        BufferHead;
+  RPI_FW_TAG_HEAD           TagHead;
+  RPI_FW_SERIAL_TAG         TagBody;
+  UINT32                    EndTag;
+} RPI_FW_GET_SERIAL_CMD;
+#pragma pack()
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareGetSerial (
+  OUT   UINT64 *Serial
+  )
+{
+  RPI_FW_GET_SERIAL_CMD       *Cmd;
+  EFI_STATUS                  Status;
+  UINT32                      Result;
+
+  if (!AcquireSpinLockOrFail (&mMailboxLock)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to acquire spinlock\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Cmd = mDmaBuffer;
+  ZeroMem (Cmd, sizeof *Cmd);
+
+  Cmd->BufferHead.BufferSize  = sizeof *Cmd;
+  Cmd->BufferHead.Response    = 0;
+  Cmd->TagHead.TagId          = RPI_FW_GET_BOARD_SERIAL;
+  Cmd->TagHead.TagSize        = sizeof Cmd->TagBody;
+  Cmd->TagHead.TagValueSize   = 0;
+  Cmd->EndTag                 = 0;
+
+  Status = MailboxTransaction (Cmd->BufferHead.BufferSize, RPI_FW_MBOX_CHANNEL, &Result);
+
+  ReleaseSpinLock (&mMailboxLock);
+
+  if (EFI_ERROR (Status) ||
+      Cmd->BufferHead.Response != RPI_FW_RESP_SUCCESS) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: mailbox transaction error: Status == %r, Response == 0x%x\n",
+      __FUNCTION__, Status, Cmd->BufferHead.Response));
+    return EFI_DEVICE_ERROR;
+  }
+
+  *Serial = Cmd->TagBody.Serial;
+  return EFI_SUCCESS;
+}
+
+#pragma pack()
+typedef struct {
+  UINT32                    Model;
+} RPI_FW_MODEL_TAG;
+
+typedef struct {
+  RPI_FW_BUFFER_HEAD        BufferHead;
+  RPI_FW_TAG_HEAD           TagHead;
+  RPI_FW_MODEL_TAG          TagBody;
+  UINT32                    EndTag;
+} RPI_FW_GET_MODEL_CMD;
+#pragma pack()
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareGetModel (
+  OUT   UINT32 *Model
+  )
+{
+  RPI_FW_GET_MODEL_CMD       *Cmd;
+  EFI_STATUS                  Status;
+  UINT32                      Result;
+
+  if (!AcquireSpinLockOrFail (&mMailboxLock)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to acquire spinlock\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Cmd = mDmaBuffer;
+  ZeroMem (Cmd, sizeof *Cmd);
+
+  Cmd->BufferHead.BufferSize  = sizeof *Cmd;
+  Cmd->BufferHead.Response    = 0;
+  Cmd->TagHead.TagId          = RPI_FW_GET_BOARD_MODEL;
+  Cmd->TagHead.TagSize        = sizeof Cmd->TagBody;
+  Cmd->TagHead.TagValueSize   = 0;
+  Cmd->EndTag                 = 0;
+
+  Status = MailboxTransaction (Cmd->BufferHead.BufferSize, RPI_FW_MBOX_CHANNEL, &Result);
+
+  ReleaseSpinLock (&mMailboxLock);
+
+  if (EFI_ERROR (Status) ||
+      Cmd->BufferHead.Response != RPI_FW_RESP_SUCCESS) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: mailbox transaction error: Status == %r, Response == 0x%x\n",
+      __FUNCTION__, Status, Cmd->BufferHead.Response));
+    return EFI_DEVICE_ERROR;
+  }
+
+  *Model = Cmd->TagBody.Model;
+  return EFI_SUCCESS;
+}
+
+#pragma pack()
+typedef struct {
+  UINT32                    Revision;
+} RPI_FW_MODEL_REVISION_TAG;
+
+typedef struct {
+  RPI_FW_BUFFER_HEAD        BufferHead;
+  RPI_FW_TAG_HEAD           TagHead;
+  RPI_FW_MODEL_REVISION_TAG TagBody;
+  UINT32                    EndTag;
+} RPI_FW_GET_MODEL_REVISION_CMD;
+#pragma pack()
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareGetModelRevision (
+  OUT   UINT32 *Revision
+  )
+{
+  RPI_FW_GET_MODEL_REVISION_CMD *Cmd;
+  EFI_STATUS                    Status;
+  UINT32                        Result;
+
+  if (!AcquireSpinLockOrFail (&mMailboxLock)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to acquire spinlock\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Cmd = mDmaBuffer;
+  ZeroMem (Cmd, sizeof *Cmd);
+
+  Cmd->BufferHead.BufferSize  = sizeof *Cmd;
+  Cmd->BufferHead.Response    = 0;
+  Cmd->TagHead.TagId          = RPI_FW_GET_BOARD_REVISION;
+  Cmd->TagHead.TagSize        = sizeof Cmd->TagBody;
+  Cmd->TagHead.TagValueSize   = 0;
+  Cmd->EndTag                 = 0;
+
+  Status = MailboxTransaction (Cmd->BufferHead.BufferSize, RPI_FW_MBOX_CHANNEL, &Result);
+
+  ReleaseSpinLock (&mMailboxLock);
+
+  if (EFI_ERROR (Status) ||
+      Cmd->BufferHead.Response != RPI_FW_RESP_SUCCESS) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: mailbox transaction error: Status == %r, Response == 0x%x\n",
+      __FUNCTION__, Status, Cmd->BufferHead.Response));
+    return EFI_DEVICE_ERROR;
+  }
+
+  *Revision = Cmd->TagBody.Revision;
+  return EFI_SUCCESS;
+}
+
+#pragma pack()
+typedef struct {
+  UINT32 Width;
+  UINT32 Height;
+} RPI_FW_FB_SIZE_TAG;
+
+typedef struct {
+  RPI_FW_BUFFER_HEAD        BufferHead;
+  RPI_FW_TAG_HEAD           TagHead;
+  RPI_FW_FB_SIZE_TAG        TagBody;
+  UINT32                    EndTag;
+} RPI_FW_GET_FB_SIZE_CMD;
+
+typedef struct {
+  UINT32 Depth;
+} RPI_FW_FB_DEPTH_TAG;
+
+typedef struct {
+  UINT32 Pitch;
+} RPI_FW_FB_PITCH_TAG;
+
+typedef struct {
+  UINT32 AlignmentBase;
+  UINT32 Size;
+} RPI_FW_FB_ALLOC_TAG;
+
+typedef struct {
+  RPI_FW_BUFFER_HEAD        BufferHead;
+  RPI_FW_TAG_HEAD           FreeFbTag;
+  UINT32                    EndTag;
+} RPI_FW_FREE_FB_CMD;
+
+typedef struct {
+  RPI_FW_BUFFER_HEAD        BufferHead;
+  RPI_FW_TAG_HEAD           PhysSizeTag;
+  RPI_FW_FB_SIZE_TAG        PhysSize;
+  RPI_FW_TAG_HEAD           VirtSizeTag;
+  RPI_FW_FB_SIZE_TAG        VirtSize;
+  RPI_FW_TAG_HEAD           DepthTag;
+  RPI_FW_FB_DEPTH_TAG       Depth;
+  RPI_FW_TAG_HEAD           AllocFbTag;
+  RPI_FW_FB_ALLOC_TAG       AllocFb;
+  RPI_FW_TAG_HEAD           PitchTag;
+  RPI_FW_FB_PITCH_TAG       Pitch;
+  UINT32                    EndTag;
+} RPI_FW_INIT_FB_CMD;
+#pragma pack()
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareGetFbSize (
+  OUT   UINT32 *Width,
+  OUT   UINT32 *Height
+  )
+{
+  RPI_FW_GET_FB_SIZE_CMD     *Cmd;
+  EFI_STATUS                  Status;
+  UINT32                      Result;
+
+  if (!AcquireSpinLockOrFail (&mMailboxLock)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to acquire spinlock\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Cmd = mDmaBuffer;
+  ZeroMem (Cmd, sizeof *Cmd);
+
+  Cmd->BufferHead.BufferSize  = sizeof *Cmd;
+  Cmd->BufferHead.Response    = 0;
+  Cmd->TagHead.TagId          = RPI_FW_GET_FB_GEOMETRY;
+  Cmd->TagHead.TagSize        = sizeof Cmd->TagBody;
+  Cmd->TagHead.TagValueSize   = 0;
+  Cmd->EndTag                 = 0;
+
+  Status = MailboxTransaction (Cmd->BufferHead.BufferSize, RPI_FW_MBOX_CHANNEL, &Result);
+
+  ReleaseSpinLock (&mMailboxLock);
+
+  if (EFI_ERROR (Status) ||
+      Cmd->BufferHead.Response != RPI_FW_RESP_SUCCESS) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: mailbox  transaction error: Status == %r, Response == 0x%x\n",
+      __FUNCTION__, Status, Cmd->BufferHead.Response));
+    return EFI_DEVICE_ERROR;
+  }
+
+  *Width = Cmd->TagBody.Width;
+  *Height = Cmd->TagBody.Height;
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareFreeFb (VOID)
+{
+  RPI_FW_FREE_FB_CMD *Cmd;
+  EFI_STATUS         Status;
+  UINT32             Result;
+
+  if (!AcquireSpinLockOrFail (&mMailboxLock)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to acquire spinlock\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Cmd = mDmaBuffer;
+  ZeroMem (Cmd, sizeof *Cmd);
+
+  Cmd->BufferHead.BufferSize     = sizeof *Cmd;
+  Cmd->BufferHead.Response       = 0;
+
+  Cmd->FreeFbTag.TagId         = RPI_FW_FREE_FB;
+  Cmd->FreeFbTag.TagSize       = 0;
+  Cmd->FreeFbTag.TagValueSize  = 0;
+  Cmd->EndTag                  = 0;
+
+  Status = MailboxTransaction (Cmd->BufferHead.BufferSize, RPI_FW_MBOX_CHANNEL, &Result);
+  ReleaseSpinLock (&mMailboxLock);
+
+  if (EFI_ERROR (Status) ||
+      Cmd->BufferHead.Response != RPI_FW_RESP_SUCCESS) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: mailbox transaction error: Status == %r, Response == 0x%x\n",
+      __FUNCTION__, Status, Cmd->BufferHead.Response));
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareAllocFb (
+  IN  UINT32 Width,
+  IN  UINT32 Height,
+  IN  UINT32 Depth,
+  OUT EFI_PHYSICAL_ADDRESS *FbBase,
+  OUT UINTN *FbSize,
+  OUT UINTN *Pitch)
+{
+  RPI_FW_INIT_FB_CMD *Cmd;
+  EFI_STATUS         Status;
+  UINT32             Result;
+
+  ASSERT (FbSize != NULL);
+  ASSERT (FbBase != NULL);
+
+  if (!AcquireSpinLockOrFail (&mMailboxLock)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to acquire spinlock\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Cmd = mDmaBuffer;
+  ZeroMem (Cmd, sizeof *Cmd);
+
+  Cmd->BufferHead.BufferSize     = sizeof *Cmd;
+  Cmd->BufferHead.Response       = 0;
+
+  Cmd->PhysSizeTag.TagId         = RPI_FW_SET_FB_PGEOM;
+  Cmd->PhysSizeTag.TagSize       = sizeof Cmd->PhysSize;
+  Cmd->PhysSize.Width = Width;
+  Cmd->PhysSize.Height = Height;
+  Cmd->VirtSizeTag.TagId         = RPI_FW_SET_FB_VGEOM;
+  Cmd->VirtSizeTag.TagSize       = sizeof Cmd->VirtSize;
+  Cmd->VirtSize.Width = Width;
+  Cmd->VirtSize.Height = Height;
+  Cmd->DepthTag.TagId            = RPI_FW_SET_FB_DEPTH;
+  Cmd->DepthTag.TagSize          = sizeof Cmd->Depth;
+  Cmd->Depth.Depth               = Depth;
+  Cmd->AllocFbTag.TagId          = RPI_FW_ALLOC_FB;
+  Cmd->AllocFbTag.TagSize        = sizeof Cmd->AllocFb;
+  Cmd->AllocFb.AlignmentBase     = 32;
+  Cmd->PitchTag.TagId            = RPI_FW_GET_FB_LINELENGTH;
+  Cmd->PitchTag.TagSize          = sizeof Cmd->Pitch;
+  Cmd->EndTag                    = 0;
+
+  Status = MailboxTransaction (Cmd->BufferHead.BufferSize, RPI_FW_MBOX_CHANNEL, &Result);
+
+  ReleaseSpinLock (&mMailboxLock);
+
+  if (EFI_ERROR (Status) ||
+      Cmd->BufferHead.Response != RPI_FW_RESP_SUCCESS) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: mailbox transaction error: Status == %r, Response == 0x%x\n",
+      __FUNCTION__, Status, Cmd->BufferHead.Response));
+    return EFI_DEVICE_ERROR;
+  }
+
+  *Pitch = Cmd->Pitch.Pitch;
+  *FbBase = Cmd->AllocFb.AlignmentBase - BCM2836_DMA_DEVICE_OFFSET;
+  *FbSize = Cmd->AllocFb.Size;
+  return EFI_SUCCESS;
+}
+
+#pragma pack()
+typedef struct {
+  RPI_FW_BUFFER_HEAD        BufferHead;
+  RPI_FW_TAG_HEAD           TagHead;
+  UINT8                     CommandLine[0];
+} RPI_FW_GET_COMMAND_LINE_CMD;
+#pragma pack()
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareGetCommmandLine (
+  IN  UINTN               BufferSize,
+  OUT CHAR8               CommandLine[]
+  )
+{
+  RPI_FW_GET_COMMAND_LINE_CMD  *Cmd;
+  EFI_STATUS                    Status;
+  UINT32                        Result;
+
+  if ((BufferSize % sizeof (UINT32)) != 0) {
+    DEBUG ((DEBUG_ERROR, "%a: BufferSize must be a multiple of 4\n",
+      __FUNCTION__));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (sizeof *Cmd + BufferSize > EFI_PAGES_TO_SIZE (NUM_PAGES)) {
+    DEBUG ((DEBUG_ERROR, "%a: BufferSize exceeds size of DMA buffer\n",
+      __FUNCTION__));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  if (!AcquireSpinLockOrFail (&mMailboxLock)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to acquire spinlock\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Cmd = mDmaBuffer;
+  ZeroMem (Cmd, sizeof *Cmd + BufferSize + sizeof (UINT32));
+
+  Cmd->BufferHead.BufferSize  = sizeof *Cmd + BufferSize + sizeof (UINT32);
+  Cmd->BufferHead.Response    = 0;
+  Cmd->TagHead.TagId          = RPI_FW_GET_COMMAND_LINE;
+  Cmd->TagHead.TagSize        = BufferSize;
+  Cmd->TagHead.TagValueSize   = 0;
+
+  Status = MailboxTransaction (Cmd->BufferHead.BufferSize, RPI_FW_MBOX_CHANNEL, &Result);
+
+  ReleaseSpinLock (&mMailboxLock);
+
+  if (EFI_ERROR (Status) ||
+      Cmd->BufferHead.Response != RPI_FW_RESP_SUCCESS) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: mailbox transaction error: Status == %r, Response == 0x%x\n",
+      __FUNCTION__, Status, Cmd->BufferHead.Response));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Cmd->TagHead.TagValueSize &= ~RPI_FW_VALUE_SIZE_RESPONSE_MASK;
+  if (Cmd->TagHead.TagValueSize >= BufferSize &&
+      Cmd->CommandLine[Cmd->TagHead.TagValueSize - 1] != '\0') {
+    DEBUG ((DEBUG_ERROR, "%a: insufficient buffer size\n", __FUNCTION__));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  CopyMem (CommandLine, Cmd->CommandLine, Cmd->TagHead.TagValueSize);
+
+  if (CommandLine[Cmd->TagHead.TagValueSize - 1] != '\0') {
+    //
+    // Add a NUL terminator if required.
+    //
+    CommandLine[Cmd->TagHead.TagValueSize] = '\0';
+  }
+
+  return EFI_SUCCESS;
+}
+
+#pragma pack()
+typedef struct {
+  UINT32                    ClockId;
+  UINT32                    ClockRate;
+  UINT32                    SkipTurbo;
+} RPI_FW_SET_CLOCK_RATE_TAG;
+
+typedef struct {
+  RPI_FW_BUFFER_HEAD        BufferHead;
+  RPI_FW_TAG_HEAD           TagHead;
+  RPI_FW_SET_CLOCK_RATE_TAG TagBody;
+  UINT32                    EndTag;
+} RPI_FW_SET_CLOCK_RATE_CMD;
+#pragma pack()
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareSetClockRate (
+  IN  UINT32 ClockId,
+  IN  UINT32 ClockRate
+  )
+{
+  RPI_FW_SET_CLOCK_RATE_CMD   *Cmd;
+  EFI_STATUS                  Status;
+  UINT32                      Result;
+
+  if (!AcquireSpinLockOrFail (&mMailboxLock)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to acquire spinlock\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Cmd = mDmaBuffer;
+  ZeroMem (Cmd, sizeof *Cmd);
+
+  Cmd->BufferHead.BufferSize  = sizeof *Cmd;
+  Cmd->BufferHead.Response    = 0;
+  Cmd->TagHead.TagId          = RPI_FW_SET_CLOCK_RATE,
+  Cmd->TagHead.TagSize        = sizeof Cmd->TagBody;
+  Cmd->TagHead.TagValueSize   = 0;
+  Cmd->TagBody.ClockId        = ClockId;
+  Cmd->TagBody.ClockRate      = ClockRate;
+  Cmd->EndTag                 = 0;
+
+  Status = MailboxTransaction (Cmd->BufferHead.BufferSize, RPI_FW_MBOX_CHANNEL, &Result);
+
+  ReleaseSpinLock (&mMailboxLock);
+
+  if (EFI_ERROR (Status) ||
+      Cmd->BufferHead.Response != RPI_FW_RESP_SUCCESS) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: mailbox transaction error: Status == %r, Response == 0x%x\n",
+      __FUNCTION__, Status, Cmd->BufferHead.Response));
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+#pragma pack()
+typedef struct {
+  UINT32                    ClockId;
+  UINT32                    ClockRate;
+} RPI_FW_CLOCK_RATE_TAG;
+
+typedef struct {
+  RPI_FW_BUFFER_HEAD        BufferHead;
+  RPI_FW_TAG_HEAD           TagHead;
+  RPI_FW_CLOCK_RATE_TAG     TagBody;
+  UINT32                    EndTag;
+} RPI_FW_GET_CLOCK_RATE_CMD;
+#pragma pack()
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareGetClockRate (
+  IN  UINT32 ClockId,
+  IN  UINT32 ClockKind,
+  OUT UINT32 *ClockRate
+  )
+{
+  RPI_FW_GET_CLOCK_RATE_CMD   *Cmd;
+  EFI_STATUS                  Status;
+  UINT32                      Result;
+
+  if (!AcquireSpinLockOrFail (&mMailboxLock)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to acquire spinlock\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Cmd = mDmaBuffer;
+  ZeroMem (Cmd, sizeof *Cmd);
+
+  Cmd->BufferHead.BufferSize  = sizeof *Cmd;
+  Cmd->BufferHead.Response    = 0;
+  Cmd->TagHead.TagId          = ClockKind,
+  Cmd->TagHead.TagSize        = sizeof Cmd->TagBody;
+  Cmd->TagHead.TagValueSize   = 0;
+  Cmd->TagBody.ClockId        = ClockId;
+  Cmd->EndTag                 = 0;
+
+  Status = MailboxTransaction (Cmd->BufferHead.BufferSize, RPI_FW_MBOX_CHANNEL, &Result);
+
+  ReleaseSpinLock (&mMailboxLock);
+
+  if (EFI_ERROR (Status) ||
+      Cmd->BufferHead.Response != RPI_FW_RESP_SUCCESS) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: mailbox transaction error: Status == %r, Response == 0x%x\n",
+      __FUNCTION__, Status, Cmd->BufferHead.Response));
+    return EFI_DEVICE_ERROR;
+  }
+
+  *ClockRate = Cmd->TagBody.ClockRate;
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareGetCurrentClockRate (
+  IN  UINT32    ClockId,
+  OUT UINT32    *ClockRate
+  )
+{
+  return RpiFirmwareGetClockRate(ClockId, RPI_FW_GET_CLOCK_RATE, ClockRate);
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareGetMaxClockRate (
+  IN  UINT32    ClockId,
+  OUT UINT32    *ClockRate
+  )
+{
+  return RpiFirmwareGetClockRate(ClockId, RPI_FW_GET_MAX_CLOCK_RATE, ClockRate);
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareGetMinClockRate (
+  IN  UINT32    ClockId,
+  OUT UINT32    *ClockRate
+  )
+{
+  return RpiFirmwareGetClockRate(ClockId, RPI_FW_GET_MIN_CLOCK_RATE, ClockRate);
+}
+
+#pragma pack()
+typedef struct {
+  UINT32 Pin;
+  UINT32 State;
+} RPI_FW_SET_GPIO_TAG;
+
+typedef struct {
+  RPI_FW_BUFFER_HEAD        BufferHead;
+  RPI_FW_TAG_HEAD           TagHead;
+  RPI_FW_SET_GPIO_TAG       TagBody;
+  UINT32                    EndTag;
+} RPI_FW_SET_GPIO_CMD;
+#pragma pack()
+
+STATIC
+VOID
+RpiFirmwareLedSet (
+  IN  BOOLEAN On
+  )
+{
+  RPI_FW_SET_GPIO_CMD *Cmd;
+  EFI_STATUS          Status;
+  UINT32              Result;
+
+  if (!AcquireSpinLockOrFail (&mMailboxLock)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to acquire spinlock\n", __FUNCTION__));
+    return;
+  }
+
+  Cmd = mDmaBuffer;
+  ZeroMem (Cmd, sizeof *Cmd);
+
+  Cmd->BufferHead.BufferSize  = sizeof *Cmd;
+  Cmd->BufferHead.Response    = 0;
+  Cmd->TagHead.TagId          = RPI_FW_SET_GPIO;
+  Cmd->TagHead.TagSize        = sizeof Cmd->TagBody;
+  /*
+   * GPIO_PIN_2 = Activity LED
+   * GPIO_PIN_4 = HDMI Detect (Input / Active Low)
+   * GPIO_PIN_7 = Power LED (Input / Active Low)
+   *
+   * There's also a 128 pin offset.
+   */
+  Cmd->TagBody.Pin = 128 + 2;
+  Cmd->TagBody.State = On;
+  Cmd->TagHead.TagValueSize   = 0;
+  Cmd->EndTag                 = 0;
+
+  Status = MailboxTransaction (Cmd->BufferHead.BufferSize, RPI_FW_MBOX_CHANNEL, &Result);
+
+  ReleaseSpinLock (&mMailboxLock);
+
+  if (EFI_ERROR (Status) ||
+      Cmd->BufferHead.Response != RPI_FW_RESP_SUCCESS) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: mailbox  transaction error: Status == %r, Response == 0x%x\n",
+      __FUNCTION__, Status, Cmd->BufferHead.Response));
+  }
+}
+
+STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL mRpiFirmwareProtocol = {
+  RpiFirmwareSetPowerState,
+  RpiFirmwareGetMacAddress,
+  RpiFirmwareGetCommmandLine,
+  RpiFirmwareGetCurrentClockRate,
+  RpiFirmwareGetMaxClockRate,
+  RpiFirmwareGetMinClockRate,
+  RpiFirmwareSetClockRate,
+  RpiFirmwareAllocFb,
+  RpiFirmwareFreeFb,
+  RpiFirmwareGetFbSize,
+  RpiFirmwareLedSet,
+  RpiFirmwareGetSerial,
+  RpiFirmwareGetModel,
+  RpiFirmwareGetModelRevision,
+  RpiFirmwareGetArmMemory
+};
+
+/**
+  Initialize the state information for the CPU Architectural Protocol
+
+  @param  ImageHandle   of the loaded driver
+  @param  SystemTable   Pointer to the System Table
+
+  @retval EFI_SUCCESS           Protocol registered
+  @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
+  @retval EFI_DEVICE_ERROR      Hardware problems
+
+**/
+EFI_STATUS
+RpiFirmwareDxeInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_STATUS      Status;
+  UINTN           BufferSize;
+
+  //
+  // We only need one of these
+  //
+  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gRaspberryPiFirmwareProtocolGuid);
+
+  InitializeSpinLock (&mMailboxLock);
+
+  Status = DmaAllocateBuffer (EfiBootServicesData, NUM_PAGES, &mDmaBuffer);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to allocate DMA buffer (Status == %r)\n",
+      __FUNCTION__));
+    return Status;
+  }
+
+  BufferSize = EFI_PAGES_TO_SIZE (NUM_PAGES);
+  Status = DmaMap (MapOperationBusMasterCommonBuffer, mDmaBuffer, &BufferSize,
+             &mDmaBufferBusAddress, &mDmaBufferMapping);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to map DMA buffer (Status == %r)\n",
+      __FUNCTION__));
+    goto FreeBuffer;
+  }
+
+  //
+  // The channel index is encoded in the low bits of the bus address,
+  // so make sure these are cleared.
+  //
+  ASSERT (!(mDmaBufferBusAddress & (BCM2836_MBOX_NUM_CHANNELS - 1)));
+
+  Status = gBS->InstallProtocolInterface (&ImageHandle,
+                  &gRaspberryPiFirmwareProtocolGuid, EFI_NATIVE_INTERFACE,
+                  &mRpiFirmwareProtocol);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: failed to install RPI firmware protocol (Status == %r)\n",
+      __FUNCTION__, Status));
+    goto UnmapBuffer;
+  }
+
+  return EFI_SUCCESS;
+
+UnmapBuffer:
+  DmaUnmap (mDmaBufferMapping);
+FreeBuffer:
+  DmaFreeBuffer (NUM_PAGES, mDmaBuffer);
+
+  return Status;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/RpiFirmwareDxe/RpiFirmwareDxe.inf b/Platform/Broadcom/Bcm283x/Drivers/RpiFirmwareDxe/RpiFirmwareDxe.inf
new file mode 100644
index 000000000000..2ab57bbf54f0
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/RpiFirmwareDxe/RpiFirmwareDxe.inf
@@ -0,0 +1,49 @@
+#/** @file
+#
+#  Copyright (c) 2017-2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+#  Copyright (c) 2016, Linaro, Ltd. 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                    = 0x00010019
+  BASE_NAME                      = RpiFirmwareDxe
+  FILE_GUID                      = 6d4628df-49a0-4b67-a325-d5af35c65745
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = RpiFirmwareDxeInitialize
+
+[Sources]
+  RpiFirmwareDxe.c
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  MdePkg/MdePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+
+[LibraryClasses]
+  ArmLib
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  DmaLib
+  IoLib
+  SynchronizationLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiLib
+
+[Protocols]
+  gRaspberryPiFirmwareProtocolGuid    ## PRODUCES
+
+[Depex]
+  TRUE
diff --git a/Platform/Broadcom/Bcm283x/Drivers/SdHostDxe/SdHostDxe.c b/Platform/Broadcom/Bcm283x/Drivers/SdHostDxe/SdHostDxe.c
new file mode 100644
index 000000000000..7dd76098c8a1
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/SdHostDxe/SdHostDxe.c
@@ -0,0 +1,830 @@
+/** @file
+ *
+ *  Copyright (c) 2017, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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 <Library/BaseLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DmaLib.h>
+#include <Library/TimerLib.h>
+
+#include <Protocol/EmbeddedExternalDevice.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/PiMmcHost.h>
+#include <Protocol/RaspberryPiFirmware.h>
+
+#include <IndustryStandard/Bcm2836.h>
+#include <IndustryStandard/RpiFirmware.h>
+#include <IndustryStandard/Bcm2836SdHost.h>
+
+#define SDHOST_BLOCK_BYTE_LENGTH            512
+
+// Driver Timing Parameters
+#define CMD_STALL_AFTER_POLL_US             1
+#define CMD_MIN_POLL_TOTAL_TIME_US          100000 // 100ms
+#define CMD_MAX_POLL_COUNT                  (CMD_MIN_POLL_TOTAL_TIME_US / CMD_STALL_AFTER_POLL_US)
+#define CMD_MAX_RETRY_COUNT                 3
+#define CMD_STALL_AFTER_RETRY_US            20 // 20us
+#define FIFO_MAX_POLL_COUNT                 1000000
+#define STALL_TO_STABILIZE_US               10000 // 10ms
+
+#define IDENT_MODE_SD_CLOCK_FREQ_HZ         400000 // 400KHz
+
+// Macros adopted from MmcDxe internal header
+#define SDHOST_R0_READY_FOR_DATA            BIT8
+#define SDHOST_R0_CURRENTSTATE(Response)    ((Response >> 9) & 0xF)
+
+#define DEBUG_MMCHOST_SD       DEBUG_VERBOSE
+#define DEBUG_MMCHOST_SD_INFO  DEBUG_INFO
+#define DEBUG_MMCHOST_SD_ERROR DEBUG_ERROR
+
+STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL   *mFwProtocol;
+
+// Per Physical Layer Simplified Specs
+#ifndef NDEBUG
+STATIC CONST CHAR8* mStrSdState[] = { "idle", "ready", "ident", "stby",
+                                      "tran", "data", "rcv", "prg", "dis",
+                                      "ina" };
+STATIC CONST CHAR8 *mFsmState[] = { "identmode", "datamode", "readdata",
+                                    "writedata", "readwait", "readcrc",
+                                    "writecrc", "writewait1", "powerdown",
+                                    "powerup", "writestart1", "writestart2",
+                                    "genpulses", "writewait2", "?",
+                                    "startpowdown" };
+#endif /* NDEBUG */
+STATIC UINT32 mLastGoodCmd = MMC_GET_INDX(MMC_CMD0);
+
+STATIC inline BOOLEAN
+IsAppCmd(
+  VOID
+  )
+{
+  return mLastGoodCmd == MMC_CMD55;
+}
+
+STATIC BOOLEAN
+IsBusyCmd(
+  IN  UINT32 MmcCmd
+  )
+{
+  if (IsAppCmd()) {
+    return FALSE;
+  }
+
+  return MmcCmd == MMC_CMD7 || MmcCmd == MMC_CMD12;
+}
+
+STATIC BOOLEAN
+IsWriteCmd(
+  IN  UINT32 MmcCmd
+  )
+{
+  if (IsAppCmd()) {
+    return FALSE;
+  }
+
+  return MmcCmd == MMC_CMD24 || MmcCmd == MMC_CMD25;
+}
+
+STATIC BOOLEAN
+IsReadCmd(
+  IN  UINT32 MmcCmd,
+  IN  UINT32 Argument
+  )
+{
+  if (MmcCmd == MMC_CMD8 && !IsAppCmd()) {
+    if (Argument == CMD8_MMC_ARG) {
+      DEBUG((DEBUG_MMCHOST_SD, "Sending MMC CMD8 variant\n"));
+      return TRUE;
+    } else {
+      ASSERT (Argument == CMD8_SD_ARG);
+      DEBUG((DEBUG_MMCHOST_SD, "Sending SD CMD8 variant\n"));
+      return FALSE;
+    }
+  }
+
+  return
+    (MmcCmd == MMC_CMD6 && !IsAppCmd()) ||
+    (MmcCmd == MMC_CMD17 && !IsAppCmd()) ||
+    (MmcCmd == MMC_CMD18 && !IsAppCmd()) ||
+    (MmcCmd == MMC_CMD13 && IsAppCmd()) ||
+    (MmcCmd == MMC_ACMD22 && IsAppCmd()) ||
+    (MmcCmd == MMC_ACMD51 && IsAppCmd());
+}
+
+STATIC VOID
+SdHostDumpRegisters(
+  VOID
+  )
+{
+  DEBUG((DEBUG_MMCHOST_SD, "SdHost: Registers Dump:\n"));
+  DEBUG((DEBUG_MMCHOST_SD, "  CMD:  0x%8.8X\n", MmioRead32(SDHOST_CMD)));
+  DEBUG((DEBUG_MMCHOST_SD, "  ARG:  0x%8.8X\n", MmioRead32(SDHOST_ARG)));
+  DEBUG((DEBUG_MMCHOST_SD, "  TOUT: 0x%8.8X\n", MmioRead32(SDHOST_TOUT)));
+  DEBUG((DEBUG_MMCHOST_SD, "  CDIV: 0x%8.8X\n", MmioRead32(SDHOST_CDIV)));
+  DEBUG((DEBUG_MMCHOST_SD, "  RSP0: 0x%8.8X\n", MmioRead32(SDHOST_RSP0)));
+  DEBUG((DEBUG_MMCHOST_SD, "  RSP1: 0x%8.8X\n", MmioRead32(SDHOST_RSP1)));
+  DEBUG((DEBUG_MMCHOST_SD, "  RSP2: 0x%8.8X\n", MmioRead32(SDHOST_RSP2)));
+  DEBUG((DEBUG_MMCHOST_SD, "  RSP3: 0x%8.8X\n", MmioRead32(SDHOST_RSP3)));
+  DEBUG((DEBUG_MMCHOST_SD, "  HSTS: 0x%8.8X\n", MmioRead32(SDHOST_HSTS)));
+  DEBUG((DEBUG_MMCHOST_SD, "  VDD:  0x%8.8X\n", MmioRead32(SDHOST_VDD)));
+  DEBUG((DEBUG_MMCHOST_SD, "  EDM:  0x%8.8X\n", MmioRead32(SDHOST_EDM)));
+  DEBUG((DEBUG_MMCHOST_SD, "  HCFG: 0x%8.8X\n", MmioRead32(SDHOST_HCFG)));
+  DEBUG((DEBUG_MMCHOST_SD, "  HBCT: 0x%8.8X\n", MmioRead32(SDHOST_HBCT)));
+  DEBUG((DEBUG_MMCHOST_SD, "  HBLC: 0x%8.8X\n\n", MmioRead32(SDHOST_HBLC)));
+}
+
+#ifndef NDEBUG
+STATIC EFI_STATUS
+SdHostGetSdStatus(
+  UINT32* SdStatus
+  )
+{
+  ASSERT(SdStatus != NULL);
+
+  // On command completion with R1 or R1b response type
+  // the SDCard status will be in RSP0
+  UINT32 Rsp0 = MmioRead32(SDHOST_RSP0);
+  if (Rsp0 != 0xFFFFFFFF) {
+    *SdStatus = Rsp0;
+    return EFI_SUCCESS;
+  }
+
+  return EFI_NO_RESPONSE;
+}
+#endif /* NDEBUG */
+
+STATIC VOID
+SdHostDumpSdCardStatus(
+  VOID
+  )
+{
+#ifndef NDEBUG
+  UINT32 SdCardStatus;
+  EFI_STATUS Status = SdHostGetSdStatus(&SdCardStatus);
+  if (!EFI_ERROR(Status)) {
+    UINT32 CurrState = SDHOST_R0_CURRENTSTATE(SdCardStatus);
+    DEBUG((
+           DEBUG_MMCHOST_SD,
+           "SdHost: SdCardStatus 0x%8.8X: ReadyForData?%d, State[%d]: %a\n",
+           SdCardStatus,
+           ((SdCardStatus & SDHOST_R0_READY_FOR_DATA) ? 1 : 0),
+           CurrState,
+           ((CurrState < (sizeof(mStrSdState) / sizeof(*mStrSdState))) ?
+            mStrSdState[CurrState] : "UNDEF")));
+  }
+#endif /* NDEBUG */
+}
+
+STATIC VOID
+SdHostDumpStatus(
+  VOID
+  )
+{
+  SdHostDumpRegisters();
+
+#ifndef NDEBUG
+  UINT32 Hsts = MmioRead32(SDHOST_HSTS);
+
+  if (Hsts & SDHOST_HSTS_ERROR) {
+    DEBUG((DEBUG_MMCHOST_SD_ERROR,
+           "SdHost: Diagnose HSTS: 0x%8.8X\n", Hsts));
+
+    DEBUG((DEBUG_MMCHOST_SD_ERROR, "SdHost: Last Good CMD = %u\n",
+           MMC_GET_INDX(mLastGoodCmd)));
+    if (Hsts & SDHOST_HSTS_FIFO_ERROR)
+      DEBUG((DEBUG_MMCHOST_SD_ERROR, "  - Fifo Error\n"));
+    if (Hsts & SDHOST_HSTS_CRC7_ERROR)
+      DEBUG((DEBUG_MMCHOST_SD_ERROR, "  - CRC7 Error\n"));
+    if (Hsts & SDHOST_HSTS_CRC16_ERROR)
+      DEBUG((DEBUG_MMCHOST_SD_ERROR, "  - CRC16 Error\n"));
+    if (Hsts & SDHOST_HSTS_CMD_TIME_OUT)
+      DEBUG((DEBUG_MMCHOST_SD_ERROR, "  - CMD Timeout (TOUT %x)\n",
+             MmioRead32(SDHOST_TOUT)));
+    if (Hsts & SDHOST_HSTS_REW_TIME_OUT)
+      DEBUG((DEBUG_MMCHOST_SD_ERROR, "  - Read/Erase/Write Transfer Timeout\n"));
+  }
+
+  UINT32 Edm = MmioRead32(SDHOST_EDM);
+  DEBUG(((Hsts & SDHOST_HSTS_ERROR) ?
+         DEBUG_MMCHOST_SD_ERROR : DEBUG_MMCHOST_SD,
+         "SdHost: Diagnose EDM: 0x%8.8X\n", Edm));
+  DEBUG(((Hsts & SDHOST_HSTS_ERROR) ?
+         DEBUG_MMCHOST_SD_ERROR : DEBUG_MMCHOST_SD,
+         "  - FSM: 0x%x (%a)\n", (Edm & 0xF), mFsmState[Edm & 0xF]));
+  DEBUG(((Hsts & SDHOST_HSTS_ERROR) ?
+         DEBUG_MMCHOST_SD_ERROR : DEBUG_MMCHOST_SD,
+         "  - Fifo Count: %d\n", ((Edm >> 4) & 0x1F)));
+  DEBUG(((Hsts & SDHOST_HSTS_ERROR) ?
+         DEBUG_MMCHOST_SD_ERROR : DEBUG_MMCHOST_SD,
+         "  - Fifo Write Threshold: %d\n",
+         ((Edm >> SDHOST_EDM_WRITE_THRESHOLD_SHIFT) &
+          SDHOST_EDM_THRESHOLD_MASK)));
+  DEBUG(((Hsts & SDHOST_HSTS_ERROR) ?
+         DEBUG_MMCHOST_SD_ERROR : DEBUG_MMCHOST_SD,
+         "  - Fifo Read Threshold: %d\n",
+         ((Edm >> SDHOST_EDM_READ_THRESHOLD_SHIFT) & SDHOST_EDM_THRESHOLD_MASK)));
+#endif
+
+  SdHostDumpSdCardStatus();
+}
+
+STATIC EFI_STATUS
+SdHostSetClockFrequency(
+  IN UINTN TargetSdFreqHz
+  )
+{
+  EFI_STATUS Status;
+  UINT32 CoreClockFreqHz = 0;
+
+  // First figure out the core clock
+  Status = mFwProtocol->GetClockRate(RPI_FW_CLOCK_RATE_CORE, &CoreClockFreqHz);
+  if (EFI_ERROR(Status)) {
+    return Status;
+  }
+
+  ASSERT (CoreClockFreqHz != 0);
+
+  // fSDCLK = fcore_pclk/(ClockDiv+2)
+  UINT32 ClockDiv = (CoreClockFreqHz - (2 * TargetSdFreqHz)) / TargetSdFreqHz;
+  UINT32 ActualSdFreqHz = CoreClockFreqHz / (ClockDiv + 2);
+
+  DEBUG((
+         DEBUG_MMCHOST_SD_INFO,
+         "SdHost: CoreClock=%dHz, CDIV=%d, Requested SdClock=%dHz, Actual SdClock=%dHz\n",
+         CoreClockFreqHz,
+         ClockDiv,
+         TargetSdFreqHz,
+         ActualSdFreqHz));
+
+  MmioWrite32(SDHOST_CDIV, ClockDiv);
+  // Set timeout after 1 second, i.e ActualSdFreqHz SD clock cycles
+  MmioWrite32(SDHOST_TOUT, ActualSdFreqHz);
+
+  gBS->Stall(STALL_TO_STABILIZE_US);
+
+  return Status;
+}
+
+STATIC BOOLEAN
+SdIsCardPresent(
+  IN EFI_MMC_HOST_PROTOCOL *This
+  )
+{
+  return TRUE;
+}
+
+STATIC BOOLEAN
+SdIsReadOnly(
+    IN EFI_MMC_HOST_PROTOCOL *This
+    )
+{
+  return FALSE;
+}
+
+STATIC EFI_STATUS
+SdBuildDevicePath(
+  IN EFI_MMC_HOST_PROTOCOL       *This,
+  IN EFI_DEVICE_PATH_PROTOCOL    **DevicePath
+  )
+{
+  EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;
+  EFI_GUID DevicePathGuid = EFI_CALLER_ID_GUID;
+
+  DEBUG((DEBUG_MMCHOST_SD, "SdHost: SdBuildDevicePath()\n"));
+
+  NewDevicePathNode = CreateDeviceNode(HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof(VENDOR_DEVICE_PATH));
+  CopyGuid(&((VENDOR_DEVICE_PATH*)NewDevicePathNode)->Guid, &DevicePathGuid);
+  *DevicePath = NewDevicePathNode;
+
+  return EFI_SUCCESS;
+}
+
+STATIC EFI_STATUS
+SdSendCommand(
+  IN EFI_MMC_HOST_PROTOCOL    *This,
+  IN MMC_CMD                  MmcCmd,
+  IN UINT32                   Argument
+  )
+{
+  UINT32 Hsts;
+
+  //
+  // Fail fast, CMD5 (CMD_IO_SEND_OP_COND)
+  // is only valid for SDIO cards and thus
+  // expected to always fail.
+  //
+  if (MmcCmd == MMC_CMD5) {
+    DEBUG((
+           DEBUG_MMCHOST_SD,
+           "SdHost: SdSendCommand(CMD%d, Argument: %08x) ignored\n",
+           MMC_GET_INDX(MmcCmd),
+           Argument));
+    return EFI_UNSUPPORTED;
+  }
+
+  if (MmioRead32(SDHOST_CMD) & SDHOST_CMD_NEW_FLAG) {
+    DEBUG((
+           DEBUG_MMCHOST_SD_ERROR,
+           "SdHost: SdSendCommand(): Failed to execute CMD%d, a CMD is already being executed.\n",
+           MMC_GET_INDX(MmcCmd)));
+    SdHostDumpStatus();
+    return EFI_DEVICE_ERROR;
+  }
+
+  // Write command argument
+  MmioWrite32(SDHOST_ARG, Argument);
+
+  UINT32 SdCmd = 0;
+  {
+    // Set response type
+    if (MmcCmd & MMC_CMD_WAIT_RESPONSE) {
+      if (MmcCmd & MMC_CMD_LONG_RESPONSE) {
+        SdCmd |= SDHOST_CMD_RESPONSE_CMD_LONG_RESP;
+      }
+    } else {
+      SdCmd |= SDHOST_CMD_RESPONSE_CMD_NO_RESP;
+    }
+
+    if (IsBusyCmd(MmcCmd)) {
+      SdCmd |= SDHOST_CMD_BUSY_CMD;
+    }
+
+    if (IsReadCmd(MmcCmd, Argument)) {
+      SdCmd |= SDHOST_CMD_READ_CMD;
+    }
+
+    if (IsWriteCmd(MmcCmd)) {
+      SdCmd |= SDHOST_CMD_WRITE_CMD;
+    }
+
+    SdCmd |= MMC_GET_INDX(MmcCmd);
+  }
+
+  if (IsReadCmd(MmcCmd, Argument) || IsWriteCmd(MmcCmd)) {
+    if (IsAppCmd() && MmcCmd == MMC_ACMD22) {
+      MmioWrite32(SDHOST_HBCT, 0x4);
+    } else if (IsAppCmd() && MmcCmd == MMC_ACMD51) {
+      MmioWrite32(SDHOST_HBCT, 0x8);
+    } else if (!IsAppCmd() && MmcCmd == MMC_CMD6) {
+      MmioWrite32(SDHOST_HBCT, 0x40);
+    } else {
+      MmioWrite32(SDHOST_HBCT, SDHOST_BLOCK_BYTE_LENGTH);
+    }
+  }
+
+  DEBUG((
+         DEBUG_MMCHOST_SD,
+         "SdHost: SdSendCommand(CMD%d, Argument: %08x): BUSY=%d, RESP=%d, WRITE=%d, READ=%d\n",
+         MMC_GET_INDX(MmcCmd),
+         Argument,
+         ((SdCmd & SDHOST_CMD_BUSY_CMD) ? 1 : 0),
+         ((SdCmd & (SDHOST_CMD_RESPONSE_CMD_LONG_RESP | SDHOST_CMD_RESPONSE_CMD_NO_RESP)) >> 9),
+         ((SdCmd & SDHOST_CMD_WRITE_CMD) ? 1 : 0),
+         ((SdCmd & SDHOST_CMD_READ_CMD) ? 1 : 0)));
+
+  UINT32 PollCount = 0;
+  UINT32 RetryCount = 0;
+  BOOLEAN IsCmdExecuted = FALSE;
+  EFI_STATUS Status = EFI_SUCCESS;
+
+  // Keep retrying the command until it succeeds.
+  while ((RetryCount < CMD_MAX_RETRY_COUNT) && !IsCmdExecuted) {
+    Status = EFI_SUCCESS;
+
+    // Clear prev cmd status
+    MmioWrite32(SDHOST_HSTS, SDHOST_HSTS_CLEAR);
+
+    if (IsReadCmd(MmcCmd, Argument) || IsWriteCmd(MmcCmd)) {
+      // Flush Fifo if this cmd will start a new transfer in case
+      // there is stale bytes in the Fifo
+      MmioOr32(SDHOST_EDM, SDHOST_EDM_FIFO_CLEAR);
+    }
+
+    if (MmioRead32(SDHOST_CMD) & SDHOST_CMD_NEW_FLAG) {
+      DEBUG((
+             DEBUG_MMCHOST_SD_ERROR,
+             "%a(%u): CMD%d is still being executed after %d trial(s)\n",
+             __FUNCTION__, __LINE__, MMC_GET_INDX(MmcCmd), RetryCount));
+    }
+
+    // Write command and set it to start execution
+    MmioWrite32(SDHOST_CMD, SDHOST_CMD_NEW_FLAG | SdCmd);
+
+    // Poll for the command status until it finishes execution
+    while (PollCount < CMD_MAX_POLL_COUNT) {
+      UINT32 CmdReg = MmioRead32(SDHOST_CMD);
+
+      // Read status of command response
+      if (CmdReg & SDHOST_CMD_FAIL_FLAG) {
+        Status = EFI_DEVICE_ERROR;
+        /*
+         * Must fall-through and wait for the command completion!
+         */
+      }
+
+      // Check if command is completed.
+      if (!(CmdReg & SDHOST_CMD_NEW_FLAG)) {
+        IsCmdExecuted = TRUE;
+        break;
+      }
+
+      ++PollCount;
+      gBS->Stall(CMD_STALL_AFTER_POLL_US);
+    }
+
+    if (!IsCmdExecuted) {
+      ++RetryCount;
+      gBS->Stall(CMD_STALL_AFTER_RETRY_US);
+    }
+  }
+
+  if (RetryCount == CMD_MAX_RETRY_COUNT) {
+    Status = EFI_TIMEOUT;
+  }
+
+  Hsts = MmioRead32(SDHOST_HSTS);
+  if (EFI_ERROR(Status) ||
+      (Hsts & SDHOST_HSTS_ERROR) != 0) {
+    if (MmcCmd == MMC_CMD1 &&
+        (Hsts & SDHOST_HSTS_CRC7_ERROR) != 0) {
+      /*
+       * SdHost seems to have no way to specify
+       * R3 as a transfer type.
+       */
+      IsCmdExecuted = TRUE;
+      Status = EFI_SUCCESS;
+      MmioWrite32(SDHOST_HSTS, SDHOST_HSTS_CLEAR);
+    } else if (MmcCmd == MMC_CMD7 && Argument == 0) {
+      /*
+       * Deselecting the SDCard with CMD7 and RCA=0x0
+       * always timeout on SDHost.
+       */
+      Status = EFI_SUCCESS;
+    } else {
+      DEBUG((
+             DEBUG_MMCHOST_SD_ERROR,
+             "%a(%u): CMD%d execution failed after %d trial(s)\n",
+             __FUNCTION__, __LINE__,
+             MMC_GET_INDX(MmcCmd),
+             RetryCount));
+      SdHostDumpStatus();
+    }
+
+    MmioWrite32(SDHOST_HSTS, SDHOST_HSTS_CLEAR);
+  }
+
+  if (IsCmdExecuted && !EFI_ERROR(Status)) {
+    ASSERT(!(MmioRead32(SDHOST_HSTS) & SDHOST_HSTS_ERROR));
+    mLastGoodCmd = MmcCmd;
+  }
+
+  return Status;
+}
+
+STATIC EFI_STATUS
+SdReceiveResponse(
+  IN EFI_MMC_HOST_PROTOCOL    *This,
+  IN MMC_RESPONSE_TYPE        Type,
+  IN UINT32*                  Buffer
+  )
+{
+  if (Buffer == NULL) {
+    DEBUG((DEBUG_MMCHOST_SD_ERROR,
+           "SdHost: SdReceiveResponse(): Input Buffer is NULL\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((Type == MMC_RESPONSE_TYPE_R1) ||
+      (Type == MMC_RESPONSE_TYPE_R1b) ||
+      (Type == MMC_RESPONSE_TYPE_R3) ||
+      (Type == MMC_RESPONSE_TYPE_R6) ||
+      (Type == MMC_RESPONSE_TYPE_R7)) {
+    Buffer[0] = MmioRead32(SDHOST_RSP0);
+    DEBUG((
+           DEBUG_MMCHOST_SD,
+           "SdHost: SdReceiveResponse(Type: %x), Buffer[0]: %08x\n",
+           Type, Buffer[0]));
+
+  } else if (Type == MMC_RESPONSE_TYPE_R2) {
+    Buffer[0] = MmioRead32(SDHOST_RSP0);
+    Buffer[1] = MmioRead32(SDHOST_RSP1);
+    Buffer[2] = MmioRead32(SDHOST_RSP2);
+    Buffer[3] = MmioRead32(SDHOST_RSP3);
+
+    DEBUG((
+           DEBUG_MMCHOST_SD,
+           "SdHost: SdReceiveResponse(Type: %x), Buffer[0-3]: %08x, %08x, %08x, %08x\n",
+           Type, Buffer[0], Buffer[1], Buffer[2], Buffer[3]));
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC EFI_STATUS
+SdReadBlockData(
+  IN EFI_MMC_HOST_PROTOCOL    *This,
+  IN EFI_LBA                  Lba,
+  IN UINTN                    Length,
+  IN UINT32*                  Buffer
+  )
+{
+  DEBUG((
+         DEBUG_MMCHOST_SD,
+         "SdHost: SdReadBlockData(LBA: 0x%x, Length: 0x%x, Buffer: 0x%x)\n",
+         (UINT32)Lba, Length, Buffer));
+
+  ASSERT(Buffer != NULL);
+  ASSERT(Length % 4 == 0);
+
+  EFI_STATUS Status = EFI_SUCCESS;
+
+  mFwProtocol->SetLed(TRUE);
+  {
+    UINT32 NumWords = Length / 4;
+    UINT32 WordIdx;
+
+    for (WordIdx = 0; WordIdx < NumWords; ++WordIdx) {
+      UINT32 PollCount = 0;
+      while (PollCount < FIFO_MAX_POLL_COUNT) {
+        UINT32 Hsts = MmioRead32(SDHOST_HSTS);
+        if ((Hsts & SDHOST_HSTS_DATA_FLAG) != 0) {
+          MmioWrite32(SDHOST_HSTS, SDHOST_HSTS_DATA_FLAG);
+          Buffer[WordIdx] = MmioRead32(SDHOST_DATA);
+          break;
+        }
+
+        ++PollCount;
+        gBS->Stall(CMD_STALL_AFTER_RETRY_US);
+      }
+
+      if (PollCount == FIFO_MAX_POLL_COUNT) {
+        DEBUG(
+              (DEBUG_MMCHOST_SD_ERROR,
+               "SdHost: SdReadBlockData(): Block Word%d read poll timed-out\n",
+               WordIdx));
+        SdHostDumpStatus();
+        MmioWrite32(SDHOST_HSTS, SDHOST_HSTS_CLEAR);
+        Status = EFI_TIMEOUT;
+        break;
+      }
+    }
+  }
+  mFwProtocol->SetLed(FALSE);
+
+  return Status;
+}
+
+STATIC EFI_STATUS
+SdWriteBlockData(
+  IN EFI_MMC_HOST_PROTOCOL    *This,
+  IN EFI_LBA                  Lba,
+  IN UINTN                    Length,
+  IN UINT32*                  Buffer
+  )
+{
+  DEBUG((
+        DEBUG_MMCHOST_SD,
+        "SdHost: SdWriteBlockData(LBA: 0x%x, Length: 0x%x, Buffer: 0x%x)\n",
+        (UINT32)Lba, Length, Buffer));
+
+  ASSERT(Buffer != NULL);
+  ASSERT(Length % SDHOST_BLOCK_BYTE_LENGTH == 0);
+
+  EFI_STATUS Status = EFI_SUCCESS;
+
+  mFwProtocol->SetLed(TRUE);
+  {
+    UINT32 NumWords = Length / 4;
+    UINT32 WordIdx;
+
+    for (WordIdx = 0; WordIdx < NumWords; ++WordIdx) {
+      UINT32 PollCount = 0;
+      while (PollCount < FIFO_MAX_POLL_COUNT) {
+        if (MmioRead32(SDHOST_HSTS) & SDHOST_HSTS_DATA_FLAG) {
+          MmioWrite32(SDHOST_HSTS, SDHOST_HSTS_DATA_FLAG);
+          MmioWrite32(SDHOST_DATA, Buffer[WordIdx]);
+          break;
+        }
+
+        ++PollCount;
+        gBS->Stall(CMD_STALL_AFTER_RETRY_US);
+      }
+
+      if (PollCount == FIFO_MAX_POLL_COUNT) {
+        DEBUG((
+               DEBUG_MMCHOST_SD_ERROR,
+               "SdHost: SdWriteBlockData(): Block Word%d write poll timed-out\n",
+               WordIdx));
+        SdHostDumpStatus();
+        MmioWrite32(SDHOST_HSTS, SDHOST_HSTS_CLEAR);
+        Status = EFI_TIMEOUT;
+        break;
+      }
+    }
+  }
+  mFwProtocol->SetLed(FALSE);
+
+  return Status;
+}
+
+STATIC EFI_STATUS
+SdSetIos (
+  IN EFI_MMC_HOST_PROTOCOL      *This,
+  IN  UINT32                    BusClockFreq,
+  IN  UINT32                    BusWidth,
+  IN  UINT32                    TimingMode
+  )
+{
+  if (BusWidth != 0) {
+    UINT32 Hcfg = MmioRead32(SDHOST_HCFG);
+
+    DEBUG((DEBUG_MMCHOST_SD_INFO, "Setting BusWidth %u\n", BusWidth));
+    if (BusWidth == 4) {
+      Hcfg |= SDHOST_HCFG_WIDE_EXT_BUS;
+    } else {
+      Hcfg &= ~SDHOST_HCFG_WIDE_EXT_BUS;
+    }
+
+    Hcfg |= SDHOST_HCFG_WIDE_INT_BUS | SDHOST_HCFG_SLOW_CARD;
+    MmioWrite32(SDHOST_HCFG, Hcfg);
+  }
+
+  if (BusClockFreq != 0) {
+    DEBUG((DEBUG_MMCHOST_SD_INFO, "Setting Freq %u Hz\n", BusClockFreq));
+    SdHostSetClockFrequency(BusClockFreq);
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC EFI_STATUS
+SdNotifyState(
+  IN EFI_MMC_HOST_PROTOCOL    *This,
+  IN MMC_STATE                State
+  )
+{
+  DEBUG((DEBUG_MMCHOST_SD, "SdHost: SdNotifyState(State: %d) ", State));
+
+  switch (State) {
+  case MmcHwInitializationState:
+    {
+      DEBUG((DEBUG_MMCHOST_SD, "MmcHwInitializationState\n", State));
+
+      // Turn-off SD Card power
+      MmioWrite32(SDHOST_VDD, 0);
+      {
+        // Reset command and arg
+        MmioWrite32(SDHOST_CMD, 0);
+        MmioWrite32(SDHOST_ARG, 0);
+        // Reset clock divider
+        MmioWrite32(SDHOST_CDIV, 0);
+        // Default timeout
+        MmioWrite32(SDHOST_TOUT, 0xffffffff);
+        // Clear status flags
+        MmioWrite32(SDHOST_HSTS, SDHOST_HSTS_CLEAR);;
+        // Reset controller configs
+        MmioWrite32(SDHOST_HCFG, 0);
+        MmioWrite32(SDHOST_HBCT, 0);
+        MmioWrite32(SDHOST_HBLC, 0);
+
+        gBS->Stall(STALL_TO_STABILIZE_US);
+      }
+      // Turn-on SD Card power
+      MmioWrite32(SDHOST_VDD, 1);
+
+      gBS->Stall(STALL_TO_STABILIZE_US);
+
+      // Write controller configs
+      UINT32 Hcfg = 0;
+      Hcfg |= SDHOST_HCFG_WIDE_INT_BUS;
+      Hcfg |= SDHOST_HCFG_SLOW_CARD; // Use all bits of CDIV in DataMode
+      MmioWrite32(SDHOST_HCFG, Hcfg);
+
+      // Set default clock frequency
+      EFI_STATUS Status = SdHostSetClockFrequency(IDENT_MODE_SD_CLOCK_FREQ_HZ);
+      if (EFI_ERROR(Status)) {
+        DEBUG((
+               DEBUG_MMCHOST_SD_ERROR,
+               "SdHost: SdNotifyState(): Fail to initialize SD clock to %dHz\n",
+               IDENT_MODE_SD_CLOCK_FREQ_HZ));
+        SdHostDumpStatus();
+        return Status;
+      }
+    }
+    break;
+  case MmcIdleState:
+    DEBUG((DEBUG_MMCHOST_SD, "MmcIdleState\n", State));
+    break;
+  case MmcReadyState:
+    DEBUG((DEBUG_MMCHOST_SD, "MmcReadyState\n", State));
+    break;
+  case MmcIdentificationState:
+    DEBUG((DEBUG_MMCHOST_SD, "MmcIdentificationState\n", State));
+    break;
+  case MmcStandByState:
+    DEBUG((DEBUG_MMCHOST_SD, "MmcStandByState\n", State));
+    break;
+  case MmcTransferState:
+    DEBUG((DEBUG_MMCHOST_SD, "MmcTransferState\n", State));
+    break;
+    break;
+  case MmcSendingDataState:
+    DEBUG((DEBUG_MMCHOST_SD, "MmcSendingDataState\n", State));
+    break;
+  case MmcReceiveDataState:
+    DEBUG((DEBUG_MMCHOST_SD, "MmcReceiveDataState\n", State));
+    break;
+  case MmcProgrammingState:
+    DEBUG((DEBUG_MMCHOST_SD, "MmcProgrammingState\n", State));
+    break;
+  case MmcDisconnectState:
+  case MmcInvalidState:
+  default:
+    DEBUG((DEBUG_MMCHOST_SD_ERROR,
+           "SdHost: SdNotifyState(): Invalid State: %d\n", State));
+    ASSERT(0);
+  }
+
+  return EFI_SUCCESS;
+}
+
+BOOLEAN
+SdIsMultiBlock (
+  IN EFI_MMC_HOST_PROTOCOL *This
+  )
+{
+  return TRUE;
+}
+
+EFI_MMC_HOST_PROTOCOL gMmcHost =
+  {
+    MMC_HOST_PROTOCOL_REVISION,
+    SdIsCardPresent,
+    SdIsReadOnly,
+    SdBuildDevicePath,
+    SdNotifyState,
+    SdSendCommand,
+    SdReceiveResponse,
+    SdReadBlockData,
+    SdWriteBlockData,
+    SdSetIos,
+    SdIsMultiBlock
+  };
+
+EFI_STATUS
+SdHostInitialize(
+  IN EFI_HANDLE          ImageHandle,
+  IN EFI_SYSTEM_TABLE    *SystemTable
+  )
+{
+  EFI_STATUS Status;
+  EFI_HANDLE Handle = NULL;
+
+  if (PcdGet32 (PcdSdIsArasan)) {
+    DEBUG((DEBUG_INFO, "SD is not routed to SdHost\n"));
+    return EFI_REQUEST_UNLOAD_IMAGE;
+  }
+
+  Status = gBS->LocateProtocol (&gRaspberryPiFirmwareProtocolGuid, NULL,
+                                (VOID **)&mFwProtocol);
+  ASSERT_EFI_ERROR (Status);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  DEBUG((DEBUG_MMCHOST_SD, "SdHost: Initialize\n"));
+  DEBUG((DEBUG_MMCHOST_SD, "Config:\n"));
+  DEBUG((DEBUG_MMCHOST_SD, " - FIFO_MAX_POLL_COUNT=%d\n", FIFO_MAX_POLL_COUNT));
+  DEBUG((DEBUG_MMCHOST_SD, " - CMD_STALL_AFTER_POLL_US=%dus\n", CMD_STALL_AFTER_POLL_US));
+  DEBUG((DEBUG_MMCHOST_SD, " - CMD_MIN_POLL_TOTAL_TIME_US=%dms\n", CMD_MIN_POLL_TOTAL_TIME_US / 1000));
+  DEBUG((DEBUG_MMCHOST_SD, " - CMD_MAX_POLL_COUNT=%d\n", CMD_MAX_POLL_COUNT));
+  DEBUG((DEBUG_MMCHOST_SD, " - CMD_MAX_RETRY_COUNT=%d\n", CMD_MAX_RETRY_COUNT));
+  DEBUG((DEBUG_MMCHOST_SD, " - CMD_STALL_AFTER_RETRY_US=%dus\n", CMD_STALL_AFTER_RETRY_US));
+
+  Status = gBS->InstallMultipleProtocolInterfaces(
+    &Handle,
+    &gRaspberryPiMmcHostProtocolGuid, &gMmcHost,
+    NULL
+    );
+  ASSERT_EFI_ERROR(Status);
+  return Status;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/SdHostDxe/SdHostDxe.inf b/Platform/Broadcom/Bcm283x/Drivers/SdHostDxe/SdHostDxe.inf
new file mode 100644
index 000000000000..3469b4611cc9
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/SdHostDxe/SdHostDxe.inf
@@ -0,0 +1,54 @@
+#/** @file
+#
+#  Copyright (c) 2017, Andrei Warkentin <andrey.warkentin at gmail.com>
+#  Copyright (c) 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                    = 0x00010005
+  BASE_NAME                      = SdHost
+  FILE_GUID                      = 58ABD787-F64D-4CA2-A034-B9AC2D5AD0CF
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = SdHostInitialize
+
+
+[Sources.common]
+  SdHostDxe.c
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  MdePkg/MdePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+
+[LibraryClasses]
+  PcdLib
+  UefiLib
+  UefiDriverEntryPoint
+  MemoryAllocationLib
+  IoLib
+  DmaLib
+  CacheMaintenanceLib
+
+[Guids]
+
+[Protocols]
+  gRaspberryPiMmcHostProtocolGuid ## PRODUCES
+  gRaspberryPiFirmwareProtocolGuid ## CONSUMES
+
+[Pcd]
+  gRaspberryPiTokenSpaceGuid.PcdSdIsArasan
+
+[Depex]
+  gRaspberryPiFirmwareProtocolGuid AND gRaspberryPiConfigAppliedProtocolGuid
diff --git a/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/FileIo.c b/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/FileIo.c
new file mode 100644
index 000000000000..961de586c78b
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/FileIo.c
@@ -0,0 +1,196 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2007-2009, Intel 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 "VarBlockService.h"
+
+
+EFI_STATUS
+FileWrite (
+  IN EFI_FILE_PROTOCOL *File,
+  IN UINTN Offset,
+  IN UINTN Buffer,
+  IN UINTN Size
+  )
+{
+  EFI_STATUS Status;
+
+  Status = File->SetPosition (File, Offset);
+  ASSERT_EFI_ERROR (Status);
+  if (!EFI_ERROR (Status)) {
+    Status = File->Write (File, &Size, (VOID *) Buffer);
+    ASSERT_EFI_ERROR (Status);
+  }
+  return Status;
+}
+
+
+VOID
+FileClose (
+  IN  EFI_FILE_PROTOCOL *File
+  )
+{
+  File->Flush (File);
+  File->Close (File);
+}
+
+
+EFI_STATUS
+FileOpen (
+  IN  EFI_DEVICE_PATH_PROTOCOL *Device,
+  IN  CHAR16 *MappedFile,
+  OUT EFI_FILE_PROTOCOL **File,
+  IN  UINT64 OpenMode
+  )
+{
+  EFI_HANDLE                        Handle;
+  EFI_FILE_HANDLE                   Root;
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL   *Volume;
+  EFI_STATUS                        Status;
+
+  *File = NULL;
+
+  Status = gBS->LocateDevicePath (
+                  &gEfiSimpleFileSystemProtocolGuid,
+                  &Device,
+                  &Handle
+                  );
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->HandleProtocol (
+                  Handle,
+                  &gEfiSimpleFileSystemProtocolGuid,
+                  (void **) &Volume
+                  );
+  ASSERT_EFI_ERROR (Status);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Open the root directory of the volume
+  //
+  Root = NULL;
+  Status = Volume->OpenVolume (
+                     Volume,
+                     &Root
+                     );
+  ASSERT_EFI_ERROR (Status);
+  ASSERT (Root != NULL);
+
+  //
+  // Open file
+  //
+  Status = Root->Open (
+                   Root,
+                   File,
+                   MappedFile,
+                   OpenMode,
+                   0
+                   );
+  if (EFI_ERROR (Status)) {
+    *File = NULL;
+  }
+
+  //
+  // Close the Root directory
+  //
+  Root->Close (Root);
+  return Status;
+}
+
+
+EFI_STATUS
+CheckStore (
+  IN  EFI_HANDLE SimpleFileSystemHandle,
+  OUT EFI_DEVICE_PATH_PROTOCOL **Device
+  )
+{
+  EFI_STATUS Status;
+  EFI_BLOCK_IO_PROTOCOL *BlkIo;
+  EFI_FILE_PROTOCOL *File;
+
+  *Device = NULL;
+  Status  = gBS->HandleProtocol (
+                   SimpleFileSystemHandle,
+                   &gEfiBlockIoProtocolGuid,
+                   (VOID*)&BlkIo
+                   );
+
+  if (EFI_ERROR (Status)) {
+    goto ErrHandle;
+  }
+  if (!BlkIo->Media->MediaPresent) {
+    DEBUG ((DEBUG_ERROR, "FwhMappedFile: Media not present!\n"));
+    Status = EFI_NO_MEDIA;
+    goto ErrHandle;
+  }
+  if (BlkIo->Media->ReadOnly) {
+    DEBUG ((DEBUG_ERROR, "FwhMappedFile: Media is read-only!\n"));
+    Status = EFI_ACCESS_DENIED;
+    goto ErrHandle;
+  }
+
+  Status = FileOpen (DevicePathFromHandle (SimpleFileSystemHandle),
+                     mFvInstance->MappedFile, &File,
+                     EFI_FILE_MODE_READ);
+  if (EFI_ERROR (Status)) {
+    goto ErrHandle;
+  }
+
+  /* We found it! Maybe do more checks...? */
+
+  FileClose (File);
+  *Device = DuplicateDevicePath (DevicePathFromHandle (SimpleFileSystemHandle));
+
+  ASSERT (*Device != NULL);
+
+ErrHandle:
+  return Status;
+}
+
+
+EFI_STATUS
+CheckStoreExists (
+  IN  EFI_DEVICE_PATH_PROTOCOL *Device
+  )
+{
+  EFI_HANDLE Handle;
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
+  EFI_STATUS Status;
+
+  Status = gBS->LocateDevicePath (
+                  &gEfiSimpleFileSystemProtocolGuid,
+                  &Device,
+                  &Handle
+                  );
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->HandleProtocol (
+                  Handle,
+                  &gEfiSimpleFileSystemProtocolGuid,
+                  (void **) &Volume
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/FvbInfo.c b/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/FvbInfo.c
new file mode 100644
index 000000000000..ddfd746b0eca
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/FvbInfo.c
@@ -0,0 +1,118 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2006-2014, Intel 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 <Pi/PiFirmwareVolume.h>
+#include <Guid/SystemNvDataGuid.h>
+#include <Library/BaseLib.h>
+#include <Library/PcdLib.h>
+
+typedef struct {
+  UINT64                      FvLength;
+  EFI_FIRMWARE_VOLUME_HEADER  FvbInfo;
+  //
+  // EFI_FV_BLOCK_MAP_ENTRY    ExtraBlockMap[n];//n=0
+  //
+  EFI_FV_BLOCK_MAP_ENTRY      End[1];
+} EFI_FVB_MEDIA_INFO;
+
+EFI_FVB_MEDIA_INFO  mPlatformFvbMediaInfo[] = {
+  //
+  // System NvStorage FVB
+  //
+  {
+    FixedPcdGet32 (PcdFlashNvStorageVariableSize) +
+    FixedPcdGet32 (PcdFlashNvStorageFtwWorkingSize) +
+    FixedPcdGet32 (PcdFlashNvStorageFtwSpareSize) +
+    FixedPcdGet32 (PcdNvStorageEventLogSize),
+    {
+      {
+        0,
+      },  // ZeroVector[16]
+      EFI_SYSTEM_NV_DATA_FV_GUID,
+      FixedPcdGet32 (PcdFlashNvStorageVariableSize) +
+      FixedPcdGet32 (PcdFlashNvStorageFtwWorkingSize) +
+      FixedPcdGet32 (PcdFlashNvStorageFtwSpareSize) +
+      FixedPcdGet32 (PcdNvStorageEventLogSize),
+      EFI_FVH_SIGNATURE,
+      EFI_FVB2_MEMORY_MAPPED |
+        EFI_FVB2_READ_ENABLED_CAP |
+        EFI_FVB2_READ_STATUS |
+        EFI_FVB2_WRITE_ENABLED_CAP |
+        EFI_FVB2_WRITE_STATUS |
+        EFI_FVB2_ERASE_POLARITY |
+        EFI_FVB2_ALIGNMENT_16,
+      sizeof (EFI_FIRMWARE_VOLUME_HEADER) + sizeof (EFI_FV_BLOCK_MAP_ENTRY),
+      0,  // CheckSum
+      0,  // ExtHeaderOffset
+      {
+        0,
+      },  // Reserved[1]
+      2,  // Revision
+      {
+        {
+          (FixedPcdGet32 (PcdFlashNvStorageVariableSize) +
+           FixedPcdGet32 (PcdFlashNvStorageFtwWorkingSize) +
+           FixedPcdGet32 (PcdFlashNvStorageFtwSpareSize) +
+           FixedPcdGet32 (PcdNvStorageEventLogSize)) /
+          FixedPcdGet32 (PcdFirmwareBlockSize),
+          FixedPcdGet32 (PcdFirmwareBlockSize),
+        }
+      } // BlockMap[1]
+    },
+    {
+      {
+        0,
+        0
+      }
+    }  // End[1]
+  }
+};
+
+
+EFI_STATUS
+GetFvbInfo (
+  IN  UINT64 FvLength,
+  OUT EFI_FIRMWARE_VOLUME_HEADER **FvbInfo
+  )
+{
+  STATIC BOOLEAN Checksummed = FALSE;
+  UINTN Index;
+
+  if (!Checksummed) {
+    for (Index = 0;
+         Index < sizeof (mPlatformFvbMediaInfo) / sizeof (EFI_FVB_MEDIA_INFO);
+         Index += 1) {
+      UINT16 Checksum;
+      mPlatformFvbMediaInfo[Index].FvbInfo.Checksum = 0;
+      Checksum = CalculateCheckSum16 (
+                   (UINT16*) &mPlatformFvbMediaInfo[Index].FvbInfo,
+                   mPlatformFvbMediaInfo[Index].FvbInfo.HeaderLength
+                   );
+      mPlatformFvbMediaInfo[Index].FvbInfo.Checksum = Checksum;
+    }
+    Checksummed = TRUE;
+  }
+
+  for (Index = 0;
+       Index < sizeof (mPlatformFvbMediaInfo) / sizeof (EFI_FVB_MEDIA_INFO);
+       Index += 1) {
+    if (mPlatformFvbMediaInfo[Index].FvLength == FvLength) {
+      *FvbInfo = &mPlatformFvbMediaInfo[Index].FvbInfo;
+      return EFI_SUCCESS;
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockService.c b/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockService.c
new file mode 100644
index 000000000000..1cea51936ba2
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockService.c
@@ -0,0 +1,984 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2006 - 2014, Intel 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 <Protocol/DevicePath.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include "VarBlockService.h"
+
+#define EFI_FVB2_STATUS \
+          (EFI_FVB2_READ_STATUS | EFI_FVB2_WRITE_STATUS | EFI_FVB2_LOCK_STATUS)
+
+EFI_FW_VOL_INSTANCE *mFvInstance;
+
+FV_MEMMAP_DEVICE_PATH mFvMemmapDevicePathTemplate = {
+  {
+    {
+      HARDWARE_DEVICE_PATH,
+      HW_MEMMAP_DP,
+      {
+        (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
+        (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)
+      }
+    },
+    EfiMemoryMappedIO,
+    (EFI_PHYSICAL_ADDRESS) 0,
+    (EFI_PHYSICAL_ADDRESS) 0,
+  },
+  {
+    END_DEVICE_PATH_TYPE,
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    {
+      END_DEVICE_PATH_LENGTH,
+      0
+    }
+  }
+};
+
+FV_PIWG_DEVICE_PATH mFvPIWGDevicePathTemplate = {
+  {
+    {
+      MEDIA_DEVICE_PATH,
+      MEDIA_PIWG_FW_VOL_DP,
+      {
+        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH)),
+        (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH) >> 8)
+      }
+    },
+    { 0 }
+  },
+  {
+    END_DEVICE_PATH_TYPE,
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    {
+      END_DEVICE_PATH_LENGTH,
+      0
+    }
+  }
+};
+
+EFI_FW_VOL_BLOCK_DEVICE mFvbDeviceTemplate = {
+  NULL,
+  {
+    FvbProtocolGetAttributes,
+    FvbProtocolSetAttributes,
+    FvbProtocolGetPhysicalAddress,
+    FvbProtocolGetBlockSize,
+    FvbProtocolRead,
+    FvbProtocolWrite,
+    FvbProtocolEraseBlocks,
+    NULL
+  }
+};
+
+
+EFI_STATUS
+VarStoreWrite (
+  IN     UINTN Address,
+  IN OUT UINTN *NumBytes,
+  IN     UINT8 *Buffer
+  )
+{
+  CopyMem ((VOID *) Address, Buffer, *NumBytes);
+  mFvInstance->Dirty = TRUE;
+
+  return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+VarStoreErase (
+  IN UINTN Address,
+  IN UINTN LbaLength
+  )
+{
+  SetMem ((VOID *)Address, LbaLength, 0xff);
+  mFvInstance->Dirty = TRUE;
+
+  return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+FvbGetVolumeAttributes (
+  OUT EFI_FVB_ATTRIBUTES_2 *Attributes
+  )
+{
+  *Attributes = mFvInstance->VolumeHeader->Attributes;
+  return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+FvbGetLbaAddress (
+  IN  EFI_LBA Lba,
+  OUT UINTN *LbaAddress,
+  OUT UINTN *LbaLength,
+  OUT UINTN *NumOfBlocks
+  )
+/*++
+
+  Routine Description:
+    Retrieves the starting address of an LBA in an FV
+
+  Arguments:
+    Lba                   - The logical block address
+    LbaAddress            - On output, contains the physical starting address
+                            of the Lba
+    LbaLength             - On output, contains the length of the block
+    NumOfBlocks           - A pointer to a caller allocated UINTN in which the
+                            number of consecutive blocks starting with Lba is
+                            returned. All blocks in this range have a size of
+                            BlockSize
+
+  Returns:
+    EFI_SUCCESS
+    EFI_INVALID_PARAMETER
+
+--*/
+{
+  UINT32 NumBlocks;
+  UINT32 BlockLength;
+  UINTN Offset;
+  EFI_LBA StartLba;
+  EFI_LBA NextLba;
+  EFI_FV_BLOCK_MAP_ENTRY *BlockMap;
+
+  StartLba  = 0;
+  Offset    = 0;
+  BlockMap  = &(mFvInstance->VolumeHeader->BlockMap[0]);
+
+  //
+  // Parse the blockmap of the FV to find which map entry the Lba belongs to.
+  //
+  while (TRUE) {
+    NumBlocks   = BlockMap->NumBlocks;
+    BlockLength = BlockMap->Length;
+
+    if (NumBlocks == 0 || BlockLength == 0) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    NextLba = StartLba + NumBlocks;
+
+    //
+    // The map entry found.
+    //
+    if (Lba >= StartLba && Lba < NextLba) {
+      Offset = Offset + (UINTN) MultU64x32 ((Lba - StartLba), BlockLength);
+      if (LbaAddress != NULL) {
+        *LbaAddress = mFvInstance->FvBase + Offset;
+      }
+
+      if (LbaLength != NULL) {
+        *LbaLength = BlockLength;
+      }
+
+      if (NumOfBlocks != NULL) {
+        *NumOfBlocks = (UINTN) (NextLba - Lba);
+      }
+
+      return EFI_SUCCESS;
+    }
+
+    StartLba  = NextLba;
+    Offset    = Offset + NumBlocks * BlockLength;
+    BlockMap++;
+  }
+}
+
+
+EFI_STATUS
+FvbEraseBlock (
+  IN EFI_LBA Lba
+  )
+/*++
+
+Routine Description:
+  Erases and initializes a firmware volume block
+
+Arguments:
+  Lba                   - The logical block index to be erased
+
+Returns:
+  EFI_SUCCESS           - The erase request was successfully completed
+  EFI_ACCESS_DENIED     - The firmware volume is in the WriteDisabled state
+  EFI_DEVICE_ERROR      - The block device is not functioning correctly and
+                          could not be written. Firmware device may have been
+                          partially erased
+  EFI_INVALID_PARAMETER
+
+--*/
+{
+  EFI_FVB_ATTRIBUTES_2 Attributes;
+  UINTN                LbaAddress;
+  UINTN                LbaLength;
+  EFI_STATUS           Status;
+
+  //
+  // Check if the FV is write enabled
+  //
+  FvbGetVolumeAttributes (&Attributes);
+
+  if ((Attributes & EFI_FVB2_WRITE_STATUS) == 0) {
+    return EFI_ACCESS_DENIED;
+  }
+  //
+  // Get the starting address of the block for erase. For debug reasons,
+  // LbaWriteAddress may not be the same as LbaAddress.
+  //
+  Status = FvbGetLbaAddress (Lba, &LbaAddress, &LbaLength, NULL);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return VarStoreErase (
+          LbaAddress,
+          LbaLength
+          );
+}
+
+
+EFI_STATUS
+FvbSetVolumeAttributes (
+  IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes
+  )
+/*++
+
+  Routine Description:
+    Modifies the current settings of the firmware volume according to the
+    input parameter, and returns the new setting of the volume
+
+  Arguments:
+    Attributes            - On input, it is a pointer to EFI_FVB_ATTRIBUTES_2
+                            containing the desired firmware volume settings.
+                            On successful return, it contains the new setting.
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+    EFI_ACCESS_DENIED     - The volume setting is locked and cannot be modified
+    EFI_INVALID_PARAMETER
+
+--*/
+{
+  EFI_FVB_ATTRIBUTES_2 OldAttributes;
+  EFI_FVB_ATTRIBUTES_2 *AttribPtr;
+  UINT32 Capabilities;
+  UINT32 OldStatus;
+  UINT32 NewStatus;
+  EFI_FVB_ATTRIBUTES_2 UnchangedAttributes;
+
+  AttribPtr =
+    (EFI_FVB_ATTRIBUTES_2 *) &(mFvInstance->VolumeHeader->Attributes);
+  OldAttributes = *AttribPtr;
+  Capabilities = OldAttributes & (EFI_FVB2_READ_DISABLED_CAP | \
+                                  EFI_FVB2_READ_ENABLED_CAP |    \
+                                  EFI_FVB2_WRITE_DISABLED_CAP |  \
+                                  EFI_FVB2_WRITE_ENABLED_CAP |   \
+                                  EFI_FVB2_LOCK_CAP              \
+                                  );
+  OldStatus = OldAttributes & EFI_FVB2_STATUS;
+  NewStatus = *Attributes & EFI_FVB2_STATUS;
+
+  UnchangedAttributes = EFI_FVB2_READ_DISABLED_CAP  | \
+                        EFI_FVB2_READ_ENABLED_CAP   | \
+                        EFI_FVB2_WRITE_DISABLED_CAP | \
+                        EFI_FVB2_WRITE_ENABLED_CAP  | \
+                        EFI_FVB2_LOCK_CAP           | \
+                        EFI_FVB2_STICKY_WRITE       | \
+                        EFI_FVB2_MEMORY_MAPPED      | \
+                        EFI_FVB2_ERASE_POLARITY     | \
+                        EFI_FVB2_READ_LOCK_CAP      | \
+                        EFI_FVB2_WRITE_LOCK_CAP     | \
+                        EFI_FVB2_ALIGNMENT;
+
+  //
+  // Some attributes of FV is read only can *not* be set.
+  //
+  if ((OldAttributes & UnchangedAttributes) ^
+      (*Attributes & UnchangedAttributes)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // If firmware volume is locked, no status bit can be updated.
+  //
+  if (OldAttributes & EFI_FVB2_LOCK_STATUS) {
+    if (OldStatus ^ NewStatus) {
+      return EFI_ACCESS_DENIED;
+    }
+  }
+
+  //
+  // Test read disable.
+  //
+  if ((Capabilities & EFI_FVB2_READ_DISABLED_CAP) == 0) {
+    if ((NewStatus & EFI_FVB2_READ_STATUS) == 0) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  //
+  // Test read enable.
+  //
+  if ((Capabilities & EFI_FVB2_READ_ENABLED_CAP) == 0) {
+    if (NewStatus & EFI_FVB2_READ_STATUS) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  //
+  // Test write disable.
+  //
+  if ((Capabilities & EFI_FVB2_WRITE_DISABLED_CAP) == 0) {
+    if ((NewStatus & EFI_FVB2_WRITE_STATUS) == 0) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  //
+  // Test write enable.
+  //
+  if ((Capabilities & EFI_FVB2_WRITE_ENABLED_CAP) == 0) {
+    if (NewStatus & EFI_FVB2_WRITE_STATUS) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  //
+  // Test lock.
+  //
+  if ((Capabilities & EFI_FVB2_LOCK_CAP) == 0) {
+    if (NewStatus & EFI_FVB2_LOCK_STATUS) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+
+  *AttribPtr  = (*AttribPtr) & (0xFFFFFFFF & (~EFI_FVB2_STATUS));
+  *AttribPtr  = (*AttribPtr) | NewStatus;
+  *Attributes = *AttribPtr;
+
+  return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetPhysicalAddress (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+  OUT EFI_PHYSICAL_ADDRESS *Address
+  )
+{
+  *Address = mFvInstance->FvBase;
+  return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetBlockSize (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+  IN CONST EFI_LBA Lba,
+  OUT UINTN *BlockSize,
+  OUT UINTN *NumOfBlocks
+  )
+/*++
+
+  Routine Description:
+    Retrieve the size of a logical block
+
+  Arguments:
+    This                  - Calling context
+    Lba                   - Indicates which block to return the size for.
+    BlockSize             - A pointer to a caller allocated UINTN in which
+                            the size of the block is returned
+    NumOfBlocks           - a pointer to a caller allocated UINTN in which the
+                            number of consecutive blocks starting with Lba is
+                            returned. All blocks in this range have a size of
+                            BlockSize
+
+  Returns:
+    EFI_SUCCESS           - The firmware volume was read successfully and
+                            contents are in Buffer
+
+--*/
+{
+  return FvbGetLbaAddress (
+          Lba,
+          NULL,
+          BlockSize,
+          NumOfBlocks
+          );
+}
+
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+  OUT EFI_FVB_ATTRIBUTES_2 *Attributes
+  )
+/*++
+
+  Routine Description:
+      Retrieves Volume attributes.  No polarity translations are done.
+
+  Arguments:
+      This                - Calling context
+      Attributes          - output buffer which contains attributes
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+
+--*/
+{
+  return FvbGetVolumeAttributes (Attributes);
+}
+
+
+EFI_STATUS
+EFIAPI
+FvbProtocolSetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+  IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes
+  )
+/*++
+
+  Routine Description:
+    Sets Volume attributes. No polarity translations are done.
+
+  Arguments:
+    This                  - Calling context
+    Attributes            - output buffer which contains attributes
+
+  Returns:
+    EFI_SUCCESS           - Successfully returns
+
+--*/
+{
+  return FvbSetVolumeAttributes (Attributes);
+}
+
+
+EFI_STATUS
+EFIAPI
+FvbProtocolEraseBlocks (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL*This,
+  ...
+  )
+/*++
+
+  Routine Description:
+
+    The EraseBlock() function erases one or more blocks as denoted by the
+    variable argument list. The entire parameter list of blocks must be
+    verified prior to erasing any blocks.  If a block is requested that does
+    not exist within the associated firmware volume (it has a larger index than
+    the last block of the firmware volume), the EraseBlock() function must
+    return EFI_INVALID_PARAMETER without modifying the contents of the firmware
+    volume.
+
+  Arguments:
+    This                  - Calling context
+    ...                   - Starting LBA followed by Number of Lba to erase.
+                            a -1 to terminate the list.
+
+  Returns:
+    EFI_SUCCESS           - The erase request was successfully completed
+    EFI_ACCESS_DENIED     - The firmware volume is in the WriteDisabled state
+    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
+                            could not be written. Firmware device may have been
+                            partially erased
+
+--*/
+{
+  UINTN NumOfBlocks;
+  VA_LIST args;
+  EFI_LBA StartingLba;
+  UINTN NumOfLba;
+  EFI_STATUS Status;
+
+  NumOfBlocks = mFvInstance->NumOfBlocks;
+  VA_START (args, This);
+
+  do {
+    StartingLba = VA_ARG (args, EFI_LBA);
+    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
+      break;
+    }
+
+    NumOfLba = VA_ARG (args, UINTN);
+
+    if ((NumOfLba == 0) || ((StartingLba + NumOfLba) > NumOfBlocks)) {
+      VA_END (args);
+      return EFI_INVALID_PARAMETER;
+    }
+  } while (1);
+
+  VA_END (args);
+
+  VA_START (args, This);
+  do {
+    StartingLba = VA_ARG (args, EFI_LBA);
+    if (StartingLba == EFI_LBA_LIST_TERMINATOR) {
+      break;
+    }
+
+    NumOfLba = VA_ARG (args, UINTN);
+
+    while (NumOfLba > 0) {
+      Status = FvbEraseBlock (StartingLba);
+      if (EFI_ERROR (Status)) {
+        VA_END (args);
+        return Status;
+      }
+
+      StartingLba++;
+      NumOfLba--;
+    }
+
+  } while (1);
+
+  VA_END (args);
+
+  return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+EFIAPI
+FvbProtocolWrite (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+  IN       EFI_LBA Lba,
+  IN       UINTN Offset,
+  IN OUT   UINTN *NumBytes,
+  IN       UINT8 *Buffer
+  )
+/*++
+
+  Routine Description:
+
+    Writes data beginning at Lba:Offset from FV. The write terminates either
+    when *NumBytes of data have been written, or when a block boundary is
+    reached.  *NumBytes is updated to reflect the actual number of bytes
+    written. The write opertion does not include erase. This routine will
+    attempt to write only the specified bytes. If the writes do not stick,
+    it will return an error.
+
+  Arguments:
+    This                  - Calling context
+    Lba                   - Block in which to begin write
+    Offset                - Offset in the block at which to begin write
+    NumBytes              - On input, indicates the requested write size. On
+                            output, indicates the actual number of bytes
+                            written
+    Buffer                - Buffer containing source data for the write.
+
+  Returns:
+    EFI_SUCCESS           - The firmware volume was written successfully
+    EFI_BAD_BUFFER_SIZE   - Write attempted across a LBA boundary. On output,
+                            NumBytes contains the total number of bytes
+                            actually written
+    EFI_ACCESS_DENIED     - The firmware volume is in the WriteDisabled state
+    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
+                            could not be written
+    EFI_INVALID_PARAMETER - NumBytes or Buffer are NULL
+
+--*/
+{
+  EFI_FVB_ATTRIBUTES_2 Attributes;
+  UINTN LbaAddress;
+  UINTN LbaLength;
+  EFI_STATUS Status;
+  EFI_STATUS ReturnStatus;
+
+  //
+  // Check for invalid conditions.
+  //
+  if ((NumBytes == NULL) || (Buffer == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (*NumBytes == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = FvbGetLbaAddress (Lba, &LbaAddress, &LbaLength, NULL);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Check if the FV is write enabled.
+  //
+  FvbGetVolumeAttributes (&Attributes);
+
+  if ((Attributes & EFI_FVB2_WRITE_STATUS) == 0) {
+    return EFI_ACCESS_DENIED;
+  }
+
+  //
+  // Perform boundary checks and adjust NumBytes.
+  //
+  if (Offset > LbaLength) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (LbaLength < (*NumBytes + Offset)) {
+    *NumBytes = (UINT32) (LbaLength - Offset);
+    Status    = EFI_BAD_BUFFER_SIZE;
+  }
+
+  ReturnStatus = VarStoreWrite (
+                  LbaAddress + Offset,
+                  NumBytes,
+                  Buffer
+                  );
+  if (EFI_ERROR (ReturnStatus)) {
+    return ReturnStatus;
+  }
+
+  return Status;
+}
+
+
+EFI_STATUS
+EFIAPI
+FvbProtocolRead (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+  IN CONST EFI_LBA Lba,
+  IN CONST UINTN Offset,
+  IN OUT UINTN *NumBytes,
+  IN UINT8 *Buffer
+  )
+/*++
+
+  Routine Description:
+
+    Reads data beginning at Lba:Offset from FV. The Read terminates either
+    when *NumBytes of data have been read, or when a block boundary is
+    reached.  *NumBytes is updated to reflect the actual number of bytes
+    written. The write opertion does not include erase. This routine will
+    attempt to write only the specified bytes. If the writes do not stick,
+    it will return an error.
+
+  Arguments:
+    This                  - Calling context
+    Lba                   - Block in which to begin Read
+    Offset                - Offset in the block at which to begin Read
+    NumBytes              - On input, indicates the requested write size. On
+                            output, indicates the actual number of bytes Read
+    Buffer                - Buffer containing source data for the Read.
+
+  Returns:
+    EFI_SUCCESS           - The firmware volume was read successfully and
+                            contents are in Buffer
+    EFI_BAD_BUFFER_SIZE   - Read attempted across a LBA boundary. On output,
+                            NumBytes contains the total number of bytes
+                            returned in Buffer
+    EFI_ACCESS_DENIED     - The firmware volume is in the ReadDisabled state
+    EFI_DEVICE_ERROR      - The block device is not functioning correctly and
+                            could not be read
+    EFI_INVALID_PARAMETER - NumBytes or Buffer are NULL
+
+--*/
+{
+  EFI_FVB_ATTRIBUTES_2 Attributes;
+  UINTN LbaAddress;
+  UINTN LbaLength;
+  EFI_STATUS Status;
+
+  //
+  // Check for invalid conditions.
+  //
+  if ((NumBytes == NULL) || (Buffer == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (*NumBytes == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = FvbGetLbaAddress (Lba, &LbaAddress, &LbaLength, NULL);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Check if the FV is read enabled.
+  //
+  FvbGetVolumeAttributes (&Attributes);
+
+  if ((Attributes & EFI_FVB2_READ_STATUS) == 0) {
+    return EFI_ACCESS_DENIED;
+  }
+
+  //
+  // Perform boundary checks and adjust NumBytes.
+  //
+  if (Offset > LbaLength) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (LbaLength < (*NumBytes + Offset)) {
+    *NumBytes = (UINT32) (LbaLength - Offset);
+    Status    = EFI_BAD_BUFFER_SIZE;
+  }
+
+  CopyMem (Buffer, (VOID *) (LbaAddress + Offset), (UINTN) *NumBytes);
+
+  return Status;
+}
+
+
+EFI_STATUS
+ValidateFvHeader (
+  IN EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader
+  )
+/*++
+
+  Routine Description:
+    Check the integrity of firmware volume header
+
+  Arguments:
+    FwVolHeader           - A pointer to a firmware volume header
+
+  Returns:
+    EFI_SUCCESS           - The firmware volume is consistent
+    EFI_NOT_FOUND         - The firmware volume has corrupted. So it is not an
+                            FV
+
+--*/
+{
+  UINT16 Checksum;
+
+  //
+  // Verify the header revision, header signature, length
+  // Length of FvBlock cannot be 2**64-1
+  // HeaderLength cannot be an odd number.
+  //
+  if ((FwVolHeader->Revision != EFI_FVH_REVISION) ||
+      (FwVolHeader->Signature != EFI_FVH_SIGNATURE) ||
+      (FwVolHeader->FvLength == ((UINTN) -1)) ||
+      ((FwVolHeader->HeaderLength & 0x01) != 0)
+      ) {
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // Verify the header checksum.
+  //
+
+  Checksum = CalculateSum16 ((UINT16 *) FwVolHeader,
+               FwVolHeader->HeaderLength);
+  if (Checksum != 0) {
+    UINT16 Expected;
+
+    Expected =
+      (UINT16) (((UINTN) FwVolHeader->Checksum + 0x10000 - Checksum) & 0xffff);
+
+    DEBUG ((DEBUG_INFO, "FV@%p Checksum is 0x%x, expected 0x%x\n",
+            FwVolHeader, FwVolHeader->Checksum, Expected));
+    return EFI_NOT_FOUND;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+EFIAPI
+FvbInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+/*++
+
+  Routine Description:
+    This function does common initialization for FVB services
+
+  Arguments:
+
+  Returns:
+
+--*/
+{
+  EFI_STATUS Status;
+  UINT32 BufferSize;
+  EFI_FV_BLOCK_MAP_ENTRY *PtrBlockMapEntry;
+  EFI_FW_VOL_BLOCK_DEVICE *FvbDevice;
+  UINT32 MaxLbaSize;
+  EFI_PHYSICAL_ADDRESS BaseAddress;
+  UINTN Length;
+  UINTN NumOfBlocks;
+  RETURN_STATUS PcdStatus;
+  UINTN StartOffset;
+
+  BaseAddress = PcdGet32 (PcdNvStorageVariableBase);
+  Length = (FixedPcdGet32 (PcdFlashNvStorageVariableSize) +
+     FixedPcdGet32 (PcdFlashNvStorageFtwWorkingSize) +
+     FixedPcdGet32 (PcdFlashNvStorageFtwSpareSize) +
+     FixedPcdGet32 (PcdNvStorageEventLogSize));
+  StartOffset = BaseAddress - FixedPcdGet64 (PcdFdBaseAddress);
+
+  BufferSize = sizeof (EFI_FW_VOL_INSTANCE);
+
+  mFvInstance = AllocateRuntimeZeroPool (BufferSize);
+  if (mFvInstance == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  mFvInstance->FvBase = (UINTN) BaseAddress;
+  mFvInstance->FvLength = (UINTN) Length;
+  mFvInstance->Offset = StartOffset;
+  /*
+   * Should I parse config.txt instead and find the real name?
+   */
+  mFvInstance->MappedFile = L"RPI_EFI.FD";
+
+  Status = ValidateFvHeader (mFvInstance->VolumeHeader);
+  if (!EFI_ERROR (Status)) {
+    if (mFvInstance->VolumeHeader->FvLength != Length ||
+        mFvInstance->VolumeHeader->BlockMap[0].Length !=
+        PcdGet32 (PcdFirmwareBlockSize)) {
+      Status = EFI_VOLUME_CORRUPTED;
+    }
+  }
+  if (EFI_ERROR (Status)) {
+    EFI_FIRMWARE_VOLUME_HEADER *GoodFwVolHeader;
+    UINTN WriteLength;
+
+    DEBUG ((DEBUG_INFO,
+      "Variable FV header is not valid. It will be reinitialized.\n"));
+
+    //
+    // Get FvbInfo
+    //
+    Status = GetFvbInfo (Length, &GoodFwVolHeader);
+    ASSERT_EFI_ERROR (Status);
+
+    //
+    // Erase all the blocks
+    //
+    Status = VarStoreErase ((UINTN) mFvInstance->FvBase,
+                            mFvInstance->FvLength);
+    ASSERT_EFI_ERROR (Status);
+    //
+    // Write good FV header
+    //
+    WriteLength = GoodFwVolHeader->HeaderLength;
+    Status = VarStoreWrite((UINTN) mFvInstance->FvBase,
+                          &WriteLength,
+                          (UINT8*) GoodFwVolHeader);
+    ASSERT_EFI_ERROR (Status);
+    ASSERT (WriteLength == GoodFwVolHeader->HeaderLength);
+
+    Status = ValidateFvHeader (mFvInstance->VolumeHeader);
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  MaxLbaSize = 0;
+  NumOfBlocks = 0;
+  for (PtrBlockMapEntry = mFvInstance->VolumeHeader->BlockMap;
+       PtrBlockMapEntry->NumBlocks != 0;
+       PtrBlockMapEntry++) {
+    //
+    // Get the maximum size of a block.
+    //
+    if (MaxLbaSize < PtrBlockMapEntry->Length) {
+      MaxLbaSize = PtrBlockMapEntry->Length;
+    }
+
+    NumOfBlocks = NumOfBlocks + PtrBlockMapEntry->NumBlocks;
+  }
+
+  //
+  // The total number of blocks in the FV.
+  //
+  mFvInstance->NumOfBlocks = NumOfBlocks;
+
+  //
+  // Add a FVB Protocol Instance
+  //
+  FvbDevice = AllocateRuntimePool (sizeof (EFI_FW_VOL_BLOCK_DEVICE));
+  ASSERT (FvbDevice != NULL);
+  CopyMem (FvbDevice, &mFvbDeviceTemplate, sizeof (EFI_FW_VOL_BLOCK_DEVICE));
+
+  //
+  // Set up the devicepath
+  //
+  if (mFvInstance->VolumeHeader->ExtHeaderOffset == 0) {
+    FV_MEMMAP_DEVICE_PATH *FvMemmapDevicePath;
+
+    //
+    // FV does not contains extension header, then produce MEMMAP_DEVICE_PATH
+    //
+    FvMemmapDevicePath = AllocateCopyPool (sizeof (FV_MEMMAP_DEVICE_PATH),
+                           &mFvMemmapDevicePathTemplate);
+    FvMemmapDevicePath->MemMapDevPath.StartingAddress = mFvInstance->FvBase;
+    FvMemmapDevicePath->MemMapDevPath.EndingAddress = mFvInstance->FvBase +
+      mFvInstance->FvLength - 1;
+    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)FvMemmapDevicePath;
+  } else {
+    FV_PIWG_DEVICE_PATH *FvPiwgDevicePath;
+
+    FvPiwgDevicePath = AllocateCopyPool (sizeof (FV_PIWG_DEVICE_PATH),
+                         &mFvPIWGDevicePathTemplate);
+    CopyGuid (
+      &FvPiwgDevicePath->FvDevPath.FvName,
+      (GUID *)(UINTN)(mFvInstance->FvBase +
+                      mFvInstance->VolumeHeader->ExtHeaderOffset)
+      );
+    FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)FvPiwgDevicePath;
+  }
+
+  //
+  // Module type specific hook.
+  //
+  InstallProtocolInterfaces (FvbDevice);
+
+  //
+  // Set several PCD values to point to flash.
+  //
+  PcdStatus = PcdSet64S (
+    PcdFlashNvStorageVariableBase64,
+    (UINTN) PcdGet32 (PcdNvStorageVariableBase)
+    );
+  ASSERT_RETURN_ERROR (PcdStatus);
+  PcdStatus = PcdSet32S (
+    PcdFlashNvStorageFtwWorkingBase,
+    PcdGet32 (PcdNvStorageFtwWorkingBase)
+    );
+  ASSERT_RETURN_ERROR (PcdStatus);
+  PcdStatus = PcdSet32S (
+    PcdFlashNvStorageFtwSpareBase,
+    PcdGet32 (PcdNvStorageFtwSpareBase)
+    );
+  ASSERT_RETURN_ERROR (PcdStatus);
+
+  InstallFSNotifyHandler ();
+  InstallDumpVarEventHandlers ();
+  InstallVirtualAddressChangeHandler ();
+
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockService.h b/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockService.h
new file mode 100644
index 000000000000..3596c4ac55b9
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockService.h
@@ -0,0 +1,217 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2007-2009, Intel 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 _FW_BLOCK_SERVICE_H
+#define _FW_BLOCK_SERVICE_H
+
+#include <Guid/EventGroup.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+#include <Protocol/SimpleFileSystem.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/LoadedImage.h>
+
+typedef struct {
+  union {
+    UINTN                      FvBase;
+    EFI_FIRMWARE_VOLUME_HEADER *VolumeHeader;
+  };
+  UINTN                      FvLength;
+  UINTN                      Offset;
+  UINTN                      NumOfBlocks;
+  EFI_DEVICE_PATH_PROTOCOL   *Device;
+  CHAR16                     *MappedFile;
+  BOOLEAN                    Dirty;
+} EFI_FW_VOL_INSTANCE;
+
+extern EFI_FW_VOL_INSTANCE *mFvInstance;
+
+typedef struct {
+  MEDIA_FW_VOL_DEVICE_PATH  FvDevPath;
+  EFI_DEVICE_PATH_PROTOCOL  EndDevPath;
+} FV_PIWG_DEVICE_PATH;
+
+typedef struct {
+  MEMMAP_DEVICE_PATH          MemMapDevPath;
+  EFI_DEVICE_PATH_PROTOCOL    EndDevPath;
+} FV_MEMMAP_DEVICE_PATH;
+
+typedef struct {
+  EFI_DEVICE_PATH_PROTOCOL            *DevicePath;
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  FwVolBlockInstance;
+} EFI_FW_VOL_BLOCK_DEVICE;
+
+EFI_STATUS
+GetFvbInfo (
+  IN  UINT64                            FvLength,
+  OUT EFI_FIRMWARE_VOLUME_HEADER        **FvbInfo
+  );
+
+EFI_STATUS
+FvbSetVolumeAttributes (
+  IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes
+  );
+
+EFI_STATUS
+FvbGetVolumeAttributes (
+  OUT EFI_FVB_ATTRIBUTES_2 *Attributes
+  );
+
+EFI_STATUS
+FvbGetPhysicalAddress (
+  OUT EFI_PHYSICAL_ADDRESS *Address
+  );
+
+EFI_STATUS
+EFIAPI
+FvbInitialize (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  );
+
+
+VOID
+EFIAPI
+FvbClassAddressChangeEvent (
+  IN EFI_EVENT        Event,
+  IN VOID             *Context
+  );
+
+EFI_STATUS
+FvbGetLbaAddress (
+  IN  EFI_LBA Lba,
+  OUT UINTN   *LbaAddress,
+  OUT UINTN   *LbaLength,
+  OUT UINTN   *NumOfBlocks
+  );
+
+//
+// Protocol APIs
+//
+EFI_STATUS
+EFIAPI
+FvbProtocolGetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL           *This,
+  OUT EFI_FVB_ATTRIBUTES_2                              *Attributes
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolSetAttributes (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL           *This,
+  IN OUT EFI_FVB_ATTRIBUTES_2                           *Attributes
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetPhysicalAddress (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL           *This,
+  OUT EFI_PHYSICAL_ADDRESS                        *Address
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolGetBlockSize (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL           *This,
+  IN CONST EFI_LBA                                     Lba,
+  OUT UINTN                                       *BlockSize,
+  OUT UINTN                                       *NumOfBlocks
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolRead (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL           *This,
+  IN CONST EFI_LBA                                      Lba,
+  IN CONST UINTN                                        Offset,
+  IN OUT UINTN                                    *NumBytes,
+  IN UINT8                                        *Buffer
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolWrite (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL           *This,
+  IN       EFI_LBA                                      Lba,
+  IN       UINTN                                        Offset,
+  IN OUT   UINTN                                        *NumBytes,
+  IN       UINT8                                        *Buffer
+  );
+
+EFI_STATUS
+EFIAPI
+FvbProtocolEraseBlocks (
+  IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    *This,
+  ...
+  );
+
+VOID
+InstallProtocolInterfaces (
+  IN EFI_FW_VOL_BLOCK_DEVICE *FvbDevice
+  );
+
+VOID
+InstallVirtualAddressChangeHandler (
+  VOID
+  );
+
+VOID
+InstallFSNotifyHandler (
+  VOID
+  );
+
+VOID
+InstallDumpVarEventHandlers (
+  VOID
+);
+
+EFI_STATUS
+FileWrite (
+  IN EFI_FILE_PROTOCOL *File,
+  IN UINTN             Offset,
+  IN UINTN             Buffer,
+  IN UINTN             Size
+  );
+
+EFI_STATUS
+CheckStore (
+  IN  EFI_HANDLE SimpleFileSystemHandle,
+  OUT EFI_DEVICE_PATH_PROTOCOL **Device
+  );
+
+EFI_STATUS
+CheckStoreExists (
+  IN  EFI_DEVICE_PATH_PROTOCOL *Device
+  );
+
+EFI_STATUS
+FileOpen (
+  IN  EFI_DEVICE_PATH_PROTOCOL *Device,
+  IN  CHAR16 *MappedFile,
+  OUT EFI_FILE_PROTOCOL **File,
+  IN  UINT64 OpenMode
+  );
+
+VOID
+FileClose (
+  IN  EFI_FILE_PROTOCOL *File
+  );
+
+#endif
diff --git a/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockServiceDxe.c b/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockServiceDxe.c
new file mode 100644
index 000000000000..8c4db3dcfd59
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockServiceDxe.c
@@ -0,0 +1,334 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (C) 2015, Red Hat, Inc.
+ *  Copyright (c) 2006-2014, Intel 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 "VarBlockService.h"
+
+VOID *mSFSRegistration;
+
+
+VOID
+InstallProtocolInterfaces (
+  IN EFI_FW_VOL_BLOCK_DEVICE *FvbDevice
+  )
+{
+  EFI_STATUS Status;
+  EFI_HANDLE FwbHandle;
+  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *OldFwbInterface;
+
+  //
+  // Find a handle with a matching device path that has supports FW Block
+  // protocol.
+  //
+  Status = gBS->LocateDevicePath (&gEfiFirmwareVolumeBlockProtocolGuid,
+                  &FvbDevice->DevicePath, &FwbHandle);
+  if (EFI_ERROR (Status)) {
+    //
+    // LocateDevicePath fails so install a new interface and device path.
+    //
+    FwbHandle = NULL;
+    Status = gBS->InstallMultipleProtocolInterfaces (
+                    &FwbHandle,
+                    &gEfiFirmwareVolumeBlockProtocolGuid,
+                    &FvbDevice->FwVolBlockInstance,
+                    &gEfiDevicePathProtocolGuid,
+                    FvbDevice->DevicePath,
+                    NULL
+                    );
+    ASSERT_EFI_ERROR (Status);
+  } else if (IsDevicePathEnd (FvbDevice->DevicePath)) {
+    //
+    // Device already exists, so reinstall the FVB protocol
+    //
+    Status = gBS->HandleProtocol (
+                    FwbHandle,
+                    &gEfiFirmwareVolumeBlockProtocolGuid,
+                    (VOID**)&OldFwbInterface
+                    );
+    ASSERT_EFI_ERROR (Status);
+
+    Status = gBS->ReinstallProtocolInterface (
+                    FwbHandle,
+                    &gEfiFirmwareVolumeBlockProtocolGuid,
+                    OldFwbInterface,
+                    &FvbDevice->FwVolBlockInstance
+                    );
+    ASSERT_EFI_ERROR (Status);
+  } else {
+    //
+    // There was a FVB protocol on an End Device Path node
+    //
+    ASSERT (FALSE);
+  }
+}
+
+
+STATIC
+VOID
+EFIAPI
+FvbVirtualAddressChangeEvent (
+  IN EFI_EVENT Event,
+  IN VOID *Context
+  )
+/*++
+
+  Routine Description:
+
+    Fixup internal data so that EFI can be called in virtual mode.
+
+  Arguments:
+
+    (Standard EFI notify event - EFI_EVENT_NOTIFY)
+
+  Returns:
+
+    None
+
+--*/
+{
+  EfiConvertPointer (0x0, (VOID **) &mFvInstance->FvBase);
+  EfiConvertPointer (0x0, (VOID **) &mFvInstance->VolumeHeader);
+  EfiConvertPointer (0x0, (VOID **) &mFvInstance);
+}
+
+
+VOID
+InstallVirtualAddressChangeHandler (
+  VOID
+  )
+{
+  EFI_STATUS Status;
+  EFI_EVENT VirtualAddressChangeEvent;
+
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  FvbVirtualAddressChangeEvent,
+                  NULL,
+                  &gEfiEventVirtualAddressChangeGuid,
+                  &VirtualAddressChangeEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+}
+
+
+STATIC
+EFI_STATUS
+DoDump(
+  IN EFI_DEVICE_PATH_PROTOCOL *Device
+  )
+{
+  EFI_STATUS Status;
+  EFI_FILE_PROTOCOL *File;
+
+  Status = FileOpen (Device,
+                     mFvInstance->MappedFile,
+                     &File,
+                     EFI_FILE_MODE_WRITE |
+                     EFI_FILE_MODE_READ);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = FileWrite (File,
+                      mFvInstance->Offset,
+                      mFvInstance->FvBase,
+                      mFvInstance->FvLength);
+  FileClose (File);
+  return Status;
+}
+
+
+STATIC
+VOID
+EFIAPI
+DumpVars(
+  IN EFI_EVENT Event,
+  IN VOID *Context
+  )
+{
+  EFI_STATUS Status;
+
+  if (mFvInstance->Device == NULL) {
+    DEBUG((DEBUG_INFO, "Variable store not found?\n"));
+    return;
+  }
+
+  if (!mFvInstance->Dirty) {
+    DEBUG((DEBUG_INFO, "Variables not dirty, not dumping!\n"));
+    return;
+  }
+
+  Status = DoDump (mFvInstance->Device);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "Couldn't dump '%s'\n",
+           mFvInstance->MappedFile));
+    ASSERT_EFI_ERROR(Status);
+    return;
+  }
+
+  DEBUG((DEBUG_INFO, "Variables dumped!\n"));
+  mFvInstance->Dirty = FALSE;
+}
+
+
+VOID
+ReadyToBootHandler (
+  IN EFI_EVENT Event,
+  IN VOID *Context
+  )
+{
+  EFI_STATUS Status;
+  EFI_EVENT ImageInstallEvent;
+  VOID *ImageRegistration;
+
+  Status = gBS->CreateEvent (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_CALLBACK,
+                  DumpVars,
+                  NULL,
+                  &ImageInstallEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->RegisterProtocolNotify (
+                  &gEfiLoadedImageProtocolGuid,
+                  ImageInstallEvent,
+                  &ImageRegistration
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  DumpVars(NULL, NULL);
+  Status = gBS->CloseEvent(Event);
+  ASSERT_EFI_ERROR (Status);
+}
+
+
+VOID
+InstallDumpVarEventHandlers (
+  VOID
+  )
+{
+  EFI_STATUS Status;
+  EFI_EVENT ResetEvent;
+  EFI_EVENT ReadyToBootEvent;
+
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_CALLBACK,
+                  DumpVars,
+                  NULL,
+                  &gRaspberryPiEventResetGuid,
+                  &ResetEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_CALLBACK,
+                  ReadyToBootHandler,
+                  NULL,
+                  &gEfiEventReadyToBootGuid,
+                  &ReadyToBootEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+}
+
+
+VOID
+EFIAPI
+OnSimpleFileSystemInstall (
+  IN EFI_EVENT Event,
+  IN VOID *Context
+  )
+{
+  EFI_STATUS Status;
+  UINTN HandleSize;
+  EFI_HANDLE Handle;
+  EFI_DEVICE_PATH_PROTOCOL *Device;
+
+  if ((mFvInstance->Device != NULL) &&
+      !EFI_ERROR (CheckStoreExists (mFvInstance->Device))
+      ) {
+    //
+    // We've already found the variable store before,
+    // and that device is not removed from the ssystem.
+    //
+    return;
+  }
+
+  while (TRUE) {
+    HandleSize = sizeof (EFI_HANDLE);
+    Status = gBS->LocateHandle (
+                    ByRegisterNotify,
+                    NULL,
+                    mSFSRegistration,
+                    &HandleSize,
+                    &Handle
+                    );
+    if (Status == EFI_NOT_FOUND) {
+      break;
+    }
+
+    ASSERT_EFI_ERROR (Status);
+
+    Status = CheckStore (Handle, &Device);
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    Status = DoDump (Device);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "Couldn't update '%s'\n",
+             mFvInstance->MappedFile));
+      ASSERT_EFI_ERROR(Status);
+      continue;
+    }
+
+    if (mFvInstance->Device != NULL) {
+      gBS->FreePool (mFvInstance->Device);
+    }
+
+    DEBUG((DEBUG_INFO, "Found variable store!\n"));
+    mFvInstance->Device = Device;
+    break;
+  }
+}
+
+
+VOID
+InstallFSNotifyHandler (
+  VOID
+  )
+{
+  EFI_STATUS Status;
+  EFI_EVENT Event;
+
+  Status = gBS->CreateEvent (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_CALLBACK,
+                  OnSimpleFileSystemInstall,
+                  NULL,
+                  &Event
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->RegisterProtocolNotify (
+                  &gEfiSimpleFileSystemProtocolGuid,
+                  Event,
+                  &mSFSRegistration
+                  );
+  ASSERT_EFI_ERROR (Status);
+}
diff --git a/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockServiceDxe.inf b/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockServiceDxe.inf
new file mode 100644
index 000000000000..95ec0f8a64e9
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockServiceDxe.inf
@@ -0,0 +1,93 @@
+#/** @file
+#
+#  Support for the FS-backed "flash" device.
+#  The trick is to keep it inside the RPI firmware file itself...
+#
+#  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+#  Copyright (c) 2006-2013, Intel 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                    = 0x00010005
+  BASE_NAME                      = VarBlockServiceDxe
+  FILE_GUID                      = 733cbac2-b23f-4b92-bc8e-fb01ce5907b7
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = FvbInitialize
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+#  VALID_ARCHITECTURES           = AARCH64
+#
+
+[Sources]
+  FvbInfo.c
+  VarBlockService.c
+  VarBlockServiceDxe.c
+  FileIo.c
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  DevicePathLib
+  DxeServicesTableLib
+  MemoryAllocationLib
+  PcdLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiRuntimeLib
+
+[Guids]
+  gEfiEventVirtualAddressChangeGuid
+  gRaspberryPiEventResetGuid
+  gEfiEventReadyToBootGuid
+
+[Protocols]
+  gEfiSimpleFileSystemProtocolGuid
+  gEfiLoadedImageProtocolGuid
+  gEfiBlockIoProtocolGuid
+  gEfiFirmwareVolumeBlockProtocolGuid           # PROTOCOL SOMETIMES_PRODUCED
+  gEfiDevicePathProtocolGuid                    # PROTOCOL SOMETIMES_PRODUCED
+
+[FixedPcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
+  gRaspberryPiTokenSpaceGuid.PcdNvStorageVariableBase
+  gRaspberryPiTokenSpaceGuid.PcdNvStorageFtwWorkingBase
+  gRaspberryPiTokenSpaceGuid.PcdNvStorageFtwSpareBase
+  gRaspberryPiTokenSpaceGuid.PcdNvStorageEventLogSize
+  gRaspberryPiTokenSpaceGuid.PcdFirmwareBlockSize
+  gArmTokenSpaceGuid.PcdFdBaseAddress
+  gArmTokenSpaceGuid.PcdFdSize
+
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase
+  gRaspberryPiTokenSpaceGuid.PcdNvStorageEventLogBase
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64
+
+[FeaturePcd]
+
+[Depex]
+  TRUE
diff --git a/Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836.h b/Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836.h
new file mode 100644
index 000000000000..bd30324ff073
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836.h
@@ -0,0 +1,70 @@
+/** @file
+ *
+ *  Copyright (c) 2017, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2016, Linaro Limited. All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice, this
+ *  list of conditions and the following disclaimer.
+ *
+ *  Redistributions in binary form must reproduce the above copyright notice,
+ *  this list of conditions and the following disclaimer in the documentation
+ *  and/or other materials provided with the distribution.
+ *
+ *  Neither the name of ARM nor the names of its contributors may be used
+ *  to endorse or promote products derived from this software without specific
+ *  prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ *
+ **/
+
+/*
+ * Both "core" and SoC perpherals (1M each).
+ */
+#define BCM2836_SOC_REGISTERS                               0x3f000000
+#define BCM2836_SOC_REGISTER_LENGTH                         0x02000000
+
+/* Synopsis DW2/DWC USB 2.0 OTG. */
+#define BCM2836_USB_DW2_BASE_ADDRESS                        0x3f980000
+
+/*
+ * Offset between the CPU's view and the VC's view of system memory.
+ */
+#define BCM2836_DMA_DEVICE_OFFSET                           0xc0000000
+
+/* watchdog constants */
+#define BCM2836_WDOG_BASE_ADDRESS                           0x3f100000
+#define BCM2836_WDOG_PASSWORD                               0x5a000000
+#define BCM2836_WDOG_RSTC_OFFSET                            0x0000001c
+#define BCM2836_WDOG_WDOG_OFFSET                            0x00000024
+#define BCM2836_WDOG_RSTC_WRCFG_MASK                        0x00000030
+#define BCM2836_WDOG_RSTC_WRCFG_FULL_RESET                  0x00000020
+
+/* mailbox interface constants */
+#define BCM2836_MBOX_BASE_ADDRESS                           0x3f00b880
+#define BCM2836_MBOX_READ_OFFSET                            0x00000000
+#define BCM2836_MBOX_STATUS_OFFSET                          0x00000018
+#define BCM2836_MBOX_CONFIG_OFFSET                          0x0000001c
+#define BCM2836_MBOX_WRITE_OFFSET                           0x00000020
+
+#define BCM2836_MBOX_STATUS_FULL                            0x1f
+#define BCM2836_MBOX_STATUS_EMPTY                           0x1e
+
+#define BCM2836_MBOX_NUM_CHANNELS                           16
+
+/* interrupt controller constants */
+#define BCM2836_INTC_TIMER_CONTROL_OFFSET                   0x00000040
+#define BCM2836_INTC_TIMER_PENDING_OFFSET                   0x00000060
diff --git a/Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836MmcHs.h b/Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836MmcHs.h
new file mode 100644
index 000000000000..2bd1742fcdff
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836MmcHs.h
@@ -0,0 +1,199 @@
+/** @file
+ *
+ *  Copyright (c) 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 __BCM2836SDIO_H__
+#define __BCM2836SDIO_H__
+
+//MMC/SD/SDIO1 register definitions.
+#define MMCHS1BASE        0x3F300000
+
+#define MMCHS_BLK         (MMCHS1BASE + 0x4)
+#define BLEN_512BYTES     (0x200UL << 0)
+
+#define MMCHS_ARG         (MMCHS1BASE + 0x8)
+
+#define MMCHS_CMD         (MMCHS1BASE + 0xC)
+#define BCE_ENABLE        BIT1
+#define DDIR_READ         BIT4
+#define DDIR_WRITE        (0x0UL << 4)
+#define MSBS_SGLEBLK      (0x0UL << 5)
+#define MSBS_MULTBLK      BIT5
+#define RSP_TYPE_MASK     (0x3UL << 16)
+#define RSP_TYPE_136BITS  BIT16
+#define RSP_TYPE_48BITS   (0x2UL << 16)
+#define RSP_TYPE_48BUSY   (0x3UL << 16)
+#define CCCE_ENABLE       BIT19
+#define CICE_ENABLE       BIT20
+#define DP_ENABLE         BIT21
+
+#define CMD_TYPE_NORMAL      0
+#define CMD_TYPE_ABORT       3
+#define TYPE(CMD_TYPE)       (((CMD_TYPE) & 0x3) << 22)
+#define _INDX(CMD_INDX)      ((CMD_INDX & 0x3F) << 24)
+#define MMC_CMD_NUM(CMD)     (((CMD) >> 24) & 0x3F)
+#define INDX(CMD_INDX)       (TYPE(CMD_TYPE_NORMAL) | _INDX(CMD_INDX))
+#define INDX_ABORT(CMD_INDX) (TYPE(CMD_TYPE_ABORT) | _INDX(CMD_INDX))
+
+#define MMCHS_RSP10       (MMCHS1BASE + 0x10)
+#define MMCHS_RSP32       (MMCHS1BASE + 0x14)
+#define MMCHS_RSP54       (MMCHS1BASE + 0x18)
+#define MMCHS_RSP76       (MMCHS1BASE + 0x1C)
+#define MMCHS_DATA        (MMCHS1BASE + 0x20)
+
+#define MMCHS_PRES_STATE  (MMCHS1BASE + 0x24)
+#define CMDI_MASK         BIT0
+#define CMDI_ALLOWED      (0x0UL << 0)
+#define CMDI_NOT_ALLOWED  BIT0
+#define DATI_MASK         BIT1
+#define DATI_ALLOWED      (0x0UL << 1)
+#define DATI_NOT_ALLOWED  BIT1
+#define WRITE_PROTECT_OFF BIT19
+
+#define MMCHS_HCTL        (MMCHS1BASE + 0x28)
+#define DTW_1_BIT         (0x0UL << 1)
+#define DTW_4_BIT         BIT1
+#define SDBP_MASK         BIT8
+#define SDBP_OFF          (0x0UL << 8)
+#define SDBP_ON           BIT8
+#define SDVS_1_8_V        (0x5UL << 9)
+#define SDVS_3_0_V        (0x6UL << 9)
+#define IWE               BIT24
+
+#define MMCHS_SYSCTL      (MMCHS1BASE + 0x2C)
+#define ICE               BIT0
+#define ICS_MASK          BIT1
+#define ICS               BIT1
+#define CEN               BIT2
+#define CLKD_MASK         (0x3FFUL << 6)
+#define CLKD_80KHZ        (0x258UL) //(96*1000/80)/2
+#define CLKD_400KHZ       (0xF0UL)
+#define CLKD_12500KHZ     (0x200UL)
+#define DTO_MASK          (0xFUL << 16)
+#define DTO_VAL           (0xEUL << 16)
+#define SRA               BIT24
+#define SRC_MASK          BIT25
+#define SRC               BIT25
+#define SRD               BIT26
+
+#define MMCHS_INT_STAT    (MMCHS1BASE + 0x30)
+#define CC                BIT0
+#define TC                BIT1
+#define BWR               BIT4
+#define BRR               BIT5
+#define CARD_INS          BIT6
+#define ERRI              BIT15
+#define CTO               BIT16
+#define DTO               BIT20
+#define DCRC              BIT21
+#define DEB               BIT22
+
+#define MMCHS_IE          (MMCHS1BASE + 0x34)
+#define CC_EN             BIT0
+#define TC_EN             BIT1
+#define BWR_EN            BIT4
+#define BRR_EN            BIT5
+#define CTO_EN            BIT16
+#define CCRC_EN           BIT17
+#define CEB_EN            BIT18
+#define CIE_EN            BIT19
+#define DTO_EN            BIT20
+#define DCRC_EN           BIT21
+#define DEB_EN            BIT22
+#define CERR_EN           BIT28
+#define BADA_EN           BIT29
+#define ALL_EN            0xFFFFFFFF
+
+#define MMCHS_ISE         (MMCHS1BASE + 0x38)
+#define CC_SIGEN          BIT0
+#define TC_SIGEN          BIT1
+#define BWR_SIGEN         BIT4
+#define BRR_SIGEN         BIT5
+#define CTO_SIGEN         BIT16
+#define CCRC_SIGEN        BIT17
+#define CEB_SIGEN         BIT18
+#define CIE_SIGEN         BIT19
+#define DTO_SIGEN         BIT20
+#define DCRC_SIGEN        BIT21
+#define DEB_SIGEN         BIT22
+#define CERR_SIGEN        BIT28
+#define BADA_SIGEN        BIT29
+
+#define MMCHS_AC12        (MMCHS1BASE + 0x3C)
+
+#define MMCHS_CAPA        (MMCHS1BASE + 0x40)
+#define VS30              BIT25
+#define VS18              BIT26
+
+#define MMCHS_CUR_CAPA    (MMCHS1BASE + 0x48)
+#define MMCHS_REV         (MMCHS1BASE + 0xFC)
+
+#define BLOCK_COUNT_SHIFT 16
+#define RCA_SHIFT         16
+
+#define CMD_R1            (RSP_TYPE_48BITS | CCCE_ENABLE | CICE_ENABLE)
+#define CMD_R1B           (RSP_TYPE_48BUSY | CCCE_ENABLE | CICE_ENABLE)
+#define CMD_R2            (RSP_TYPE_136BITS | CCCE_ENABLE)
+#define CMD_R3            (RSP_TYPE_48BITS)
+#define CMD_R6            (RSP_TYPE_48BITS | CCCE_ENABLE | CICE_ENABLE)
+#define CMD_R7            (RSP_TYPE_48BITS | CCCE_ENABLE | CICE_ENABLE)
+
+#define CMD_R1_ADTC       (CMD_R1 | DP_ENABLE)
+#define CMD_R1_ADTC_READ  (CMD_R1_ADTC | DDIR_READ)
+#define CMD_R1_ADTC_WRITE (CMD_R1_ADTC | DDIR_WRITE)
+
+#define CMD0              (INDX(0)) // Go idle
+#define CMD1              (INDX(1) | CMD_R3) // MMC: Send Op Cond
+#define CMD2              (INDX(2) | CMD_R2) // Send CID
+#define CMD3              (INDX(3) | CMD_R6) // Set Relative Addr
+#define CMD4              (INDX(4)) // Set DSR
+#define CMD5              (INDX(5) | CMD_R1B) // SDIO: Sleep/Awake
+#define CMD6              (INDX(6) | CMD_R1_ADTC_READ) // Switch
+#define CMD7              (INDX(7) | CMD_R1B) // Select/Deselect
+#define CMD8_SD           (INDX(8) | CMD_R7) // Send If Cond
+#define CMD8_SD_ARG       (0x0UL << 12 | BIT8 | 0xCEUL << 0)
+#define CMD8_MMC          (INDX(8) | CMD_R1_ADTC_READ) // Send Ext Csd
+#define CMD8_MMC_ARG      (0)
+#define CMD9              (INDX(9) | CMD_R2) // Send CSD
+#define CMD10             (INDX(10) | CMD_R2) // Send CID
+#define CMD11             (INDX(11) | CMD_R1) // Voltage Switch
+#define CMD12             (INDX_ABORT(12) | CMD_R1B) // Stop Transmission
+#define CMD13             (INDX(13) | CMD_R1) // Send Status
+#define CMD15             (INDX(15)) // Go inactive state
+#define CMD16             (INDX(16) | CMD_R1) // Set Blocklen
+#define CMD17             (INDX(17) | CMD_R1_ADTC_READ) // Read Single Block
+#define CMD18             (INDX(18) | CMD_R1_ADTC_READ | MSBS_MULTBLK) // Read Multiple Blocks
+#define CMD19             (INDX(19) | CMD_R1_ADTC_READ) // SD: Send Tuning Block (64 bytes)
+#define CMD20             (INDX(20) | CMD_R1B) // SD: Speed Class Control
+#define CMD23             (INDX(23) | CMD_R1) // Set Block Count for CMD18 and CMD25
+#define CMD24             (INDX(24) | CMD_R1_ADTC_WRITE) // Write Block
+#define CMD25             (INDX(25) | CMD_R1_ADTC_WRITE | MSBS_MULTBLK) // Write Multiple Blocks
+#define CMD55             (INDX(55) | CMD_R1) // App Cmd
+
+#define ACMD6             (INDX(6) | CMD_R1) // Set Bus Width
+#define ACMD22            (INDX(22) | CMD_R1_ADTC_READ) // SEND_NUM_WR_BLOCKS
+#define ACMD41            (INDX(41) | CMD_R3) // Send Op Cond
+#define ACMD51            (INDX(51) | CMD_R1_ADTC_READ) // Send SCR
+
+// User-friendly command names
+#define CMD_IO_SEND_OP_COND      CMD5
+#define CMD_SEND_CSD             CMD9  // CSD: Card-Specific Data
+#define CMD_STOP_TRANSMISSION    CMD12
+#define CMD_SEND_STATUS          CMD13
+#define CMD_READ_SINGLE_BLOCK    CMD17
+#define CMD_READ_MULTIPLE_BLOCK  CMD18
+#define CMD_SET_BLOCK_COUNT      CMD23
+#define CMD_WRITE_SINGLE_BLOCK   CMD24
+#define CMD_WRITE_MULTIPLE_BLOCK CMD25
+
+#endif //__BCM2836SDIO_H__
diff --git a/Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836SdHost.h b/Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836SdHost.h
new file mode 100644
index 000000000000..fb3d6b5ccad3
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836SdHost.h
@@ -0,0 +1,92 @@
+/** @file
+ *
+ *  Copyright (c) 2017, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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 __BCM2836SDHOST_H__
+#define __BCM2836SDHOST_H__
+
+#define SDHOST_BASE_ADDRESS         (BCM2836_SOC_REGISTERS + 0x00202000)
+#define SDHOST_REG(X)               (SDHOST_BASE_ADDRESS + (X))
+#define SDHOST_CMD                  SDHOST_REG(0x0)
+#define SDHOST_ARG                  SDHOST_REG(0x4)
+#define SDHOST_TOUT                 SDHOST_REG(0x8)
+#define SDHOST_CDIV                 SDHOST_REG(0xC)
+#define SDHOST_RSP0                 SDHOST_REG(0x10) // [31:0]
+#define SDHOST_RSP1                 SDHOST_REG(0x14) // [63:32]
+#define SDHOST_RSP2                 SDHOST_REG(0x18) // [95:64]
+#define SDHOST_RSP3                 SDHOST_REG(0x1C) // [127:96]
+#define SDHOST_HSTS                 SDHOST_REG(0x20)
+#define SDHOST_VDD                  SDHOST_REG(0x30)
+#define SDHOST_EDM                  SDHOST_REG(0x34)
+#define SDHOST_HCFG                 SDHOST_REG(0x38)
+#define SDHOST_HBCT                 SDHOST_REG(0x3C)
+#define SDHOST_DATA                 SDHOST_REG(0x40)
+#define SDHOST_HBLC                 SDHOST_REG(0x50)
+
+//
+// CMD
+//
+#define SDHOST_CMD_READ_CMD                     BIT6
+#define SDHOST_CMD_WRITE_CMD                    BIT7
+#define SDHOST_CMD_RESPONSE_CMD_LONG_RESP       BIT9
+#define SDHOST_CMD_RESPONSE_CMD_NO_RESP         BIT10
+#define SDHOST_CMD_BUSY_CMD                     BIT11
+#define SDHOST_CMD_FAIL_FLAG                    BIT14
+#define SDHOST_CMD_NEW_FLAG                     BIT15
+
+//
+// VDD
+//
+#define SDHOST_VDD_POWER_ON         BIT0
+
+//
+// HSTS
+//
+#define SDHOST_HSTS_CLEAR           0x7F8
+#define SDHOST_HSTS_BLOCK_IRPT      BIT9
+#define SDHOST_HSTS_REW_TIME_OUT    BIT7
+#define SDHOST_HSTS_CMD_TIME_OUT    BIT6
+#define SDHOST_HSTS_CRC16_ERROR     BIT5
+#define SDHOST_HSTS_CRC7_ERROR      BIT4
+#define SDHOST_HSTS_FIFO_ERROR      BIT3
+#define SDHOST_HSTS_DATA_FLAG       BIT0
+
+#define SDHOST_HSTS_TIMOUT_ERROR    (SDHOST_HSTS_CMD_TIME_OUT | SDHOST_HSTS_REW_TIME_OUT)
+#define SDHOST_HSTS_TRANSFER_ERROR  (SDHOST_HSTS_FIFO_ERROR | SDHOST_HSTS_CRC7_ERROR | SDHOST_HSTS_CRC16_ERROR)
+#define SDHOST_HSTS_ERROR           (SDHOST_HSTS_TIMOUT_ERROR | SDHOST_HSTS_TRANSFER_ERROR)
+
+//
+// HCFG
+//
+#define SDHOST_HCFG_SLOW_CARD       BIT3
+#define SDHOST_HCFG_WIDE_EXT_BUS    BIT2
+#define SDHOST_HCFG_WIDE_INT_BUS    BIT1
+#define SDHOST_HCFG_DATA_IRPT_EN    BIT4
+#define SDHOST_HCFG_BLOCK_IRPT_EN   BIT8
+#define SDHOST_HCFG_BUSY_IRPT_EN    BIT10
+
+//
+// EDM
+//
+#define SDHOST_EDM_FIFO_CLEAR               BIT21
+#define SDHOST_EDM_WRITE_THRESHOLD_SHIFT    9
+#define SDHOST_EDM_READ_THRESHOLD_SHIFT     14
+#define SDHOST_EDM_THRESHOLD_MASK           0x1F
+#define SDHOST_EDM_READ_THRESHOLD(X)        ((X) << SDHOST_EDM_READ_THRESHOLD_SHIFT)
+#define SDHOST_EDM_WRITE_THRESHOLD(X)       ((X) << SDHOST_EDM_WRITE_THRESHOLD_SHIFT)
+
+#define CMD8_SD_ARG       (0x0UL << 12 | BIT8 | 0xCEUL << 0)
+#define CMD8_MMC_ARG      (0)
+
+#endif //__BCM2836SDHOST_H__
diff --git a/Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2837Gpio.h b/Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2837Gpio.h
new file mode 100644
index 000000000000..27e6665f1745
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2837Gpio.h
@@ -0,0 +1,50 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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 __BCM2837GPIO_H__
+#define __BCM2837GPIO_H__
+
+#define GPIO_BASE_ADDRESS  (BCM2836_SOC_REGISTERS + 0x00200000)
+
+#define GPIO_GPFSEL0       (GPIO_BASE_ADDRESS + 0x00)
+#define GPIO_GPFSEL1       (GPIO_BASE_ADDRESS + 0x04)
+#define GPIO_GPFSEL2       (GPIO_BASE_ADDRESS + 0x08)
+#define GPIO_GPFSEL3       (GPIO_BASE_ADDRESS + 0x0C)
+#define GPIO_GPFSEL4       (GPIO_BASE_ADDRESS + 0x10)
+#define GPIO_GPFSEL5       (GPIO_BASE_ADDRESS + 0x14)
+
+#define GPIO_GPCLR0        (GPIO_BASE_ADDRESS + 0x28)
+#define GPIO_GPCLR1        (GPIO_BASE_ADDRESS + 0x2C)
+
+#define GPIO_GPSET0        (GPIO_BASE_ADDRESS + 0x1C)
+#define GPIO_GPSET1        (GPIO_BASE_ADDRESS + 0x20)
+
+#define GPIO_FSEL_INPUT    0x0
+#define GPIO_FSEL_OUTPUT   0x1
+#define GPIO_FSEL_ALT0     0x4
+#define GPIO_FSEL_ALT1     0x5
+#define GPIO_FSEL_ALT2     0x6
+#define GPIO_FSEL_ALT3     0x7
+#define GPIO_FSEL_ALT4     0x3
+#define GPIO_FSEL_ALT5     0x2
+
+#define GPIO_FSEL_PINS_PER_REGISTER 10
+#define GPIO_FSEL_BITS_PER_PIN      3
+#define GPIO_FSEL_MASK              ((1 << GPIO_FSEL_BITS_PER_PIN) - 1)
+
+#define GPIO_PINS          54
+
+#endif // __BCM2837GPIO_H__
+
diff --git a/Platform/Broadcom/Bcm283x/Include/IndustryStandard/RpiFirmware.h b/Platform/Broadcom/Bcm283x/Include/IndustryStandard/RpiFirmware.h
new file mode 100644
index 000000000000..83f8633c26cf
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Include/IndustryStandard/RpiFirmware.h
@@ -0,0 +1,93 @@
+/** @file
+ *
+ * Copyright (c) 2016, Linaro Limited. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ **/
+
+#define RPI_FW_MBOX_CHANNEL                                 0x00000008
+
+#define RPI_FW_RESP_SUCCESS                                 0x80000000
+#define RPI_FW_RESP_FAILURE                                 0x80000001
+
+#define RPI_FW_VALUE_SIZE_RESPONSE_MASK                     BIT31
+
+#define RPI_FW_GET_REVISION                                 0x00000001
+#define RPI_FW_GET_BOARD_MODEL                              0x00010001
+#define RPI_FW_GET_BOARD_REVISION                           0x00010002
+#define RPI_FW_GET_MAC_ADDRESS                              0x00010003
+#define RPI_FW_GET_BOARD_SERIAL                             0x00010004
+#define RPI_FW_GET_ARM_MEMSIZE                              0x00010005
+
+#define RPI_FW_SET_POWER_STATE                              0x00028001
+
+#define RPI_FW_POWER_STATE_SDHCI                            0x00000000
+#define RPI_FW_POWER_STATE_UART0                            0x00000001
+#define RPI_FW_POWER_STATE_UART1                            0x00000002
+#define RPI_FW_POWER_STATE_USB_HCD                          0x00000003
+#define RPI_FW_POWER_STATE_I2C0                             0x00000004
+#define RPI_FW_POWER_STATE_I2C1                             0x00000005
+#define RPI_FW_POWER_STATE_I2C2                             0x00000006
+#define RPI_FW_POWER_STATE_SPI                              0x00000007
+#define RPI_FW_POWER_STATE_CCP2TX                           0x00000008
+
+#define RPI_FW_GET_CLOCK_RATE                               0x00030002
+#define RPI_FW_GET_MAX_CLOCK_RATE                           0x00030004
+#define RPI_FW_GET_MIN_CLOCK_RATE                           0x00030007
+
+#define RPI_FW_SET_CLOCK_RATE                               0x00038002
+#define RPI_FW_SET_GPIO                                     0x00038041
+
+#define RPI_FW_GET_FB_GEOMETRY                              0x00040003
+#define RPI_FW_GET_FB_LINELENGTH                            0x00040008
+#define RPI_FW_GET_FB_COLOR_DEPTH                           0x00040005
+#define RPI_FW_GET_FB_REGION                                0x00040001
+
+#define RPI_FW_SET_FB_PGEOM                                 0x00048003
+#define RPI_FW_SET_FB_VGEOM                                 0x00048004
+#define RPI_FW_SET_FB_DEPTH                                 0x00048005
+#define RPI_FW_ALLOC_FB                                     0x00040001
+#define RPI_FW_FREE_FB                                      0x00048001
+
+#define RPI_FW_GET_COMMAND_LINE                             0x00050001
+
+#define RPI_FW_POWER_STATE_ENABLE                           BIT0
+#define RPI_FW_POWER_STATE_WAIT                             BIT1
+
+#define RPI_FW_CLOCK_RATE_EMMC                              0x000000001
+#define RPI_FW_CLOCK_RATE_UART                              0x000000002
+#define RPI_FW_CLOCK_RATE_ARM                               0x000000003
+#define RPI_FW_CLOCK_RATE_CORE                              0x000000004
+#define RPI_FW_CLOCK_RATE_V3D                               0x000000005
+#define RPI_FW_CLOCK_RATE_H264                              0x000000006
+#define RPI_FW_CLOCK_RATE_ISP                               0x000000007
+#define RPI_FW_CLOCK_RATE_SDRAM                             0x000000008
+#define RPI_FW_CLOCK_RATE_PIXEL                             0x000000009
+#define RPI_FW_CLOCK_RATE_PWM                               0x00000000a
+
+#define RPI_FB_MBOX_CHANNEL                                 0x1
diff --git a/Platform/Broadcom/Bcm283x/Include/Library/GpioLib.h b/Platform/Broadcom/Bcm283x/Include/Library/GpioLib.h
new file mode 100644
index 000000000000..ca9da4b11a87
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Include/Library/GpioLib.h
@@ -0,0 +1,33 @@
+/** @file
+ *
+ *  GPIO manipulation.
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *
+ *  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 __GPIO_LIB__
+#define __GPIO_LIB__
+
+#include <IndustryStandard/Bcm2837Gpio.h>
+
+VOID
+GpioPinFuncSet(
+  IN  UINTN Pin,
+  IN  UINTN Function
+  );
+
+UINTN
+GpioPinFuncGet(
+  IN  UINTN Pin
+  );
+
+#endif /* __GPIO_LIB__ */
diff --git a/Platform/Broadcom/Bcm283x/Include/Protocol/DwUsb.h b/Platform/Broadcom/Bcm283x/Include/Protocol/DwUsb.h
new file mode 100644
index 000000000000..dfad80c719e1
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Include/Protocol/DwUsb.h
@@ -0,0 +1,53 @@
+/** @file
+ *
+ *  Copyright (c) 2015-2016, Linaro. All rights reserved.
+ *  Copyright (c) 2015-2016, Hisilicon 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 __DW_USB_H__
+#define __DW_USB_H__
+
+//
+// Protocol GUID
+//
+#define DW_USB_PROTOCOL_GUID { 0x109fa264, 0x7811, 0x4862, { 0xa9, 0x73, 0x4a, 0xb2, 0xef, 0x2e, 0xe2, 0xff }}
+
+//
+// Protocol interface structure
+//
+typedef struct _DW_USB_PROTOCOL  DW_USB_PROTOCOL;
+
+#define USB_HOST_MODE    0
+#define USB_DEVICE_MODE  1
+#define USB_CABLE_NOT_ATTACHED  2
+
+typedef
+EFI_STATUS
+(EFIAPI *DW_USB_GET_SERIAL_NO) (
+  OUT CHAR16                           *SerialNo,
+  OUT UINT8                            *Length
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *DW_USB_PHY_INIT) (
+  IN UINT8                           Mode
+  );
+
+struct _DW_USB_PROTOCOL {
+  DW_USB_GET_SERIAL_NO                 Get;
+  DW_USB_PHY_INIT                      PhyInit;
+};
+
+extern EFI_GUID gDwUsbProtocolGuid;
+
+#endif
diff --git a/Platform/Broadcom/Bcm283x/Include/Protocol/ExtendedTextOut.h b/Platform/Broadcom/Bcm283x/Include/Protocol/ExtendedTextOut.h
new file mode 100644
index 000000000000..35e82a17e922
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Include/Protocol/ExtendedTextOut.h
@@ -0,0 +1,36 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *
+ *  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 __EXTENDED_TEXT_OUT_H__
+#define __EXTENDED_TEXT_OUT_H__
+
+#include <Protocol/SimpleTextOut.h>
+#include <Protocol/GraphicsOutput.h>
+
+#define EXTENDED_TEXT_OUTPUT_PROTOCOL_GUID \
+  { \
+    0x387477ff, 0xffc7, 0xffd2, {0x8e, 0x39, 0x0, 0xff, 0xc9, 0x69, 0x72, 0x3b } \
+  }
+
+typedef struct _EXTENDED_TEXT_OUTPUT_PROTOCOL EXTENDED_TEXT_OUTPUT_PROTOCOL;
+
+struct _EXTENDED_TEXT_OUTPUT_PROTOCOL {
+  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut;
+  EFI_GRAPHICS_OUTPUT_PROTOCOL    *GraphicsOutput;
+  BOOLEAN                         AutoWrap;
+};
+
+extern EFI_GUID gExtendedTextOutputProtocolGuid;
+
+#endif /* __EXTENDED_TEXT_OUT__ */
diff --git a/Platform/Broadcom/Bcm283x/Include/Protocol/PiMmcHost.h b/Platform/Broadcom/Bcm283x/Include/Protocol/PiMmcHost.h
new file mode 100644
index 000000000000..c0e65687e8d4
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Include/Protocol/PiMmcHost.h
@@ -0,0 +1,187 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  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 __PI_MMC_HOST_H__
+#define __PI_MMC_HOST_H__
+
+///
+/// Global ID for the MMC Host Protocol
+///
+#define RASPBERRY_PI_MMC_HOST_PROTOCOL_GUID \
+  { 0x3e591c00, 0x9e4a, 0x11df, {0x92, 0x44, 0x00, 0x02, 0xA5, 0xF5, 0xF5, 0x1B } }
+
+#define MMC_RESPONSE_TYPE_R1        0
+#define MMC_RESPONSE_TYPE_R1b       0
+#define MMC_RESPONSE_TYPE_R2        1
+#define MMC_RESPONSE_TYPE_R3        0
+#define MMC_RESPONSE_TYPE_R6        0
+#define MMC_RESPONSE_TYPE_R7        0
+#define MMC_RESPONSE_TYPE_OCR       0
+#define MMC_RESPONSE_TYPE_CID       1
+#define MMC_RESPONSE_TYPE_CSD       1
+#define MMC_RESPONSE_TYPE_RCA       0
+
+typedef UINT32  MMC_RESPONSE_TYPE;
+
+typedef UINT32 MMC_CMD;
+
+#define MMC_CMD_WAIT_RESPONSE      (1 << 16)
+#define MMC_CMD_LONG_RESPONSE      (1 << 17)
+#define MMC_CMD_NO_CRC_RESPONSE    (1 << 18)
+
+#define MMC_INDX(Index)       ((Index) & 0xFFFF)
+#define MMC_GET_INDX(MmcCmd)  ((MmcCmd) & 0xFFFF)
+
+#define MMC_CMD0              (MMC_INDX(0) | MMC_CMD_NO_CRC_RESPONSE)
+#define MMC_CMD1              (MMC_INDX(1) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_NO_CRC_RESPONSE)
+#define MMC_CMD2              (MMC_INDX(2) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_LONG_RESPONSE)
+#define MMC_CMD3              (MMC_INDX(3) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD5              (MMC_INDX(5) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_NO_CRC_RESPONSE)
+#define MMC_CMD6              (MMC_INDX(6) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD7              (MMC_INDX(7) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD8              (MMC_INDX(8) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD9              (MMC_INDX(9) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_LONG_RESPONSE)
+#define MMC_CMD11             (MMC_INDX(11) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD12             (MMC_INDX(12) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD13             (MMC_INDX(13) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD16             (MMC_INDX(16) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD17             (MMC_INDX(17) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD18             (MMC_INDX(18) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD20             (MMC_INDX(20) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD23             (MMC_INDX(23) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD24             (MMC_INDX(24) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD25             (MMC_INDX(25) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_CMD55             (MMC_INDX(55) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_ACMD22            (MMC_INDX(22) | MMC_CMD_WAIT_RESPONSE)
+#define MMC_ACMD41            (MMC_INDX(41) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_NO_CRC_RESPONSE)
+#define MMC_ACMD51            (MMC_INDX(51) | MMC_CMD_WAIT_RESPONSE)
+
+// Valid responses for CMD1 in eMMC
+#define EMMC_CMD1_CAPACITY_LESS_THAN_2GB 0x00FF8080 // Capacity <= 2GB, byte addressing used
+#define EMMC_CMD1_CAPACITY_GREATER_THAN_2GB 0x40FF8080 // Capacity > 2GB, 512-byte sector addressing used
+
+#define MMC_STATUS_APP_CMD    (1 << 5)
+
+typedef enum _MMC_STATE {
+    MmcInvalidState = 0,
+    MmcHwInitializationState,
+    MmcIdleState,
+    MmcReadyState,
+    MmcIdentificationState,
+    MmcStandByState,
+    MmcTransferState,
+    MmcSendingDataState,
+    MmcReceiveDataState,
+    MmcProgrammingState,
+    MmcDisconnectState,
+} MMC_STATE;
+
+#define EMMCBACKWARD         (0)
+#define EMMCHS26             (1 << 0)      // High-Speed @26MHz at rated device voltages
+#define EMMCHS52             (1 << 1)      // High-Speed @52MHz at rated device voltages
+#define EMMCHS52DDR1V8       (1 << 2)      // High-Speed Dual Data Rate @52MHz 1.8V or 3V I/O
+#define EMMCHS52DDR1V2       (1 << 3)      // High-Speed Dual Data Rate @52MHz 1.2V I/O
+#define EMMCHS200SDR1V8      (1 << 4)      // HS200 Single Data Rate @200MHz 1.8V I/O
+#define EMMCHS200SDR1V2      (1 << 5)      // HS200 Single Data Rate @200MHz 1.2V I/O
+#define EMMCHS400DDR1V8      (1 << 6)      // HS400 Dual Data Rate @400MHz 1.8V I/O
+#define EMMCHS400DDR1V2      (1 << 7)      // HS400 Dual Data Rate @400MHz 1.2V I/O
+
+///
+/// Forward declaration for EFI_MMC_HOST_PROTOCOL
+///
+typedef struct _EFI_MMC_HOST_PROTOCOL  EFI_MMC_HOST_PROTOCOL;
+
+typedef BOOLEAN (EFIAPI *MMC_ISCARDPRESENT) (
+  IN  EFI_MMC_HOST_PROTOCOL   *This
+  );
+
+typedef BOOLEAN (EFIAPI *MMC_ISREADONLY) (
+  IN  EFI_MMC_HOST_PROTOCOL   *This
+  );
+
+typedef EFI_STATUS (EFIAPI *MMC_BUILDDEVICEPATH) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This,
+  OUT EFI_DEVICE_PATH_PROTOCOL  **DevicePath
+  );
+
+typedef EFI_STATUS (EFIAPI *MMC_NOTIFYSTATE) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This,
+  IN  MMC_STATE                 State
+  );
+
+typedef EFI_STATUS (EFIAPI *MMC_SENDCOMMAND) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This,
+  IN  MMC_CMD                   Cmd,
+  IN  UINT32                    Argument
+  );
+
+typedef EFI_STATUS (EFIAPI *MMC_RECEIVERESPONSE) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This,
+  IN  MMC_RESPONSE_TYPE         Type,
+  IN  UINT32                    *Buffer
+  );
+
+typedef EFI_STATUS (EFIAPI *MMC_READBLOCKDATA) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This,
+  IN  EFI_LBA                   Lba,
+  IN  UINTN                     Length,
+  OUT UINT32                    *Buffer
+  );
+
+typedef EFI_STATUS (EFIAPI *MMC_WRITEBLOCKDATA) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This,
+  IN  EFI_LBA                   Lba,
+  IN  UINTN                     Length,
+  IN  UINT32                    *Buffer
+  );
+
+typedef EFI_STATUS (EFIAPI *MMC_SETIOS) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This,
+  IN  UINT32                    BusClockFreq,
+  IN  UINT32                    BusWidth,
+  IN  UINT32                    TimingMode
+  );
+
+typedef BOOLEAN (EFIAPI *MMC_ISMULTIBLOCK) (
+  IN  EFI_MMC_HOST_PROTOCOL     *This
+  );
+
+struct _EFI_MMC_HOST_PROTOCOL {
+  UINT32                  Revision;
+  MMC_ISCARDPRESENT       IsCardPresent;
+  MMC_ISREADONLY          IsReadOnly;
+  MMC_BUILDDEVICEPATH     BuildDevicePath;
+
+  MMC_NOTIFYSTATE         NotifyState;
+
+  MMC_SENDCOMMAND         SendCommand;
+  MMC_RECEIVERESPONSE     ReceiveResponse;
+
+  MMC_READBLOCKDATA       ReadBlockData;
+  MMC_WRITEBLOCKDATA      WriteBlockData;
+
+  MMC_SETIOS              SetIos;
+  MMC_ISMULTIBLOCK        IsMultiBlock;
+};
+
+#define MMC_HOST_PROTOCOL_REVISION    0x00010002    // 1.2
+
+#define MMC_HOST_HAS_SETIOS(Host)     (Host->Revision >= MMC_HOST_PROTOCOL_REVISION && \
+                                       Host->SetIos != NULL)
+#define MMC_HOST_HAS_ISMULTIBLOCK(Host) (Host->Revision >= MMC_HOST_PROTOCOL_REVISION && \
+                                         Host->IsMultiBlock != NULL)
+
+#endif
+
diff --git a/Platform/Broadcom/Bcm283x/Include/Protocol/RaspberryPiFirmware.h b/Platform/Broadcom/Bcm283x/Include/Protocol/RaspberryPiFirmware.h
new file mode 100644
index 000000000000..2a4247584641
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Include/Protocol/RaspberryPiFirmware.h
@@ -0,0 +1,131 @@
+/** @file
+ *
+ *  Copyright (c) 2016, Linaro 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 __RASPBERRY_PI_FIRMWARE_PROTOCOL_H__
+#define __RASPBERRY_PI_FIRMWARE_PROTOCOL_H__
+
+#define RASPBERRY_PI_FIRMWARE_PROTOL_GUID \
+  { 0x0ACA9535, 0x7AD0, 0x4286, { 0xB0, 0x2E, 0x87, 0xFA, 0x7E, 0x2A, 0x57, 0x11 } }
+
+typedef
+EFI_STATUS
+(EFIAPI *SET_POWER_STATE) (
+  IN  UINT32    DeviceId,
+  IN  BOOLEAN   PowerState,
+  IN  BOOLEAN   Wait
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *GET_MAC_ADDRESS) (
+  OUT UINT8     MacAddress[6]
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *GET_COMMAND_LINE) (
+  IN  UINTN     BufferSize,
+  OUT CHAR8     CommandLine[]
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *GET_CLOCK_RATE) (
+  IN  UINT32    ClockId,
+  OUT UINT32    *ClockRate
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *SET_CLOCK_RATE) (
+  IN  UINT32    ClockId,
+  OUT UINT32    ClockRate
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *GET_FB) (
+  IN  UINT32 Width,
+  IN  UINT32 Height,
+  IN  UINT32 Depth,
+  OUT EFI_PHYSICAL_ADDRESS *FbBase,
+  OUT UINTN *FbSize,
+  OUT UINTN *Pitch
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *GET_FB_SIZE) (
+  OUT   UINT32 *Width,
+  OUT   UINT32 *Height
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *FREE_FB) (
+  VOID
+  );
+
+typedef
+VOID
+(EFIAPI *SET_LED) (
+  BOOLEAN On
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *GET_SERIAL) (
+  UINT64 *Serial
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *GET_MODEL) (
+  UINT32 *Model
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *GET_MODEL_REVISION) (
+  UINT32 *Revision
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *GET_ARM_MEM) (
+  UINT32 *Base,
+  UINT32 *Size
+  );
+
+typedef struct {
+  SET_POWER_STATE    SetPowerState;
+  GET_MAC_ADDRESS    GetMacAddress;
+  GET_COMMAND_LINE   GetCommandLine;
+  GET_CLOCK_RATE     GetClockRate;
+  GET_CLOCK_RATE     GetMaxClockRate;
+  GET_CLOCK_RATE     GetMinClockRate;
+  SET_CLOCK_RATE     SetClockRate;
+  GET_FB             GetFB;
+  FREE_FB            FreeFB;
+  GET_FB_SIZE        GetFBSize;
+  SET_LED            SetLed;
+  GET_SERIAL         GetSerial;
+  GET_MODEL          GetModel;
+  GET_MODEL_REVISION GetModelRevision;
+  GET_ARM_MEM        GetArmMem;
+} RASPBERRY_PI_FIRMWARE_PROTOCOL;
+
+extern EFI_GUID gRaspberryPiFirmwareProtocolGuid;
+
+#endif
diff --git a/Platform/Broadcom/Bcm283x/Include/Utils.h b/Platform/Broadcom/Bcm283x/Include/Utils.h
new file mode 100644
index 000000000000..47713275de3f
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Include/Utils.h
@@ -0,0 +1,33 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *
+ *  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 UTILS_H
+#define UTILS_H
+
+#define _IX_BITS(sm, bg) ((bg) - (sm) + 1)
+#define _IX_MASK(sm, bg) ((1ul << _IX_BITS((sm), (bg))) - 1)
+#define _X(val, sm, bg) ((val) >> (sm)) & _IX_MASK((sm), (bg))
+#define X(val, ix1, ix2) (((ix1) < (ix2)) ? _X((val), (ix1), (ix2)) :   \
+                          _X((val), (ix2), (ix1)))
+
+#define _I(val, sm, bg)  (((val) & _IX_MASK((sm), (bg))) << (sm))
+#define I(val, ix1, ix2) (((ix1) < (ix2)) ? _I((val), (ix1), (ix2)) :   \
+                          _I((val), (ix2), (ix1)))
+#define _M(val, sm, bg)  ((val) & (_IX_MASK((sm), (bg)) << (sm)))
+#define M(val, ix1, ix2) (((ix1) < (ix2)) ? _M((val), (ix1), (ix2)) :   \
+                          _M((val), (ix2), (ix1)))
+
+#define ELES(x) (sizeof((x)) / sizeof((x)[0]))
+
+#endif /* UTILS_H */
diff --git a/Platform/Broadcom/Bcm283x/Library/GpioLib/GpioLib.c b/Platform/Broadcom/Bcm283x/Library/GpioLib/GpioLib.c
new file mode 100644
index 000000000000..db38acefb7c3
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Library/GpioLib/GpioLib.c
@@ -0,0 +1,79 @@
+/** @file
+ *
+ *  GPIO manipulation.
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *
+ *  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 <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/GpioLib.h>
+#include <IndustryStandard/Bcm2836.h>
+#include <Utils.h>
+
+STATIC VOID
+GpioFSELModify(
+  IN  UINTN RegIndex,
+  IN  UINT32 ModifyMask,
+  IN  UINT32 FunctionMask
+  )
+{
+  UINT32 Val;
+  EFI_PHYSICAL_ADDRESS Reg = RegIndex * sizeof(UINT32) + GPIO_GPFSEL0;
+
+  ASSERT(Reg <= GPIO_GPFSEL5);
+  ASSERT((~ModifyMask & FunctionMask) == 0);
+
+  Val = MmioRead32(Reg);
+  Val &= ~ModifyMask;
+  Val |= FunctionMask;
+  MmioWrite32(Reg, Val);
+}
+
+VOID
+GpioPinFuncSet(
+  IN  UINTN Pin,
+  IN  UINTN Function
+  )
+{
+  UINTN RegIndex = Pin / 10;
+  UINTN SelIndex = Pin % 10;
+  UINT32 ModifyMask;
+  UINT32 FunctionMask;
+
+  ASSERT(Pin < GPIO_PINS);
+  ASSERT(Function <= GPIO_FSEL_MASK);
+
+  ModifyMask = GPIO_FSEL_MASK << (SelIndex * GPIO_FSEL_BITS_PER_PIN);
+  FunctionMask = Function << (SelIndex * GPIO_FSEL_BITS_PER_PIN);
+  GpioFSELModify(RegIndex, ModifyMask, FunctionMask);
+}
+
+UINTN
+GpioPinFuncGet(
+  IN  UINTN Pin
+  )
+{
+  UINT32 Val;
+  UINTN RegIndex = Pin / 10;
+  UINTN SelIndex = Pin % 10;
+  EFI_PHYSICAL_ADDRESS Reg = RegIndex * sizeof(UINT32) + GPIO_GPFSEL0;
+
+  ASSERT(Pin < GPIO_PINS);
+
+  Val = MmioRead32(Reg);
+  Val >>= SelIndex * GPIO_FSEL_BITS_PER_PIN;
+  Val &= GPIO_FSEL_MASK;
+  return Val;
+}
diff --git a/Platform/Broadcom/Bcm283x/Library/GpioLib/GpioLib.inf b/Platform/Broadcom/Bcm283x/Library/GpioLib/GpioLib.inf
new file mode 100644
index 000000000000..82114568b0a6
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Library/GpioLib/GpioLib.inf
@@ -0,0 +1,39 @@
+#/** @file
+#
+#  Manipulate GPIOs.
+#
+#  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+#
+#  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                    = 0x00010005
+  BASE_NAME                      = GpioLib
+  FILE_GUID                      = B9F59B6B-B105-41C7-8F5A-2C60DD7FD7AB
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = GpioLib
+
+[Sources]
+  GpioLib.c
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  MdePkg/MdePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  IoLib
+
+[Guids]
diff --git a/Platform/Broadcom/Bcm283x/Library/MemoryInitPeiLib/MemoryInitPeiLib.c b/Platform/Broadcom/Bcm283x/Library/MemoryInitPeiLib/MemoryInitPeiLib.c
new file mode 100644
index 000000000000..81d810b5d428
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Library/MemoryInitPeiLib/MemoryInitPeiLib.c
@@ -0,0 +1,183 @@
+/** @file
+ *
+ *  Copyright (c) 2017-2018, Andrey Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2011-2015, 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 <PiPei.h>
+
+#include <Library/ArmMmuLib.h>
+#include <Library/ArmPlatformLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+
+extern UINT64 mSystemMemoryEnd;
+
+VOID
+BuildMemoryTypeInformationHob (
+  VOID
+  );
+
+STATIC
+VOID
+InitMmu (
+  IN ARM_MEMORY_REGION_DESCRIPTOR  *MemoryTable
+  )
+{
+
+  VOID                          *TranslationTableBase;
+  UINTN                         TranslationTableSize;
+  RETURN_STATUS                 Status;
+
+  //Note: Because we called PeiServicesInstallPeiMemory() before to call InitMmu() the MMU Page Table resides in
+  //      DRAM (even at the top of DRAM as it is the first permanent memory allocation)
+  Status = ArmConfigureMmu (MemoryTable, &TranslationTableBase, &TranslationTableSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Error: Failed to enable MMU\n"));
+  }
+}
+
+STATIC
+VOID
+AddAndRTSData(ARM_MEMORY_REGION_DESCRIPTOR *Desc)
+{
+  BuildResourceDescriptorHob (
+                              EFI_RESOURCE_SYSTEM_MEMORY,
+                              EFI_RESOURCE_ATTRIBUTE_PRESENT |
+                              EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
+                              EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |
+                              EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |
+                              EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE |
+                              EFI_RESOURCE_ATTRIBUTE_TESTED,
+                              Desc->PhysicalBase,
+                              Desc->Length
+                              );
+
+  BuildMemoryAllocationHob (
+                            Desc->PhysicalBase,
+                            Desc->Length,
+                            EfiRuntimeServicesData
+                            );
+}
+
+STATIC
+VOID
+AddAndReserved(ARM_MEMORY_REGION_DESCRIPTOR *Desc)
+{
+  BuildResourceDescriptorHob (
+                              EFI_RESOURCE_SYSTEM_MEMORY,
+                              EFI_RESOURCE_ATTRIBUTE_PRESENT |
+                              EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
+                              EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |
+                              EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |
+                              EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE |
+                              EFI_RESOURCE_ATTRIBUTE_TESTED,
+                              Desc->PhysicalBase,
+                              Desc->Length
+                              );
+
+  BuildMemoryAllocationHob (
+                            Desc->PhysicalBase,
+                            Desc->Length,
+                            EfiReservedMemoryType
+                            );
+}
+
+STATIC
+VOID
+AddAndMmio(ARM_MEMORY_REGION_DESCRIPTOR *Desc)
+{
+  BuildResourceDescriptorHob (
+                              EFI_RESOURCE_SYSTEM_MEMORY,
+                              (EFI_RESOURCE_ATTRIBUTE_PRESENT    |
+                               EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
+                               EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
+                               EFI_RESOURCE_ATTRIBUTE_TESTED),
+                              Desc->PhysicalBase,
+                              Desc->Length
+                              );
+
+  BuildMemoryAllocationHob (
+                            Desc->PhysicalBase,
+                            Desc->Length,
+                            EfiMemoryMappedIO
+                            );
+}
+
+/*++
+
+Routine Description:
+
+
+
+Arguments:
+
+  FileHandle  - Handle of the file being invoked.
+  PeiServices - Describes the list of possible PEI Services.
+
+Returns:
+
+  Status -  EFI_SUCCESS if the boot mode could be set
+
+--*/
+EFI_STATUS
+EFIAPI
+MemoryPeim (
+  IN EFI_PHYSICAL_ADDRESS               UefiMemoryBase,
+  IN UINT64                             UefiMemorySize
+  )
+{
+  ARM_MEMORY_REGION_DESCRIPTOR *MemoryTable;
+
+  // Get Virtual Memory Map from the Platform Library
+  ArmPlatformGetVirtualMemoryMap (&MemoryTable);
+
+  // Ensure PcdSystemMemorySize has been set
+  ASSERT (PcdGet64 (PcdSystemMemorySize) != 0);
+
+  // FD without variable store
+  AddAndReserved(&MemoryTable[0]);
+
+  // Variable store.
+  AddAndRTSData(&MemoryTable[1]);
+
+  // Trusted Firmware region
+  AddAndReserved(&MemoryTable[2]);
+
+  // Usable memory.
+  BuildResourceDescriptorHob (
+                              EFI_RESOURCE_SYSTEM_MEMORY,
+                              EFI_RESOURCE_ATTRIBUTE_PRESENT |
+                              EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
+                              EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |
+                              EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |
+                              EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE |
+                              EFI_RESOURCE_ATTRIBUTE_TESTED,
+                              MemoryTable[3].PhysicalBase,
+                              MemoryTable[3].Length
+                              );
+
+  AddAndReserved(&MemoryTable[4]);
+  AddAndMmio(&MemoryTable[5]);
+
+  // Build Memory Allocation Hob
+  InitMmu (MemoryTable);
+
+  if (FeaturePcdGet (PcdPrePiProduceMemoryTypeInformationHob)) {
+    // Optional feature that helps prevent EFI memory map fragmentation.
+    BuildMemoryTypeInformationHob ();
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Broadcom/Bcm283x/Library/MemoryInitPeiLib/MemoryInitPeiLib.inf b/Platform/Broadcom/Bcm283x/Library/MemoryInitPeiLib/MemoryInitPeiLib.inf
new file mode 100644
index 000000000000..9f5204a210de
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Library/MemoryInitPeiLib/MemoryInitPeiLib.inf
@@ -0,0 +1,51 @@
+#/** @file
+#
+#  Copyright (c) 2016, Linaro, Ltd. All rights reserved.
+#  Copyright (c) 2011-2014, ARM Ltd. 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                    = 0x00010005
+  BASE_NAME                      = MemoryInitPeiLib
+  FILE_GUID                      = 4bbc9c10-a100-43fb-8311-332ba497d1b4
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = MemoryInitPeiLib|SEC PEIM
+
+[Sources]
+  MemoryInitPeiLib.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  ArmPkg/ArmPkg.dec
+  ArmPlatformPkg/ArmPlatformPkg.dec
+
+[LibraryClasses]
+  DebugLib
+  HobLib
+  ArmMmuLib
+  ArmPlatformLib
+
+[Guids]
+  gEfiMemoryTypeInformationGuid
+
+[FeaturePcd]
+  gEmbeddedTokenSpaceGuid.PcdPrePiProduceMemoryTypeInformationHob
+
+[FixedPcd]
+  gArmTokenSpaceGuid.PcdSystemMemoryBase
+  gArmTokenSpaceGuid.PcdSystemMemorySize
+
+[Depex]
+  TRUE
diff --git a/Platform/Broadcom/Bcm283x/Library/PlatformBootManagerLib/PlatformBm.c b/Platform/Broadcom/Bcm283x/Library/PlatformBootManagerLib/PlatformBm.c
new file mode 100644
index 000000000000..58eeb117c769
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Library/PlatformBootManagerLib/PlatformBm.c
@@ -0,0 +1,831 @@
+/** @file
+ *
+ *  Copyright (c) 2018, Pete Batard <pete at akeo.ie>
+ *  Copyright (c) 2017-2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2016, Linaro Ltd. All rights reserved.
+ *  Copyright (c) 2015-2016, Red Hat, Inc.
+ *  Copyright (c) 2014, ARM Ltd. All rights reserved.
+ *  Copyright (c) 2004-2016, Intel 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 <Library/BootLogoLib.h>
+#include <Library/CapsuleLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/HobLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootManagerLib.h>
+#include <Library/UefiLib.h>
+#include <Library/PrintLib.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/EsrtManagement.h>
+#include <Protocol/GraphicsOutput.h>
+#include <Protocol/LoadedImage.h>
+#include <Guid/EventGroup.h>
+#include <Guid/TtyTerm.h>
+#include <Protocol/BootLogo.h>
+
+#include "PlatformBm.h"
+
+#define BOOT_PROMPT L"ESC (setup), F1 (shell), ENTER (boot)"
+
+#define DP_NODE_LEN(Type) { (UINT8)sizeof (Type), (UINT8)(sizeof (Type) >> 8) }
+
+#pragma pack (1)
+typedef struct {
+  VENDOR_DEVICE_PATH         SerialDxe;
+  UART_DEVICE_PATH           Uart;
+  VENDOR_DEFINED_DEVICE_PATH TermType;
+  EFI_DEVICE_PATH_PROTOCOL   End;
+} PLATFORM_SERIAL_CONSOLE;
+#pragma pack ()
+
+typedef struct {
+  VENDOR_DEVICE_PATH            Custom;
+  USB_DEVICE_PATH               Hub;
+  USB_DEVICE_PATH               Dev;
+  EFI_DEVICE_PATH_PROTOCOL      EndDevicePath;
+} PLATFORM_USB_DEV;
+
+typedef struct {
+  VENDOR_DEVICE_PATH            Custom;
+  EFI_DEVICE_PATH_PROTOCOL      EndDevicePath;
+} PLATFORM_SD_DEV;
+
+#define DW_USB_DXE_FILE_GUID { \
+          0x4bf1704c, 0x03f4, 0x46d5, \
+          { 0xbc, 0xa6, 0x82, 0xfa, 0x58, 0x0b, 0xad, 0xfd } \
+          }
+
+#define ARASAN_MMC_DXE_FILE_GUID { \
+          0x100c2cfa, 0xb586, 0x4198, \
+          { 0x9b, 0x4c, 0x16, 0x83, 0xd1, 0x95, 0xb1, 0xda } \
+          }
+
+#define SDHOST_MMC_DXE_FILE_GUID { \
+          0x58abd787, 0xf64d, 0x4ca2, \
+          { 0xa0, 0x34, 0xb9, 0xac, 0x2d, 0x5a, 0xd0, 0xcf } \
+          }
+
+STATIC PLATFORM_SD_DEV mArasan = {
+  //
+  // VENDOR_DEVICE_PATH ArasanMMCHostDxe
+  //
+  {
+    { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, DP_NODE_LEN (VENDOR_DEVICE_PATH) },
+    ARASAN_MMC_DXE_FILE_GUID
+  },
+
+  //
+  // EFI_DEVICE_PATH_PROTOCOL End
+  //
+  {
+    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL)
+  }
+};
+
+STATIC PLATFORM_SD_DEV mSDHost = {
+  //
+  // VENDOR_DEVICE_PATH SdHostDxe
+  //
+  {
+    { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, DP_NODE_LEN (VENDOR_DEVICE_PATH) },
+    SDHOST_MMC_DXE_FILE_GUID
+  },
+
+  //
+  // EFI_DEVICE_PATH_PROTOCOL End
+  //
+  {
+    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL)
+  }
+};
+
+STATIC PLATFORM_USB_DEV mUsbHubPort = {
+  //
+  // VENDOR_DEVICE_PATH DwUsbHostDxe
+  //
+  {
+    { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, DP_NODE_LEN (VENDOR_DEVICE_PATH) },
+    DW_USB_DXE_FILE_GUID
+  },
+
+  //
+  // USB_DEVICE_PATH Hub
+  //
+  {
+    { MESSAGING_DEVICE_PATH, MSG_USB_DP, DP_NODE_LEN (USB_DEVICE_PATH) },
+    0, 0
+  },
+
+  //
+  // USB_DEVICE_PATH Dev
+  //
+  {
+    { MESSAGING_DEVICE_PATH, MSG_USB_DP, DP_NODE_LEN (USB_DEVICE_PATH) },
+    1, 0
+  },
+
+  //
+  // EFI_DEVICE_PATH_PROTOCOL End
+  //
+  {
+    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL)
+  }
+};
+
+#define SERIAL_DXE_FILE_GUID { \
+          0xD3987D4B, 0x971A, 0x435F, \
+          { 0x8C, 0xAF, 0x49, 0x67, 0xEB, 0x62, 0x72, 0x41 } \
+          }
+
+STATIC PLATFORM_SERIAL_CONSOLE mSerialConsole = {
+  //
+  // VENDOR_DEVICE_PATH SerialDxe
+  //
+  {
+    { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, DP_NODE_LEN (VENDOR_DEVICE_PATH) },
+    SERIAL_DXE_FILE_GUID
+  },
+
+  //
+  // UART_DEVICE_PATH Uart
+  //
+  {
+    { MESSAGING_DEVICE_PATH, MSG_UART_DP, DP_NODE_LEN (UART_DEVICE_PATH) },
+    0,                                      // Reserved
+    FixedPcdGet64 (PcdUartDefaultBaudRate), // BaudRate
+    FixedPcdGet8 (PcdUartDefaultDataBits),  // DataBits
+    FixedPcdGet8 (PcdUartDefaultParity),    // Parity
+    FixedPcdGet8 (PcdUartDefaultStopBits)   // StopBits
+  },
+
+  //
+  // VENDOR_DEFINED_DEVICE_PATH TermType
+  //
+  {
+    {
+      MESSAGING_DEVICE_PATH, MSG_VENDOR_DP,
+      DP_NODE_LEN (VENDOR_DEFINED_DEVICE_PATH)
+    }
+    //
+    // Guid to be filled in dynamically
+    //
+  },
+
+  //
+  // EFI_DEVICE_PATH_PROTOCOL End
+  //
+  {
+    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL)
+  }
+};
+
+
+#pragma pack (1)
+typedef struct {
+  USB_CLASS_DEVICE_PATH    Keyboard;
+  EFI_DEVICE_PATH_PROTOCOL End;
+} PLATFORM_USB_KEYBOARD;
+#pragma pack ()
+
+STATIC PLATFORM_USB_KEYBOARD mUsbKeyboard = {
+  //
+  // USB_CLASS_DEVICE_PATH Keyboard
+  //
+  {
+    {
+      MESSAGING_DEVICE_PATH, MSG_USB_CLASS_DP,
+      DP_NODE_LEN (USB_CLASS_DEVICE_PATH)
+    },
+    0xFFFF, // VendorId: any
+    0xFFFF, // ProductId: any
+    3,      // DeviceClass: HID
+    1,      // DeviceSubClass: boot
+    1       // DeviceProtocol: keyboard
+  },
+
+  //
+  // EFI_DEVICE_PATH_PROTOCOL End
+  //
+  {
+    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL)
+  }
+};
+
+STATIC EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *mSerialConProtocol;
+
+/**
+  Check if the handle satisfies a particular condition.
+
+  @param[in] Handle      The handle to check.
+  @param[in] ReportText  A caller-allocated string passed in for reporting
+                         purposes. It must never be NULL.
+
+  @retval TRUE   The condition is satisfied.
+  @retval FALSE  Otherwise. This includes the case when the condition could not
+                 be fully evaluated due to an error.
+**/
+typedef
+BOOLEAN
+(EFIAPI *FILTER_FUNCTION) (
+  IN EFI_HANDLE   Handle,
+  IN CONST CHAR16 *ReportText
+  );
+
+
+/**
+  Process a handle.
+
+  @param[in] Handle      The handle to process.
+  @param[in] ReportText  A caller-allocated string passed in for reporting
+                         purposes. It must never be NULL.
+**/
+typedef
+VOID
+(EFIAPI *CALLBACK_FUNCTION)  (
+  IN EFI_HANDLE   Handle,
+  IN CONST CHAR16 *ReportText
+  );
+
+/**
+  Locate all handles that carry the specified protocol, filter them with a
+  callback function, and pass each handle that passes the filter to another
+  callback.
+
+  @param[in] ProtocolGuid  The protocol to look for.
+
+  @param[in] Filter        The filter function to pass each handle to. If this
+                           parameter is NULL, then all handles are processed.
+
+  @param[in] Process       The callback function to pass each handle to that
+                           clears the filter.
+**/
+STATIC
+VOID
+FilterAndProcess (
+  IN EFI_GUID          *ProtocolGuid,
+  IN FILTER_FUNCTION   Filter         OPTIONAL,
+  IN CALLBACK_FUNCTION Process
+  )
+{
+  EFI_STATUS Status;
+  EFI_HANDLE *Handles;
+  UINTN      NoHandles;
+  UINTN      Idx;
+
+  Status = gBS->LocateHandleBuffer (ByProtocol, ProtocolGuid,
+                  NULL /* SearchKey */, &NoHandles, &Handles);
+  if (EFI_ERROR (Status)) {
+    //
+    // This is not an error, just an informative condition.
+    //
+    DEBUG ((DEBUG_VERBOSE, "%a: %g: %r\n", __FUNCTION__, ProtocolGuid,
+      Status));
+    return;
+  }
+
+  ASSERT (NoHandles > 0);
+  for (Idx = 0; Idx < NoHandles; ++Idx) {
+    CHAR16        *DevicePathText;
+    STATIC CHAR16 Fallback[] = L"<device path unavailable>";
+
+    //
+    // The ConvertDevicePathToText() function handles NULL input transparently.
+    //
+    DevicePathText = ConvertDevicePathToText (
+                       DevicePathFromHandle (Handles[Idx]),
+                       FALSE, // DisplayOnly
+                       FALSE  // AllowShortcuts
+                       );
+    if (DevicePathText == NULL) {
+      DevicePathText = Fallback;
+    }
+
+    if (Filter == NULL || Filter (Handles[Idx], DevicePathText)) {
+      Process (Handles[Idx], DevicePathText);
+    }
+
+    if (DevicePathText != Fallback) {
+      FreePool (DevicePathText);
+    }
+  }
+  gBS->FreePool (Handles);
+}
+
+/**
+  This CALLBACK_FUNCTION retrieves the EFI_DEVICE_PATH_PROTOCOL from the
+  handle, and adds it to ConOut and ErrOut.
+**/
+STATIC
+VOID
+EFIAPI
+AddOutput (
+  IN EFI_HANDLE   Handle,
+  IN CONST CHAR16 *ReportText
+  )
+{
+  EFI_STATUS               Status;
+  EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+  DevicePath = DevicePathFromHandle (Handle);
+  if (DevicePath == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: %s: handle %p: device path not found\n",
+      __FUNCTION__, ReportText, Handle));
+    return;
+  }
+
+  Status = EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: %s: adding to ConOut: %r\n", __FUNCTION__,
+      ReportText, Status));
+    return;
+  }
+
+  Status = EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: %s: adding to ErrOut: %r\n", __FUNCTION__,
+      ReportText, Status));
+    return;
+  }
+
+  DEBUG ((DEBUG_VERBOSE, "%a: %s: added to ConOut and ErrOut\n", __FUNCTION__,
+    ReportText));
+}
+
+STATIC
+INTN
+PlatformRegisterBootOption (
+  EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+  CHAR16                   *Description,
+  UINT32                   Attributes
+  )
+{
+  EFI_STATUS                        Status;
+  INTN                              OptionIndex;
+  EFI_BOOT_MANAGER_LOAD_OPTION      NewOption;
+  EFI_BOOT_MANAGER_LOAD_OPTION      *BootOptions;
+  UINTN                             BootOptionCount;
+
+  Status = EfiBootManagerInitializeLoadOption (
+             &NewOption,
+             LoadOptionNumberUnassigned,
+             LoadOptionTypeBoot,
+             Attributes,
+             Description,
+             DevicePath,
+             NULL,
+             0
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  BootOptions = EfiBootManagerGetLoadOptions (
+                  &BootOptionCount, LoadOptionTypeBoot
+                  );
+
+  OptionIndex = EfiBootManagerFindLoadOption (
+                  &NewOption, BootOptions, BootOptionCount
+                  );
+
+  if (OptionIndex == -1) {
+    Status = EfiBootManagerAddLoadOptionVariable (&NewOption, MAX_UINTN);
+    ASSERT_EFI_ERROR (Status);
+    OptionIndex = BootOptionCount;
+  }
+
+  EfiBootManagerFreeLoadOption (&NewOption);
+  EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+
+  return OptionIndex;
+}
+
+STATIC
+INTN
+PlatformRegisterFvBootOption (
+  CONST EFI_GUID                   *FileGuid,
+  CHAR16                           *Description,
+  UINT32                           Attributes
+  )
+{
+  EFI_STATUS                        Status;
+  MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
+  EFI_LOADED_IMAGE_PROTOCOL         *LoadedImage;
+  EFI_DEVICE_PATH_PROTOCOL          *DevicePath;
+  INTN OptionIndex;
+
+  Status = gBS->HandleProtocol (
+                  gImageHandle,
+                  &gEfiLoadedImageProtocolGuid,
+                  (VOID **) &LoadedImage
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid);
+  DevicePath = DevicePathFromHandle (LoadedImage->DeviceHandle);
+  ASSERT (DevicePath != NULL);
+  DevicePath = AppendDevicePathNode (
+                 DevicePath,
+                 (EFI_DEVICE_PATH_PROTOCOL *) &FileNode
+                 );
+  ASSERT (DevicePath != NULL);
+
+  OptionIndex = PlatformRegisterBootOption (DevicePath,
+                                            Description,
+                                            Attributes);
+  FreePool (DevicePath);
+
+  return OptionIndex;
+}
+
+
+STATIC
+VOID
+PlatformRegisterOptionsAndKeys (
+  VOID
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_INPUT_KEY                Enter;
+  EFI_INPUT_KEY                F1;
+  EFI_INPUT_KEY                Esc;
+  EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
+  INTN ShellOption;
+
+  ShellOption = PlatformRegisterFvBootOption (&gUefiShellFileGuid, L"UEFI Shell",
+                                              LOAD_OPTION_ACTIVE);
+  if (ShellOption != -1) {
+    //
+    // F1 boots Shell.
+    //
+    F1.ScanCode = SCAN_F1;
+    F1.UnicodeChar = CHAR_NULL;
+    Status = EfiBootManagerAddKeyOptionVariable (
+      NULL, (UINT16) ShellOption, 0, &F1, NULL);
+    ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
+  }
+
+  //
+  // Register ENTER as CONTINUE key
+  //
+  Enter.ScanCode    = SCAN_NULL;
+  Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;
+  Status = EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Map ESC to Boot Manager Menu
+  //
+  Esc.ScanCode    = SCAN_ESC;
+  Esc.UnicodeChar = CHAR_NULL;
+  Status = EfiBootManagerGetBootManagerMenu (&BootOption);
+  ASSERT_EFI_ERROR (Status);
+  Status = EfiBootManagerAddKeyOptionVariable (
+             NULL, (UINT16) BootOption.OptionNumber, 0, &Esc, NULL
+             );
+  ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
+}
+
+STATIC VOID
+SerialConPrint (
+  IN CHAR16 *Text
+  )
+{
+  if (mSerialConProtocol != NULL) {
+    mSerialConProtocol->OutputString (mSerialConProtocol, Text);
+  }
+}
+
+STATIC VOID EFIAPI
+ExitBootServicesHandler (
+                         EFI_EVENT     Event,
+                         VOID          *Context
+                         )
+{
+  EFI_STATUS Status;
+  CHAR16 *OsBootStr;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Green;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Yellow;
+  //
+  // Long enough to occlude the string printed
+  // in PlatformBootManagerWaitCallback.
+  //
+  STATIC CHAR16 *OsBootStrEL1 = L"Exiting UEFI and booting EL1 OS kernel!\r\n";
+  STATIC CHAR16 *OsBootStrEL2 = L"Exiting UEFI and booting EL2 OS kernel!\r\n";
+
+  if (!PcdGet32 (PcdDebugShowUEFIExit)) {
+    return;
+  }
+
+  if (PcdGet32 (PcdHypEnable)) {
+    OsBootStr = OsBootStrEL1;
+  } else {
+    OsBootStr = OsBootStrEL2;
+  }
+
+  Green.Raw = 0x00007F00;
+  Black.Raw = 0x00000000;
+  Yellow.Raw = 0x00FFFF00;
+
+  Status = BootLogoUpdateProgress (Yellow.Pixel,
+                                   Black.Pixel,
+                                   OsBootStr,
+                                   Green.Pixel,
+                                   100, 0);
+  if (Status == EFI_SUCCESS) {
+    SerialConPrint(OsBootStr);
+  } else {
+    Print(L"\n");
+    Print(OsBootStr);
+    Print(L"\n");
+  }
+}
+
+//
+// BDS Platform Functions
+//
+/**
+  Do the platform init, can be customized by OEM/IBV
+  Possible things that can be done in PlatformBootManagerBeforeConsole:
+  > Update console variable: 1. include hot-plug devices;
+  >                          2. Clear ConIn and add SOL for AMT
+  > Register new Driver#### or Boot####
+  > Register new Key####: e.g.: F12
+  > Signal ReadyToLock event
+  > Authentication action: 1. connect Auth devices;
+  >                        2. Identify auto logon user.
+**/
+VOID
+EFIAPI
+PlatformBootManagerBeforeConsole (
+  VOID
+  )
+{
+  EFI_STATUS Status;
+  EFI_EVENT ExitBSEvent;
+  ESRT_MANAGEMENT_PROTOCOL *EsrtManagement;
+
+  Status = gBS->CreateEventEx (
+                               EVT_NOTIFY_SIGNAL,
+                               TPL_NOTIFY,
+                               ExitBootServicesHandler,
+                               NULL,
+                               &gEfiEventExitBootServicesGuid,
+                               &ExitBSEvent
+                               );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to register ExitBootServices handler\n",
+            __FUNCTION__));
+  }
+
+  if (GetBootModeHob() == BOOT_ON_FLASH_UPDATE) {
+    DEBUG ((DEBUG_INFO, "ProcessCapsules Before EndOfDxe ......\n"));
+    Status = ProcessCapsules ();
+    DEBUG ((DEBUG_INFO, "ProcessCapsules returned %r\n", Status));
+  } else {
+    Status = gBS->LocateProtocol (&gEsrtManagementProtocolGuid, NULL,
+                    (VOID **)&EsrtManagement);
+    if (!EFI_ERROR (Status)) {
+      EsrtManagement->SyncEsrtFmp ();
+    }
+  }
+
+  //
+  // Now add the device path of all handles with GOP on them to ConOut and
+  // ErrOut.
+  //
+  FilterAndProcess (&gEfiGraphicsOutputProtocolGuid, NULL, AddOutput);
+
+  //
+  // Add the hardcoded short-form USB keyboard device path to ConIn.
+  //
+  EfiBootManagerUpdateConsoleVariable (ConIn,
+    (EFI_DEVICE_PATH_PROTOCOL *)&mUsbKeyboard, NULL);
+
+  //
+  // Add the hardcoded serial console device path to ConIn, ConOut, ErrOut.
+  //
+  ASSERT (FixedPcdGet8 (PcdDefaultTerminalType) == 4);
+  CopyGuid (&mSerialConsole.TermType.Guid, &gEfiTtyTermGuid);
+
+  EfiBootManagerUpdateConsoleVariable (ConIn,
+    (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL);
+  EfiBootManagerUpdateConsoleVariable (ConOut,
+    (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL);
+  EfiBootManagerUpdateConsoleVariable (ErrOut,
+    (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL);
+
+  //
+  // Signal EndOfDxe PI Event
+  //
+  EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid);
+
+  //
+  // Dispatch deferred images after EndOfDxe event and ReadyToLock installation.
+  //
+  EfiBootManagerDispatchDeferredImages ();
+}
+
+/**
+  Do the platform specific action after the console is ready
+  Possible things that can be done in PlatformBootManagerAfterConsole:
+  > Console post action:
+    > Dynamically switch output mode from 100x31 to 80x25 for certain senarino
+    > Signal console ready platform customized event
+  > Run diagnostics like memory testing
+  > Connect certain devices
+  > Dispatch aditional option roms
+  > Special boot: e.g.: USB boot, enter UI
+**/
+VOID
+EFIAPI
+PlatformBootManagerAfterConsole (
+  VOID
+  )
+{
+  UINTN Index;
+  ESRT_MANAGEMENT_PROTOCOL      *EsrtManagement;
+  EFI_STATUS                    Status;
+  EFI_HANDLE SerialHandle;
+
+  Status = EfiBootManagerConnectDevicePath((EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, &SerialHandle);
+  if (Status == EFI_SUCCESS) {
+    gBS->HandleProtocol(SerialHandle, &gEfiSimpleTextOutProtocolGuid,
+                        (VOID **) &mSerialConProtocol);
+  }
+
+  //
+  // Show the splash screen.
+  //
+  Status = BootLogoEnableLogo ();
+  if (Status == EFI_SUCCESS) {
+    SerialConPrint(BOOT_PROMPT);
+  } else {
+    Print(BOOT_PROMPT);
+  }
+
+  //
+  // Connect the rest of the devices.
+  //
+  EfiBootManagerConnectAll ();
+
+  Status = gBS->LocateProtocol (&gEsrtManagementProtocolGuid, NULL,
+                  (VOID **)&EsrtManagement);
+  if (!EFI_ERROR (Status)) {
+    EsrtManagement->SyncEsrtFmp ();
+  }
+
+  if (GetBootModeHob() == BOOT_ON_FLASH_UPDATE) {
+    DEBUG((DEBUG_INFO, "ProcessCapsules After EndOfDxe ......\n"));
+    Status = ProcessCapsules ();
+    DEBUG((DEBUG_INFO, "ProcessCapsules returned %r\n", Status));
+  }
+
+  for (Index = 1; Index < 5; Index++) {
+    UINT16 Desc[11];
+    /*
+     * Add boot options to allow booting from
+     * a mass storage device plugged into any
+     * of the RPi USB ports.
+     */
+    mUsbHubPort.Dev.ParentPortNumber = Index;
+    UnicodeSPrint(Desc, sizeof (Desc), L"USB Port %u", Index);
+    PlatformRegisterBootOption ((VOID *) &mUsbHubPort,
+                                Desc, LOAD_OPTION_ACTIVE);
+  }
+
+  PlatformRegisterBootOption ((VOID *) &mSDHost,
+                              L"uSD on SD Host",
+                              LOAD_OPTION_ACTIVE);
+  PlatformRegisterBootOption ((VOID *) &mArasan,
+                              L"uSD on Arasan MMC Host",
+                              LOAD_OPTION_ACTIVE);
+
+  PlatformRegisterOptionsAndKeys ();
+}
+
+/**
+  This function is called each second during the boot manager waits the
+  timeout.
+
+  @param TimeoutRemain  The remaining timeout.
+**/
+VOID
+EFIAPI
+PlatformBootManagerWaitCallback (
+  UINT16          TimeoutRemain
+  )
+{
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White;
+  UINT16                              Timeout;
+  EFI_STATUS                          Status;
+  EFI_BOOT_LOGO_PROTOCOL *BootLogo;
+
+  Timeout = PcdGet16 (PcdPlatformBootTimeOut);
+
+  Black.Raw = 0x00000000;
+  White.Raw = 0x00FFFFFF;
+
+  Status = BootLogoUpdateProgress (
+    White.Pixel,
+    Black.Pixel,
+    BOOT_PROMPT,
+    White.Pixel,
+    (Timeout - TimeoutRemain) * 100 / Timeout,
+    0
+    );
+  if (Status == EFI_SUCCESS) {
+    SerialConPrint(L".");
+  } else {
+    Print(L".");
+  }
+
+  if (TimeoutRemain == 0) {
+    BootLogo = NULL;
+
+    //
+    // Clear out the boot logo so that Windows displays its own logo
+    // instead of ours.
+    //
+    Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo);
+    if (!EFI_ERROR (Status) && (BootLogo != NULL)) {
+      Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0);
+      ASSERT_EFI_ERROR (Status);
+    };
+
+    gST->ConOut->ClearScreen (gST->ConOut);
+  }
+}
+
+/**
+  The function is called when no boot option could be launched,
+  including platform recovery options and options pointing to applications
+  built into firmware volumes.
+  If this function returns, BDS attempts to enter an infinite loop.
+**/
+VOID
+EFIAPI
+PlatformBootManagerUnableToBoot(
+    VOID
+)
+{
+  EFI_STATUS                   Status;
+  EFI_INPUT_KEY                Key;
+  EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;
+  UINTN                        Index;
+
+  //
+  // BootManagerMenu doesn't contain the correct information when return status
+  // is EFI_NOT_FOUND.
+  //
+  Status = EfiBootManagerGetBootManagerMenu(&BootManagerMenu);
+  if (EFI_ERROR(Status)) {
+    return;
+  }
+  //
+  // Normally BdsDxe does not print anything to the system console, but this is
+  // a last resort -- the end-user will likely not see any DEBUG messages
+  // logged in this situation.
+  //
+  // AsciiPrint() will NULL-check gST->ConOut internally. We check gST->ConIn
+  // here to see if it makes sense to request and wait for a keypress.
+  //
+  if (gST->ConIn != NULL) {
+    AsciiPrint(
+      "%a: No bootable option or device was found.\n"
+      "%a: Press any key to enter the Boot Manager Menu.\n",
+      gEfiCallerBaseName,
+      gEfiCallerBaseName
+    );
+    Status = gBS->WaitForEvent(1, &gST->ConIn->WaitForKey, &Index);
+    ASSERT_EFI_ERROR(Status);
+    ASSERT(Index == 0);
+
+    //
+    // Drain any queued keys.
+    //
+    while (!EFI_ERROR(gST->ConIn->ReadKeyStroke(gST->ConIn, &Key))) {
+      //
+      // just throw away Key
+      //
+    }
+  }
+
+  for (;;) {
+    EfiBootManagerBoot(&BootManagerMenu);
+  }
+}
diff --git a/Platform/Broadcom/Bcm283x/Library/PlatformBootManagerLib/PlatformBm.h b/Platform/Broadcom/Bcm283x/Library/PlatformBootManagerLib/PlatformBm.h
new file mode 100644
index 000000000000..3717ba6174df
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Library/PlatformBootManagerLib/PlatformBm.h
@@ -0,0 +1,60 @@
+/** @file
+ *
+ *  Copyright (c) 2017-2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2016, Linaro Ltd. All rights reserved.
+ *  Copyright (c) 2015-2016, Red Hat, Inc.
+ *  Copyright (c) 2014, ARM Ltd. All rights reserved.
+ *  Copyright (c) 2004-2016, Intel 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 _PLATFORM_BM_H_
+#define _PLATFORM_BM_H_
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+/**
+  Use SystemTable Conout to stop video based Simple Text Out consoles from
+  going to the video device. Put up LogoFile on every video device that is a
+  console.
+
+  @param[in]  LogoFile   File name of logo to display on the center of the
+                         screen.
+
+  @retval EFI_SUCCESS     ConsoleControl has been flipped to graphics and logo
+                          displayed.
+  @retval EFI_UNSUPPORTED Logo not found
+**/
+EFI_STATUS
+EnableQuietBoot (
+  IN  EFI_GUID  *LogoFile
+  );
+
+/**
+  Use SystemTable Conout to turn on video based Simple Text Out consoles. The
+  Simple Text Out screens will now be synced up with all non video output
+  devices
+
+  @retval EFI_SUCCESS     UGA devices are back in text mode and synced up.
+**/
+EFI_STATUS
+DisableQuietBoot (
+  VOID
+  );
+
+#endif // _PLATFORM_BM_H_
diff --git a/Platform/Broadcom/Bcm283x/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/Platform/Broadcom/Bcm283x/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
new file mode 100644
index 000000000000..cb1d42364265
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
@@ -0,0 +1,90 @@
+#/** @file
+#
+#  Copyright (c) 2017-2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+#  Copyright (c) 2016, Linaro Ltd. All rights reserved.
+#  Copyright (c) 2015-2016, Red Hat, Inc.
+#  Copyright (c) 2014, ARM Ltd. All rights reserved.
+#  Copyright (c) 2007-2014, Intel 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                    = 0x00010005
+  BASE_NAME                      = PlatformBootManagerLib
+  FILE_GUID                      = 92FD2DE3-B9CB-4B35-8141-42AD34D73C9F
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = PlatformBootManagerLib|DXE_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = AARCH64
+#
+
+[Sources]
+  PlatformBm.c
+
+[Packages]
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  ShellPkg/ShellPkg.dec
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  BootLogoLib
+  CapsuleLib
+  DebugLib
+  DevicePathLib
+  DxeServicesLib
+  HobLib
+  MemoryAllocationLib
+  PcdLib
+  PrintLib
+  UefiBootManagerLib
+  UefiBootServicesTableLib
+  UefiLib
+
+[FeaturePcd]
+  gEfiMdePkgTokenSpaceGuid.PcdUgaConsumeSupport
+
+[FixedPcd]
+  gEfiMdePkgTokenSpaceGuid.PcdUartDefaultBaudRate
+  gEfiMdePkgTokenSpaceGuid.PcdUartDefaultDataBits
+  gEfiMdePkgTokenSpaceGuid.PcdUartDefaultParity
+  gEfiMdePkgTokenSpaceGuid.PcdUartDefaultStopBits
+  gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType
+
+[Pcd]
+  gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut
+  gRaspberryPiTokenSpaceGuid.PcdHypEnable
+  gRaspberryPiTokenSpaceGuid.PcdDebugShowUEFIExit
+
+[Guids]
+  gEfiFileInfoGuid
+  gEfiFileSystemInfoGuid
+  gEfiFileSystemVolumeLabelInfoIdGuid
+  gEfiEndOfDxeEventGroupGuid
+  gEfiTtyTermGuid
+  gUefiShellFileGuid
+  gEfiEventExitBootServicesGuid
+
+[Protocols]
+  gEfiDevicePathProtocolGuid
+  gEfiGraphicsOutputProtocolGuid
+  gEfiLoadedImageProtocolGuid
+  gEfiSimpleFileSystemProtocolGuid
+  gEsrtManagementProtocolGuid
+  gEfiUsb2HcProtocolGuid
+  gEfiBootLogoProtocolGuid
diff --git a/Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/AArch64/RaspberryPiHelper.S b/Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/AArch64/RaspberryPiHelper.S
new file mode 100644
index 000000000000..91567de8bb78
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/AArch64/RaspberryPiHelper.S
@@ -0,0 +1,107 @@
+/** @file
+ *
+ *  Copyright (c) 2016, Linaro Limited. All rights reserved.
+ *  Copyright (c) 2011-2013, 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 <AsmMacroIoLibV8.h>
+#include <Library/ArmLib.h>
+#include <IndustryStandard/Bcm2836.h>
+#include <IndustryStandard/RpiFirmware.h>
+
+#define MAX_TRIES     0x100000
+
+    .macro  drain
+    mov     x5, #MAX_TRIES
+0:  ldr     w6, [x4, #BCM2836_MBOX_STATUS_OFFSET]
+    tbnz    w6, #BCM2836_MBOX_STATUS_EMPTY, 1f
+    dmb     ld
+    ldr     wzr, [x4, #BCM2836_MBOX_READ_OFFSET]
+    subs    x5, x5, #1
+    b.ne    0b
+1:
+    .endm
+
+    .macro  poll, status
+    mov     x5, #MAX_TRIES
+0:  ldr     w6, [x4, #BCM2836_MBOX_STATUS_OFFSET]
+    tbz     w6, #\status, 1f
+    dmb     ld
+    subs    x5, x5, #1
+    b.ne    0b
+1:
+    .endm
+
+ASM_FUNC(ArmPlatformPeiBootAction)
+    adr     x0, .Lmeminfo_buffer
+    mov     x1, #FixedPcdGet64 (PcdDmaDeviceOffset)
+    orr     x0, x0, #RPI_FW_MBOX_CHANNEL
+    add     x0, x0, x1
+
+    MOV32   (x4, BCM2836_MBOX_BASE_ADDRESS)
+
+    drain
+    poll    BCM2836_MBOX_STATUS_FULL
+    str     w0, [x4, #BCM2836_MBOX_WRITE_OFFSET]
+    dmb     sy
+    poll    BCM2836_MBOX_STATUS_EMPTY
+    dmb     sy
+    ldr     wzr, [x4, #BCM2836_MBOX_READ_OFFSET]
+    dmb     ld
+
+    ldr     w0, .Lmemsize
+    sub     x0, x0, #1
+    adr     x1, mSystemMemoryEnd
+    str     x0, [x1]
+    ret
+
+    .align  4
+.Lmeminfo_buffer:
+    .long   .Lbuffer_size
+    .long   0x0
+    .long   RPI_FW_GET_ARM_MEMSIZE
+    .long   8                           // buf size
+    .long   0                           // input len
+    .long   0                           // mem base
+.Lmemsize:
+    .long   0                           // mem size
+    .long   0                           // end tag
+    .set    .Lbuffer_size, . - .Lmeminfo_buffer
+
+//UINTN
+//ArmPlatformGetPrimaryCoreMpId (
+//  VOID
+//  );
+ASM_FUNC(ArmPlatformGetPrimaryCoreMpId)
+    MOV32  (w0, FixedPcdGet32 (PcdArmPrimaryCore))
+    ret
+
+//UINTN
+//ArmPlatformIsPrimaryCore (
+//  IN UINTN MpId
+//  );
+ASM_FUNC(ArmPlatformIsPrimaryCore)
+    mov   x0, #1
+    ret
+
+//UINTN
+//ArmPlatformGetCorePosition (
+//  IN UINTN MpId
+//  );
+// With this function: CorePos = (ClusterId * 4) + CoreId
+ASM_FUNC(ArmPlatformGetCorePosition)
+    and   x1, x0, #ARM_CORE_MASK
+    and   x0, x0, #ARM_CLUSTER_MASK
+    add   x0, x1, x0, LSR #6
+    ret
+
+ASM_FUNCTION_REMOVE_IF_UNREFERENCED
diff --git a/Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/RaspberryPi.c b/Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/RaspberryPi.c
new file mode 100644
index 000000000000..ae0b7680f3c3
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/RaspberryPi.c
@@ -0,0 +1,99 @@
+/** @file
+ *
+ *  Copyright (c) 2014-2016, Linaro Limited. All rights reserved.
+ *  Copyright (c) 2014, Red Hat, Inc.
+ *  Copyright (c) 2011-2013, 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 <Library/IoLib.h>
+#include <Library/ArmPlatformLib.h>
+#include <Library/DebugLib.h>
+#include <Pi/PiBootMode.h>
+
+#include <Ppi/ArmMpCoreInfo.h>
+
+/**
+  Return the current Boot Mode
+
+  This function returns the boot reason on the platform
+
+  @return   Return the current Boot Mode of the platform
+
+**/
+EFI_BOOT_MODE
+ArmPlatformGetBootMode (
+  VOID
+  )
+{
+  return BOOT_WITH_FULL_CONFIGURATION;
+}
+
+/**
+  This function is called by PrePeiCore, in the SEC phase.
+**/
+RETURN_STATUS
+ArmPlatformInitialize (
+  IN  UINTN                     MpId
+  )
+{
+  return RETURN_SUCCESS;
+}
+
+VOID
+ArmPlatformInitializeSystemMemory (
+  VOID
+  )
+{
+}
+
+STATIC ARM_CORE_INFO mRpi3InfoTable[] = {
+  { 0x0, 0x0, },             // Cluster 0, Core 0
+  { 0x0, 0x1, },             // Cluster 0, Core 1
+  { 0x0, 0x2, },             // Cluster 0, Core 2
+  { 0x0, 0x3, },             // Cluster 0, Core 3
+};
+
+STATIC
+EFI_STATUS
+PrePeiCoreGetMpCoreInfo (
+  OUT UINTN                   *CoreCount,
+  OUT ARM_CORE_INFO           **ArmCoreTable
+  )
+{
+  // Only support one cluster
+  *CoreCount    = sizeof(mRpi3InfoTable) / sizeof(ARM_CORE_INFO);
+  *ArmCoreTable = mRpi3InfoTable;
+
+  return EFI_SUCCESS;
+}
+
+STATIC ARM_MP_CORE_INFO_PPI     mMpCoreInfoPpi = {
+  PrePeiCoreGetMpCoreInfo
+};
+STATIC EFI_PEI_PPI_DESCRIPTOR   mPlatformPpiTable[] = {
+  {
+    EFI_PEI_PPI_DESCRIPTOR_PPI,
+    &gArmMpCoreInfoPpiGuid,
+    &mMpCoreInfoPpi
+  }
+};
+
+VOID
+ArmPlatformGetPlatformPpiList (
+  OUT UINTN                   *PpiListSize,
+  OUT EFI_PEI_PPI_DESCRIPTOR  **PpiList
+  )
+{
+  *PpiListSize = sizeof(mPlatformPpiTable);
+  *PpiList = mPlatformPpiTable;
+}
diff --git a/Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/RaspberryPiMem.c b/Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/RaspberryPiMem.c
new file mode 100644
index 000000000000..69821e405134
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/RaspberryPiMem.c
@@ -0,0 +1,160 @@
+/** @file
+ *
+ *  Copyright (c) 2017-2018, Andrey Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2014, Linaro 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 <Library/ArmPlatformLib.h>
+#include <Library/DebugLib.h>
+#include <IndustryStandard/Bcm2836.h>
+#include <Library/PcdLib.h>
+
+extern UINT64 mSystemMemoryEnd;
+extern UINT64 mGPUMemoryBase;
+extern UINT64 mGPUMemoryLength;
+
+#define VariablesSize (FixedPcdGet32(PcdFlashNvStorageVariableSize) +   \
+                       FixedPcdGet32(PcdFlashNvStorageFtwWorkingSize) + \
+                       FixedPcdGet32(PcdFlashNvStorageFtwSpareSize) +  \
+                       FixedPcdGet32(PcdNvStorageEventLogSize))
+
+#define VariablesBase (FixedPcdGet64(PcdFdBaseAddress) + \
+                       FixedPcdGet32(PcdFdSize) - \
+                       VariablesSize)
+
+#define ATFBase (FixedPcdGet64(PcdFdBaseAddress) + FixedPcdGet32(PcdFdSize))
+
+STATIC ARM_MEMORY_REGION_DESCRIPTOR RaspberryPiMemoryRegionDescriptor[] = {
+  {
+    /* Firmware Volume. */
+    FixedPcdGet64(PcdFdBaseAddress), FixedPcdGet64(PcdFdBaseAddress),
+    FixedPcdGet32(PcdFdSize) - VariablesSize,
+    ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK
+  },
+  {
+    /* Variables Volume. */
+    VariablesBase, VariablesBase,
+    VariablesSize,
+    ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK
+  },
+  {
+    /* ATF reserved RAM. */
+    ATFBase, ATFBase,
+    FixedPcdGet64(PcdSystemMemoryBase) - ATFBase,
+    ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK
+  },
+  {
+    /* System RAM. */
+    FixedPcdGet64(PcdSystemMemoryBase), FixedPcdGet64(PcdSystemMemoryBase),
+    0,
+    ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK
+  },
+  {
+    /* Reserved GPU RAM. */
+    0,
+    0,
+    0,
+    ARM_MEMORY_REGION_ATTRIBUTE_DEVICE
+  },
+  {
+    /* SOC registers. */
+    BCM2836_SOC_REGISTERS,
+    BCM2836_SOC_REGISTERS,
+    BCM2836_SOC_REGISTER_LENGTH,
+    ARM_MEMORY_REGION_ATTRIBUTE_DEVICE
+  },
+  {
+  }
+};
+
+/**
+  Return the Virtual Memory Map of your platform
+
+  This Virtual Memory Map is used by MemoryInitPei Module to initialize the MMU
+  on your platform.
+
+  @param[out]   VirtualMemoryMap    Array of ARM_MEMORY_REGION_DESCRIPTOR
+                                    describing a Physical-to-Virtual Memory
+                                    mapping. This array must be ended by a
+                                    zero-filled entry
+
+**/
+VOID
+ArmPlatformGetVirtualMemoryMap (
+  IN ARM_MEMORY_REGION_DESCRIPTOR** VirtualMemoryMap
+  )
+{
+  RaspberryPiMemoryRegionDescriptor[3].Length = mSystemMemoryEnd + 1 -
+    FixedPcdGet64(PcdSystemMemoryBase);
+
+  RaspberryPiMemoryRegionDescriptor[4].PhysicalBase =
+    RaspberryPiMemoryRegionDescriptor[3].PhysicalBase +
+    RaspberryPiMemoryRegionDescriptor[3].Length;
+
+  RaspberryPiMemoryRegionDescriptor[4].VirtualBase =
+    RaspberryPiMemoryRegionDescriptor[4].PhysicalBase;
+
+  RaspberryPiMemoryRegionDescriptor[4].Length =
+    RaspberryPiMemoryRegionDescriptor[5].PhysicalBase -
+    RaspberryPiMemoryRegionDescriptor[4].PhysicalBase;
+
+  DEBUG ((DEBUG_INFO, "FD:\n"
+          "\tPhysicalBase: 0x%lX\n"
+          "\tVirtualBase: 0x%lX\n"
+          "\tLength: 0x%lX\n",
+          RaspberryPiMemoryRegionDescriptor[0].PhysicalBase,
+          RaspberryPiMemoryRegionDescriptor[0].VirtualBase,
+          RaspberryPiMemoryRegionDescriptor[0].Length +
+          RaspberryPiMemoryRegionDescriptor[1].Length));
+
+  DEBUG ((DEBUG_INFO, "Variables (part of FD):\n"
+          "\tPhysicalBase: 0x%lX\n"
+          "\tVirtualBase: 0x%lX\n"
+          "\tLength: 0x%lX\n",
+          RaspberryPiMemoryRegionDescriptor[1].PhysicalBase,
+          RaspberryPiMemoryRegionDescriptor[1].VirtualBase,
+          RaspberryPiMemoryRegionDescriptor[1].Length));
+
+  DEBUG ((DEBUG_INFO, "ATF RAM:\n"
+          "\tPhysicalBase: 0x%lX\n"
+          "\tVirtualBase: 0x%lX\n"
+          "\tLength: 0x%lX\n",
+          RaspberryPiMemoryRegionDescriptor[2].PhysicalBase,
+          RaspberryPiMemoryRegionDescriptor[2].VirtualBase,
+          RaspberryPiMemoryRegionDescriptor[2].Length));
+
+  DEBUG ((DEBUG_INFO, "System RAM:\n"
+          "\tPhysicalBase: 0x%lX\n"
+          "\tVirtualBase: 0x%lX\n"
+          "\tLength: 0x%lX\n",
+          RaspberryPiMemoryRegionDescriptor[3].PhysicalBase,
+          RaspberryPiMemoryRegionDescriptor[3].VirtualBase,
+          RaspberryPiMemoryRegionDescriptor[3].Length));
+
+  DEBUG ((DEBUG_INFO, "GPU Reserved:\n"
+          "\tPhysicalBase: 0x%lX\n"
+          "\tVirtualBase: 0x%lX\n"
+          "\tLength: 0x%lX\n",
+          RaspberryPiMemoryRegionDescriptor[4].PhysicalBase,
+          RaspberryPiMemoryRegionDescriptor[4].VirtualBase,
+          RaspberryPiMemoryRegionDescriptor[4].Length));
+
+  DEBUG ((DEBUG_INFO, "SoC reserved:\n"
+          "\tPhysicalBase: 0x%lX\n"
+          "\tVirtualBase: 0x%lX\n"
+          "\tLength: 0x%lX\n",
+          RaspberryPiMemoryRegionDescriptor[5].PhysicalBase,
+          RaspberryPiMemoryRegionDescriptor[5].VirtualBase,
+          RaspberryPiMemoryRegionDescriptor[5].Length));
+
+  *VirtualMemoryMap = RaspberryPiMemoryRegionDescriptor;
+}
diff --git a/Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/RaspberryPiPlatformLib.inf b/Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/RaspberryPiPlatformLib.inf
new file mode 100644
index 000000000000..2ee4450d79e4
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/RaspberryPiPlatformLib.inf
@@ -0,0 +1,64 @@
+#/** @file
+#
+#  Copyright (c) 2017-2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+#  Copyright (c) 2014-2016, Linaro Limited. 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.
+#
+#**/
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = RaspberryPiPlatformLib
+  FILE_GUID                      = 050182ef-f708-4148-b4bc-256cabf74fea
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = ArmPlatformLib|SEC PEIM
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  ArmPkg/ArmPkg.dec
+  ArmPlatformPkg/ArmPlatformPkg.dec
+ Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+
+[LibraryClasses]
+  ArmLib
+  FdtLib
+  IoLib
+  MemoryAllocationLib
+  PcdLib
+  PrintLib
+
+[Sources.common]
+  RaspberryPi.c
+  RaspberryPiMem.c
+
+[Sources.AARCH64]
+  AArch64/RaspberryPiHelper.S
+
+[FixedPcd]
+  gArmTokenSpaceGuid.PcdFdBaseAddress
+  gArmTokenSpaceGuid.PcdFvBaseAddress
+  gArmPlatformTokenSpaceGuid.PcdCoreCount
+  gArmTokenSpaceGuid.PcdArmPrimaryCoreMask
+  gArmTokenSpaceGuid.PcdArmPrimaryCore
+  gArmTokenSpaceGuid.PcdFdSize
+  gEmbeddedTokenSpaceGuid.PcdDmaDeviceOffset
+  gArmTokenSpaceGuid.PcdSystemMemoryBase
+  gArmTokenSpaceGuid.PcdSystemMemorySize
+  gRaspberryPiTokenSpaceGuid.PcdNvStorageEventLogSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
+
+[Ppis]
+  gArmMpCoreInfoPpiGuid
diff --git a/Platform/Broadcom/Bcm283x/Library/ResetLib/ResetLib.c b/Platform/Broadcom/Bcm283x/Library/ResetLib/ResetLib.c
new file mode 100644
index 000000000000..1a3944b71d03
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Library/ResetLib/ResetLib.c
@@ -0,0 +1,104 @@
+/** @file
+ *
+ *  Support ResetSystem Runtime call using PSCI calls.
+ *  Signals the gRaspberryPiEventResetGuid event group on reset.
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 2014, Linaro Ltd. All rights reserved.
+ *  Copyright (c) 2013-2015, ARM Ltd. All rights reserved.
+ *  Copyright (c) 2008-2009, Apple Inc. 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 <PiDxe.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/EfiResetSystemLib.h>
+#include <Library/ArmSmcLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiRuntimeLib.h>
+
+#include <IndustryStandard/ArmStdSmc.h>
+
+/**
+  Resets the entire platform.
+
+  @param  ResetType             The type of reset to perform.
+  @param  ResetStatus           The status code for the reset.
+  @param  DataSize              The size, in bytes, of WatchdogData.
+  @param  ResetData             For a ResetType of EfiResetCold, EfiResetWarm, or
+                                EfiResetShutdown the data buffer starts with a Null-terminated
+                                Unicode string, optionally followed by additional binary data.
+
+**/
+EFI_STATUS
+EFIAPI
+LibResetSystem (
+  IN EFI_RESET_TYPE   ResetType,
+  IN EFI_STATUS       ResetStatus,
+  IN UINTN            DataSize,
+  IN CHAR16           *ResetData OPTIONAL
+  )
+{
+  ARM_SMC_ARGS ArmSmcArgs;
+
+  if (!EfiAtRuntime ()) {
+    /*
+     * Only if still in UEFI.
+     */
+    EfiEventGroupSignal(&gRaspberryPiEventResetGuid);
+  }
+
+  switch (ResetType) {
+  case EfiResetPlatformSpecific:
+    // Map the platform specific reset as reboot
+  case EfiResetWarm:
+    // Map a warm reset into a cold reset
+  case EfiResetCold:
+    // Send a PSCI 0.2 SYSTEM_RESET command
+    ArmSmcArgs.Arg0 = ARM_SMC_ID_PSCI_SYSTEM_RESET;
+    break;
+  case EfiResetShutdown:
+    // Send a PSCI 0.2 SYSTEM_OFF command
+    ArmSmcArgs.Arg0 = ARM_SMC_ID_PSCI_SYSTEM_OFF;
+    break;
+  default:
+    ASSERT (FALSE);
+    return EFI_UNSUPPORTED;
+  }
+
+  ArmCallSmc (&ArmSmcArgs);
+
+  // We should never be here
+  DEBUG ((DEBUG_ERROR, "%a: PSCI Reset failed\n", __FUNCTION__));
+  CpuDeadLoop ();
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Initialize any infrastructure required for LibResetSystem () to function.
+
+  @param  ImageHandle   The firmware allocated handle for the EFI image.
+  @param  SystemTable   A pointer to the EFI System Table.
+
+  @retval EFI_SUCCESS   The constructor always returns EFI_SUCCESS.
+
+**/
+EFI_STATUS
+EFIAPI
+LibInitializeResetSystem (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  return EFI_SUCCESS;
+}
diff --git a/Platform/Broadcom/Bcm283x/Library/ResetLib/ResetLib.inf b/Platform/Broadcom/Bcm283x/Library/ResetLib/ResetLib.inf
new file mode 100644
index 000000000000..1c32a4b08162
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Library/ResetLib/ResetLib.inf
@@ -0,0 +1,46 @@
+#/** @file
+#
+#  Reset System lib using PSCI hypervisor or secure monitor calls.
+#  Signals the gRaspberryPiEventResetGuid event group on reset.
+#
+#  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+#  Copyright (c) 2014, Linaro Ltd. All rights reserved.
+#  Copyright (c) 2014, ARM Ltd. All rights reserved.
+#  Copyright (c) 2008, Apple Inc. 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                    = 0x00010005
+  BASE_NAME                      = ResetLib
+  FILE_GUID                      = B9F59B69-A105-41C7-8F5A-2C60DD7FD7AB
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = EfiResetSystemLib
+
+[Sources]
+  ResetLib.c
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  MdePkg/MdePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+
+[LibraryClasses]
+  DebugLib
+  BaseLib
+  ArmSmcLib
+  UefiLib
+  UefiRuntimeLib
+
+[Guids]
+  gRaspberryPiEventResetGuid
diff --git a/Platform/Broadcom/Bcm283x/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c b/Platform/Broadcom/Bcm283x/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c
new file mode 100644
index 000000000000..5e1dd768331a
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.c
@@ -0,0 +1,222 @@
+/** @file
+ *
+ *  Implement dummy EFI RealTimeClock runtime services.
+ *
+ *  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  Copyright (c) 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 <PiDxe.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/RealTimeClockLib.h>
+#include <Library/TimerLib.h>
+#include <Library/TimeBaseLib.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Library/ArmGenericTimerCounterLib.h>
+
+/**
+   Returns the current time and date information, and the time-keeping capabilities
+   of the virtual RTC.
+
+   For simplicity, this LibGetTime does not report Years/Months, instead it will only report current
+   Day, Hours, Minutes and Seconds starting from the begining of CPU up-time. Otherwise, a more
+   complex logic will be required to account for leap years and days/month differences.
+
+   @param  Time                  A pointer to storage to receive a snapshot of the current time.
+   @param  Capabilities          An optional pointer to a buffer to receive the real time clock
+   device's capabilities.
+
+   @retval EFI_SUCCESS           The operation completed successfully.
+   @retval EFI_INVALID_PARAMETER Time is NULL.
+   @retval EFI_DEVICE_ERROR      The time could not be retrieved due to hardware error.
+
+**/
+EFI_STATUS
+EFIAPI
+LibGetTime (
+            OUT EFI_TIME                *Time,
+            OUT  EFI_TIME_CAPABILITIES  *Capabilities
+            )
+{
+  UINTN DataSize;
+  UINT64 Counter;
+  EFI_STATUS Status;
+  UINTN ElapsedSeconds;
+  UINT32 Remainder;
+  UINT32 Freq = ArmGenericTimerGetTimerFreq();
+
+  if (Time == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Depend on ARM generic timer to report date/time relative to the
+  // start of CPU timer counting where date and time will always
+  // be relative to the date/time 1/1/1900 00H:00M:00S
+  //
+
+  ASSERT (Freq != 0);
+  if (Freq == 0) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (Capabilities) {
+    Capabilities->Accuracy = 0;
+    Capabilities->Resolution = Freq;
+    Capabilities->SetsToZero = FALSE;
+  }
+
+  DataSize = sizeof (UINTN);
+  ElapsedSeconds = 0;
+  Status = EfiGetVariable (L"RtcEpochSeconds",
+                           &gEfiCallerIdGuid,
+                           NULL,
+                           &DataSize,
+                           &ElapsedSeconds);
+  if (EFI_ERROR (Status)) {
+    ElapsedSeconds = PcdGet64(PcdBootEpochSeconds);
+  }
+  Counter = GetPerformanceCounter ();
+  ElapsedSeconds += DivU64x32Remainder (Counter, Freq, &Remainder);
+  EpochToEfiTime (ElapsedSeconds, Time);
+
+  //
+  // Frequency < 0x100000000, so Remainder < 0x100000000, then (Remainder * 1,000,000,000)
+  // will not overflow 64-bit.
+  //
+  Time->Nanosecond = DivU64x32 (MultU64x64 ((UINT64) Remainder,
+                                            1000000000U), Freq);
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+   Sets the current local time and date information.
+
+   @param  Time                  A pointer to the current time.
+
+   @retval EFI_SUCCESS           The operation completed successfully.
+   @retval EFI_INVALID_PARAMETER A time field is out of range.
+   @retval EFI_DEVICE_ERROR      The time could not be set due due to hardware error.
+
+**/
+EFI_STATUS
+EFIAPI
+LibSetTime (
+            IN EFI_TIME *Time
+            )
+{
+  UINTN Epoch;
+
+  if (!IsTimeValid(Time)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Epoch = EfiTimeToEpoch(Time);
+  return EfiSetVariable(L"RtcEpochSeconds", &gEfiCallerIdGuid,
+                        EFI_VARIABLE_BOOTSERVICE_ACCESS |
+                        EFI_VARIABLE_RUNTIME_ACCESS |
+                        EFI_VARIABLE_NON_VOLATILE,
+                        sizeof (Epoch),
+                        &Epoch);
+}
+
+
+/**
+   Returns the current wakeup alarm clock setting.
+
+   @param  Enabled               Indicates if the alarm is currently enabled or disabled.
+   @param  Pending               Indicates if the alarm signal is pending and requires acknowledgement.
+   @param  Time                  The current alarm setting.
+
+   @retval EFI_SUCCESS           The alarm settings were returned.
+   @retval EFI_INVALID_PARAMETER Any parameter is NULL.
+   @retval EFI_DEVICE_ERROR      The wakeup time could not be retrieved due to a hardware error.
+
+**/
+EFI_STATUS
+EFIAPI
+LibGetWakeupTime (
+                  OUT BOOLEAN     *Enabled,
+                  OUT BOOLEAN     *Pending,
+                  OUT EFI_TIME    *Time
+                  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+
+/**
+   Sets the system wakeup alarm clock time.
+
+   @param  Enabled               Enable or disable the wakeup alarm.
+   @param  Time                  If Enable is TRUE, the time to set the wakeup alarm for.
+
+   @retval EFI_SUCCESS           If Enable is TRUE, then the wakeup alarm was enabled. If
+   Enable is FALSE, then the wakeup alarm was disabled.
+   @retval EFI_INVALID_PARAMETER A time field is out of range.
+   @retval EFI_DEVICE_ERROR      The wakeup time could not be set due to a hardware error.
+   @retval EFI_UNSUPPORTED       A wakeup timer is not supported on this platform.
+
+**/
+EFI_STATUS
+EFIAPI
+LibSetWakeupTime (
+                  IN BOOLEAN      Enabled,
+                  OUT EFI_TIME    *Time
+                  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+
+/**
+   This is the declaration of an EFI image entry point. This can be the entry point to an application
+   written to this specification, an EFI boot service driver, or an EFI runtime driver.
+
+   @param  ImageHandle           Handle that identifies the loaded image.
+   @param  SystemTable           System Table for this image.
+
+   @retval EFI_SUCCESS           The operation completed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+LibRtcInitialize (
+                  IN EFI_HANDLE                            ImageHandle,
+                  IN EFI_SYSTEM_TABLE                      *SystemTable
+                  )
+{
+  return EFI_SUCCESS;
+}
+
+
+/**
+   Fixup internal data so that EFI can be call in virtual mode.
+   Call the passed in Child Notify event and convert any pointers in
+   lib to virtual mode.
+
+   @param[in]    Event   The Event that is being processed
+   @param[in]    Context Event Context
+**/
+VOID
+EFIAPI
+LibRtcVirtualNotifyEvent (
+                          IN EFI_EVENT        Event,
+                          IN VOID             *Context
+                          )
+{
+  return;
+}
diff --git a/Platform/Broadcom/Bcm283x/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf b/Platform/Broadcom/Bcm283x/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf
new file mode 100644
index 000000000000..847bcfadd824
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf
@@ -0,0 +1,43 @@
+#/** @file
+#
+#  Implement dummy EFI RealTimeClock runtime services.
+#
+#  Copyright (c) 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+#  Copyright (c) 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                    = 0x00010005
+  BASE_NAME                      = VirtualRealTimeClockLib
+  FILE_GUID                      = 1E27D461-78F3-4F7D-B1C2-F72384F13A6E
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = RealTimeClockLib
+
+[Sources.common]
+  VirtualRealTimeClockLib.c
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  MdePkg/MdePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
+
+[LibraryClasses]
+  IoLib
+  DebugLib
+  TimerLib
+  TimeBaseLib
+  UefiRuntimeLib
+
+[Pcd]
+  gRaspberryPiTokenSpaceGuid.PcdBootEpochSeconds
diff --git a/Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec b/Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
new file mode 100644
index 000000000000..51f2f07aab8a
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec
@@ -0,0 +1,63 @@
+## @file
+#
+#  Copyright (c) 2016, Linaro, Ltd. All rights reserved.
+#  Copyright (c) 2017 - 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+#
+#  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]
+  DEC_SPECIFICATION              = 0x0001001A
+  PACKAGE_NAME                   = RaspberryPiPkg
+  PACKAGE_GUID                   = DFA0CA8B-F3AC-4607-96AC-46FA04B84DCC
+  PACKAGE_VERSION                = 0.1
+
+[Includes]
+  Include
+
+[Protocols]
+  gRaspberryPiFirmwareProtocolGuid = { 0x0ACA9535, 0x7AD0, 0x4286, { 0xB0, 0x2E, 0x87, 0xFA, 0x7E, 0x2A, 0x57, 0x11 } }
+  gRaspberryPiConfigAppliedProtocolGuid = { 0x0ACA4444, 0x7AD0, 0x4286, { 0xB0, 0x2E, 0x87, 0xFA, 0x7E, 0x2A, 0x57, 0x11 } }
+  gRaspberryPiMmcHostProtocolGuid = { 0x3e591c00, 0x9e4a, 0x11df, {0x92, 0x44, 0x00, 0x02, 0xA5, 0xF5, 0xF5, 0x1B } }
+  gExtendedTextOutputProtocolGuid = { 0x387477ff, 0xffc7, 0xffd2, {0x8e, 0x39, 0x0, 0xff, 0xc9, 0x69, 0x72, 0x3b } }
+
+[Guids]
+  gRaspberryPiTokenSpaceGuid = {0xCD7CC258, 0x31DB, 0x11E6, {0x9F, 0xD3, 0x63, 0xB0, 0xB8, 0xEE, 0xD6, 0xB5}}
+  gRaspberryPiFdtFileGuid = {0xDF5DA223, 0x1D27, 0x47C3, { 0x8D, 0x1B, 0x9A, 0x41, 0xB5, 0x5A, 0x18, 0xBC}}
+  gRaspberryPiEventResetGuid = {0xCD7CC258, 0x31DB, 0x11E6, {0x9F, 0xD3, 0x63, 0xB4, 0xB4, 0xE4, 0xD4, 0xB4}}
+  gConfigDxeFormSetGuid = {0xCD7CC258, 0x31DB, 0x22E6, {0x9F, 0x22, 0x63, 0xB0, 0xB8, 0xEE, 0xD6, 0xB5}}
+
+[PcdsFixedAtBuild.common]
+  gRaspberryPiTokenSpaceGuid.PcdFdtBaseAddress|0x10000|UINT32|0x00000001
+  gRaspberryPiTokenSpaceGuid.PcdFirmwareBlockSize|0x0|UINT32|0x00000002
+  gRaspberryPiTokenSpaceGuid.PcdNvStorageEventLogBase|0x0|UINT32|0x00000003
+  gRaspberryPiTokenSpaceGuid.PcdNvStorageEventLogSize|0x0|UINT32|0x00000004
+  gRaspberryPiTokenSpaceGuid.PcdNvStorageVariableBase|0x0|UINT32|0x00000005
+  gRaspberryPiTokenSpaceGuid.PcdNvStorageFtwSpareBase|0x0|UINT32|0x00000006
+  gRaspberryPiTokenSpaceGuid.PcdNvStorageFtwWorkingBase|0x0|UINT32|0x00000007
+  gRaspberryPiTokenSpaceGuid.PcdBootEpochSeconds|0x0|UINT64|0x00000008
+
+[PcdsFixedAtBuild, PcdsPatchableInModule, PcdsDynamic, PcdsDynamicEx]
+  gRaspberryPiTokenSpaceGuid.PcdHypEnable|0|UINT32|0x00000009
+  gRaspberryPiTokenSpaceGuid.PcdHypLogMask|0|UINT32|0x0000000a
+  gRaspberryPiTokenSpaceGuid.PcdHypWindowsDebugHook|0|UINT32|0x0000000b
+  gRaspberryPiTokenSpaceGuid.PcdHypWin2000Mask|0|UINT32|0x0000000c
+  gRaspberryPiTokenSpaceGuid.PcdCpuClock|0|UINT32|0x0000000d
+  gRaspberryPiTokenSpaceGuid.PcdSdIsArasan|0|UINT32|0x0000000e
+  gRaspberryPiTokenSpaceGuid.PcdMmcForce1Bit|0|UINT32|0x0000000f
+  gRaspberryPiTokenSpaceGuid.PcdMmcForceDefaultSpeed|0|UINT32|0x00000010
+  gRaspberryPiTokenSpaceGuid.PcdMmcSdDefaultSpeedMHz|0|UINT32|0x00000011
+  gRaspberryPiTokenSpaceGuid.PcdMmcSdHighSpeedMHz|0|UINT32|0x00000012
+  gRaspberryPiTokenSpaceGuid.PcdMmcDisableMulti|0|UINT32|0x00000013
+  gRaspberryPiTokenSpaceGuid.PcdDebugEnableJTAG|0|UINT32|0x00000014
+  gRaspberryPiTokenSpaceGuid.PcdDebugShowUEFIExit|0|UINT32|0x00000015
+  gRaspberryPiTokenSpaceGuid.PcdDisplayEnableVModes|0|UINT32|0x00000017
+  gRaspberryPiTokenSpaceGuid.PcdDisplayEnableSShot|0|UINT32|0x00000018
diff --git a/Platform/Broadcom/Bcm283x/RaspberryPiPkg.dsc b/Platform/Broadcom/Bcm283x/RaspberryPiPkg.dsc
new file mode 100644
index 000000000000..8610fae0b92f
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/RaspberryPiPkg.dsc
@@ -0,0 +1,636 @@
+# @file
+#
+#  Copyright (c) 2011-2015, ARM Limited. All rights reserved.
+#  Copyright (c) 2014, Linaro Limited. All rights reserved.
+#  Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+#  Copyright (c) 2017 - 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+#
+#  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 Section - statements that will be processed to create a Makefile.
+#
+################################################################################
+[Defines]
+  PLATFORM_NAME                  = RaspberryPi
+  PLATFORM_GUID                  = 5d30c4fc-93cf-40c9-8486-3badc0410816
+  PLATFORM_VERSION               = 0.3
+  DSC_SPECIFICATION              = 0x00010005
+  OUTPUT_DIRECTORY               = Build/RaspberryPiPkg
+  SUPPORTED_ARCHITECTURES        = AARCH64
+  BUILD_TARGETS                  = DEBUG|RELEASE
+  SKUID_IDENTIFIER               = DEFAULT
+  FLASH_DEFINITION               = Platform/Broadcom/Bcm283x/RaspberryPiPkg.fdf
+
+  #
+  # Defines for default states.  These can be changed on the command line.
+  # -D FLAG=VALUE
+  #
+  DEFINE SECURE_BOOT_ENABLE      = FALSE
+  DEFINE DEBUG_PRINT_ERROR_LEVEL = 0x8000004F
+
+!ifndef BUILD_EPOCH
+  DEFINE BUILD_EPOCH             = 1546300800
+!endif
+################################################################################
+#
+# Library Class section - list of all Library Classes needed by this Platform.
+#
+################################################################################
+[LibraryClasses.common]
+!if $(TARGET) == RELEASE
+  DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
+!else
+  DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
+!endif
+  DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
+
+  BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
+  SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf
+  BmpSupportLib|MdeModulePkg/Library/BaseBmpSupportLib/BaseBmpSupportLib.inf
+  SynchronizationLib|MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.inf
+  PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf
+  PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
+  PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
+  PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf
+  IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
+  UefiDecompressLib|MdePkg/Library/BaseUefiDecompressLib/BaseUefiDecompressLib.inf
+  CpuLib|MdePkg/Library/BaseCpuLib/BaseCpuLib.inf
+
+  UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
+  HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf
+  UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
+  DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
+  UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
+  DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf
+  DxeServicesLib|MdePkg/Library/DxeServicesLib/DxeServicesLib.inf
+  UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf
+  UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
+  HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf
+  UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf
+  SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf
+
+  UefiRuntimeLib|MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.inf
+  OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrderedCollectionRedBlackTreeLib.inf
+
+  #
+  # Ramdisk Requirements
+  #
+  FileExplorerLib|MdeModulePkg/Library/FileExplorerLib/FileExplorerLib.inf
+
+  # Allow dynamic PCDs
+  #
+  PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
+
+  # use the accelerated BaseMemoryLibOptDxe by default, overrides for SEC/PEI below
+  BaseMemoryLib|MdePkg/Library/BaseMemoryLibOptDxe/BaseMemoryLibOptDxe.inf
+
+  # Networking Requirements
+  NetLib|MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf
+  DpcLib|MdeModulePkg/Library/DxeDpcLib/DxeDpcLib.inf
+  UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf
+  IpIoLib|MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf
+
+  #
+  # It is not possible to prevent the ARM compiler from inserting calls to intrinsic functions.
+  # This library provides the instrinsic functions such a compiler may generate calls to.
+  #
+  NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf
+
+  # Add support for GCC stack protector
+  NULL|MdePkg/Library/BaseStackCheckLib/BaseStackCheckLib.inf
+
+  # ARM Architectural Libraries
+  CacheMaintenanceLib|ArmPkg/Library/ArmCacheMaintenanceLib/ArmCacheMaintenanceLib.inf
+  DefaultExceptionHandlerLib|ArmPkg/Library/DefaultExceptionHandlerLib/DefaultExceptionHandlerLib.inf
+  CpuExceptionHandlerLib|ArmPkg/Library/ArmExceptionLib/ArmExceptionLib.inf
+  ArmDisassemblerLib|ArmPkg/Library/ArmDisassemblerLib/ArmDisassemblerLib.inf
+  DmaLib|EmbeddedPkg/Library/NonCoherentDmaLib/NonCoherentDmaLib.inf
+  TimeBaseLib|EmbeddedPkg/Library/TimeBaseLib/TimeBaseLib.inf
+  ArmPlatformStackLib|ArmPlatformPkg/Library/ArmPlatformStackLib/ArmPlatformStackLib.inf
+  ArmSmcLib|ArmPkg/Library/ArmSmcLib/ArmSmcLib.inf
+  ArmHvcLib|ArmPkg/Library/ArmHvcLib/ArmHvcLib.inf
+  ArmGenericTimerCounterLib|ArmPkg/Library/ArmGenericTimerPhyCounterLib/ArmGenericTimerPhyCounterLib.inf
+
+  PciCf8Lib|MdePkg/Library/BasePciCf8Lib/BasePciCf8Lib.inf
+  PciLib|MdePkg/Library/BasePciLibCf8/BasePciLibCf8.inf
+  PlatformHookLib|MdeModulePkg/Library/BasePlatformHookLibNull/BasePlatformHookLibNull.inf
+  SerialPortLib|MdeModulePkg/Library/BaseSerialPortLib16550/BaseSerialPortLib16550.inf
+
+  #
+  # Uncomment (and comment out the next line) For RealView Debugger. The Standard IO window
+  # in the debugger will show load and unload commands for symbols. You can cut and paste this
+  # into the command window to load symbols. We should be able to use a script to do this, but
+  # the version of RVD I have does not support scripts accessing system memory.
+  #
+  #PeCoffExtraActionLib|ArmPkg/Library/RvdPeCoffExtraActionLib/RvdPeCoffExtraActionLib.inf
+  PeCoffExtraActionLib|ArmPkg/Library/DebugPeCoffExtraActionLib/DebugPeCoffExtraActionLib.inf
+  #PeCoffExtraActionLib|MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf
+
+  DebugAgentLib|MdeModulePkg/Library/DebugAgentLibNull/DebugAgentLibNull.inf
+  DebugAgentTimerLib|EmbeddedPkg/Library/DebugAgentTimerLibNull/DebugAgentTimerLibNull.inf
+
+  # Flattened Device Tree (FDT) access library
+  FdtLib|EmbeddedPkg/Library/FdtLib/FdtLib.inf
+
+  # USB Libraries
+  UefiUsbLib|MdePkg/Library/UefiUsbLib/UefiUsbLib.inf
+
+  #
+  # Secure Boot dependencies
+  #
+!if $(SECURE_BOOT_ENABLE) == TRUE
+  IntrinsicLib|CryptoPkg/Library/IntrinsicLib/IntrinsicLib.inf
+  OpensslLib|CryptoPkg/Library/OpensslLib/OpensslLib.inf
+  TpmMeasurementLib|SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf
+  AuthVariableLib|SecurityPkg/Library/AuthVariableLib/AuthVariableLib.inf
+  BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
+
+  # re-use the UserPhysicalPresent() dummy implementation from the ovmf tree
+  PlatformSecureLib|OvmfPkg/Library/PlatformSecureLib/PlatformSecureLib.inf
+!else
+  TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
+  AuthVariableLib|MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf
+!endif
+  VarCheckLib|MdeModulePkg/Library/VarCheckLib/VarCheckLib.inf
+  GpioLib|Platform/Broadcom/Bcm283x/Library/GpioLib/GpioLib.inf
+
+[LibraryClasses.common.SEC]
+  PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+  BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+  MemoryInitPeiLib|Platform/Broadcom/Bcm283x/Library/MemoryInitPeiLib/MemoryInitPeiLib.inf
+  PlatformPeiLib|ArmPlatformPkg/PlatformPei/PlatformPeiLib.inf
+  ExtractGuidedSectionLib|EmbeddedPkg/Library/PrePiExtractGuidedSectionLib/PrePiExtractGuidedSectionLib.inf
+  LzmaDecompressLib|MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf
+  PrePiLib|EmbeddedPkg/Library/PrePiLib/PrePiLib.inf
+  HobLib|EmbeddedPkg/Library/PrePiHobLib/PrePiHobLib.inf
+  PrePiHobListPointerLib|ArmPlatformPkg/Library/PrePiHobListPointerLib/PrePiHobListPointerLib.inf
+  MemoryAllocationLib|EmbeddedPkg/Library/PrePiMemoryAllocationLib/PrePiMemoryAllocationLib.inf
+
+[LibraryClasses.common.DXE_CORE]
+  HobLib|MdePkg/Library/DxeCoreHobLib/DxeCoreHobLib.inf
+  MemoryAllocationLib|MdeModulePkg/Library/DxeCoreMemoryAllocationLib/DxeCoreMemoryAllocationLib.inf
+  DxeCoreEntryPoint|MdePkg/Library/DxeCoreEntryPoint/DxeCoreEntryPoint.inf
+  ReportStatusCodeLib|IntelFrameworkModulePkg/Library/DxeReportStatusCodeLibFramework/DxeReportStatusCodeLib.inf
+  ExtractGuidedSectionLib|MdePkg/Library/DxeExtractGuidedSectionLib/DxeExtractGuidedSectionLib.inf
+  UefiDecompressLib|MdePkg/Library/BaseUefiDecompressLib/BaseUefiDecompressLib.inf
+  PerformanceLib|MdeModulePkg/Library/DxeCorePerformanceLib/DxeCorePerformanceLib.inf
+
+[LibraryClasses.common.DXE_DRIVER]
+  ReportStatusCodeLib|IntelFrameworkModulePkg/Library/DxeReportStatusCodeLibFramework/DxeReportStatusCodeLib.inf
+  SecurityManagementLib|MdeModulePkg/Library/DxeSecurityManagementLib/DxeSecurityManagementLib.inf
+  PerformanceLib|MdeModulePkg/Library/DxePerformanceLib/DxePerformanceLib.inf
+  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+
+[LibraryClasses.common.UEFI_APPLICATION]
+  UefiDecompressLib|IntelFrameworkModulePkg/Library/BaseUefiTianoCustomDecompressLib/BaseUefiTianoCustomDecompressLib.inf
+  PerformanceLib|MdeModulePkg/Library/DxePerformanceLib/DxePerformanceLib.inf
+  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf
+  ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf
+  FileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf
+  ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
+
+[LibraryClasses.common.UEFI_DRIVER]
+  ReportStatusCodeLib|IntelFrameworkModulePkg/Library/DxeReportStatusCodeLibFramework/DxeReportStatusCodeLib.inf
+  UefiDecompressLib|IntelFrameworkModulePkg/Library/BaseUefiTianoCustomDecompressLib/BaseUefiTianoCustomDecompressLib.inf
+  ExtractGuidedSectionLib|MdePkg/Library/DxeExtractGuidedSectionLib/DxeExtractGuidedSectionLib.inf
+  PerformanceLib|MdeModulePkg/Library/DxePerformanceLib/DxePerformanceLib.inf
+  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+
+[LibraryClasses.common.DXE_RUNTIME_DRIVER]
+  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  ReportStatusCodeLib|IntelFrameworkModulePkg/Library/DxeReportStatusCodeLibFramework/DxeReportStatusCodeLib.inf
+  CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf
+  EfiResetSystemLib|Platform/Broadcom/Bcm283x/Library/ResetLib/ResetLib.inf
+  ArmSmcLib|ArmPkg/Library/ArmSmcLib/ArmSmcLib.inf
+
+!if $(SECURE_BOOT_ENABLE) == TRUE
+  BaseCryptLib|CryptoPkg/Library/BaseCryptLib/RuntimeCryptLib.inf
+!endif
+
+###################################################################################################
+# BuildOptions Section - Define the module specific tool chain flags that should be used as
+#                        the default flags for a module. These flags are appended to any
+#                        standard flags that are defined by the build process.
+###################################################################################################
+
+[BuildOptions]
+  GCC:RELEASE_*_*_CC_FLAGS    = -DMDEPKG_NDEBUG -DNDEBUG
+
+[BuildOptions.common.EDKII.DXE_RUNTIME_DRIVER]
+  GCC:*_*_AARCH64_DLINK_FLAGS = -z common-page-size=0x10000
+
+################################################################################
+#
+# Pcd Section - list of all EDK II PCD Entries defined by this Platform
+#
+################################################################################
+
+[PcdsFeatureFlag.common]
+  # Use the Vector Table location in CpuDxe. We will not copy the Vector Table at PcdCpuVectorBaseAddress
+  gArmTokenSpaceGuid.PcdRelocateVectorTable|FALSE
+
+  gEmbeddedTokenSpaceGuid.PcdPrePiProduceMemoryTypeInformationHob|TRUE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdTurnOffUsbLegacySupport|TRUE
+
+  ## If TRUE, Graphics Output Protocol will be installed on virtual handle created by ConsplitterDxe.
+  #  It could be set FALSE to save size.
+  gEfiMdeModulePkgTokenSpaceGuid.PcdConOutGopSupport|TRUE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdConOutUgaSupport|FALSE
+
+[PcdsFixedAtBuild.common]
+  gEfiMdePkgTokenSpaceGuid.PcdMaximumUnicodeStringLength|1000000
+  gEfiMdePkgTokenSpaceGuid.PcdMaximumAsciiStringLength|1000000
+  gEfiMdePkgTokenSpaceGuid.PcdMaximumLinkedListLength|1000000
+  gEfiMdePkgTokenSpaceGuid.PcdSpinLockTimeout|10000000
+  gEfiMdePkgTokenSpaceGuid.PcdDebugClearMemoryValue|0xAF
+  gEfiMdePkgTokenSpaceGuid.PcdPerformanceLibraryPropertyMask|1
+  gEfiMdePkgTokenSpaceGuid.PcdPostCodePropertyMask|0
+  gEfiMdePkgTokenSpaceGuid.PcdUefiLibMaxPrintBufferSize|320
+
+  # DEBUG_ASSERT_ENABLED       0x01
+  # DEBUG_PRINT_ENABLED        0x02
+  # DEBUG_CODE_ENABLED         0x04
+  # CLEAR_MEMORY_ENABLED       0x08
+  # ASSERT_BREAKPOINT_ENABLED  0x10
+  # ASSERT_DEADLOOP_ENABLED    0x20
+!if $(TARGET) == RELEASE
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x21
+!else
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2f
+!endif
+
+  #  DEBUG_INIT      0x00000001  // Initialization
+  #  DEBUG_WARN      0x00000002  // Warnings
+  #  DEBUG_LOAD      0x00000004  // Load events
+  #  DEBUG_FS        0x00000008  // EFI File system
+  #  DEBUG_POOL      0x00000010  // Alloc & Free (pool)
+  #  DEBUG_PAGE      0x00000020  // Alloc & Free (page)
+  #  DEBUG_INFO      0x00000040  // Informational debug messages
+  #  DEBUG_DISPATCH  0x00000080  // PEI/DXE/SMM Dispatchers
+  #  DEBUG_VARIABLE  0x00000100  // Variable
+  #  DEBUG_BM        0x00000400  // Boot Manager
+  #  DEBUG_BLKIO     0x00001000  // BlkIo Driver
+  #  DEBUG_NET       0x00004000  // SNP Driver
+  #  DEBUG_UNDI      0x00010000  // UNDI Driver
+  #  DEBUG_LOADFILE  0x00020000  // LoadFile
+  #  DEBUG_EVENT     0x00080000  // Event messages
+  #  DEBUG_GCD       0x00100000  // Global Coherency Database changes
+  #  DEBUG_CACHE     0x00200000  // Memory range cachability changes
+  #  DEBUG_VERBOSE   0x00400000  // Detailed debug messages that may
+  #                              // significantly impact boot performance
+  #  DEBUG_ERROR     0x80000000  // Error
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|$(DEBUG_PRINT_ERROR_LEVEL)
+
+  gEfiMdePkgTokenSpaceGuid.PcdReportStatusCodePropertyMask|0x07
+
+  #
+  # Optional feature to help prevent EFI memory map fragments
+  # Turned on and off via: PcdPrePiProduceMemoryTypeInformationHob
+  # Values are in EFI Pages (4K). DXE Core will make sure that
+  # at least this much of each type of memory can be allocated
+  # from a single memory range. This way you only end up with
+  # maximum of two fragments for each type in the memory map
+  # (the memory used, and the free memory that was prereserved
+  # but not used).
+  #
+  gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiACPIReclaimMemory|0
+  gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiACPIMemoryNVS|0
+  gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiReservedMemoryType|0
+!if $(SECURE_BOOT_ENABLE) == TRUE
+  gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesData|600
+  gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesCode|400
+  gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiBootServicesCode|1500
+!else
+  gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesData|300
+  gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiRuntimeServicesCode|150
+  gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiBootServicesCode|1000
+!endif
+  gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiBootServicesData|12000
+  gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiLoaderCode|20
+  gEmbeddedTokenSpaceGuid.PcdMemoryTypeEfiLoaderData|0
+
+  gEmbeddedTokenSpaceGuid.PcdDmaDeviceOffset|0xc0000000
+
+!if $(SECURE_BOOT_ENABLE) == TRUE
+  # override the default values from SecurityPkg to ensure images from all sources are verified in secure boot
+  gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy|0x04
+  gEfiSecurityPkgTokenSpaceGuid.PcdFixedMediaImageVerificationPolicy|0x04
+  gEfiSecurityPkgTokenSpaceGuid.PcdRemovableMediaImageVerificationPolicy|0x04
+!endif
+
+  gEmbeddedTokenSpaceGuid.PcdInterruptBaseAddress|0x40000000
+  gArmTokenSpaceGuid.PcdArmArchTimerSecIntrNum|0x0
+  gArmTokenSpaceGuid.PcdArmArchTimerIntrNum|0x1
+  gArmTokenSpaceGuid.PcdArmArchTimerVirtIntrNum|0x2
+  gArmTokenSpaceGuid.PcdArmArchTimerHypIntrNum|0x3
+
+[LibraryClasses.common]
+  ArmLib|ArmPkg/Library/ArmLib/ArmBaseLib.inf
+  ArmMmuLib|ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf
+  ArmPlatformLib|Platform/Broadcom/Bcm283x/Library/RaspberryPiPlatformLib/RaspberryPiPlatformLib.inf
+  TimerLib|ArmPkg/Library/ArmArchTimerLib/ArmArchTimerLib.inf
+  CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf
+  UefiBootManagerLib|MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf
+  BootLogoLib|MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf
+  PlatformBootManagerLib|Platform/Broadcom/Bcm283x/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
+  CustomizedDisplayLib|MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf
+  FileExplorerLib|MdeModulePkg/Library/FileExplorerLib/FileExplorerLib.inf
+
+[LibraryClasses.common.UEFI_DRIVER]
+  UefiScsiLib|MdePkg/Library/UefiScsiLib/UefiScsiLib.inf
+
+
+################################################################################
+#
+# Pcd Section - list of all EDK II PCD Entries defined by this Platform
+#
+################################################################################
+
+[PcdsFeatureFlag.common]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdConOutGopSupport|TRUE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdConOutUgaSupport|FALSE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdInstallAcpiSdtProtocol|TRUE
+
+[PcdsFixedAtBuild.common]
+  gArmPlatformTokenSpaceGuid.PcdCoreCount|4
+  gArmTokenSpaceGuid.PcdVFPEnabled|1
+
+  gArmPlatformTokenSpaceGuid.PcdCPUCorePrimaryStackSize|0x4000
+  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize|0x2000
+  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize|0x2800
+
+  # Size of the region used by UEFI in permanent memory (Reserved 64MB)
+  gArmPlatformTokenSpaceGuid.PcdSystemMemoryUefiRegionSize|0x04000000
+  #
+  # This matches PcdFvBaseAddress, since everything less is ATF, and
+  # will be reserved away.
+  #
+  gArmTokenSpaceGuid.PcdSystemMemoryBase|0x00400000
+  gArmTokenSpaceGuid.PcdSystemMemorySize|0x3FC00000
+
+  ## NS16550 compatible UART
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterBase|0x3f215040
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialUseMmio|TRUE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterStride|4
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialClockRate|500000000
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialFifoControl|0x27
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialExtendedTxFifoSize|8
+  gEfiMdePkgTokenSpaceGuid.PcdUartDefaultBaudRate|115200
+
+  ## Default Terminal Type
+  ## 0-PCANSI, 1-VT100, 2-VT00+, 3-UTF8, 4-TTYTERM
+  gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType|4
+
+  gEfiMdeModulePkgTokenSpaceGuid.PcdResetOnMemoryTypeInformationChange|FALSE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdBootManagerMenuFile|{ 0x21, 0xaa, 0x2c, 0x46, 0x14, 0x76, 0x03, 0x45, 0x83, 0x6e, 0x8a, 0xb6, 0xf4, 0x66, 0x23, 0x31 }
+  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdShellFile|{ 0x83, 0xA5, 0x04, 0x7C, 0x3E, 0x9E, 0x1C, 0x4F, 0xAD, 0x65, 0xE0, 0x52, 0x68, 0xD0, 0xB4, 0xD1 }
+
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFirmwareVendor|L"Raspberry Pi 3 64-bit UEFI"
+
+  #
+  # Build timestamp. This is used if RtcEpochSeconds NVRAM
+  # variable is not present.
+  #
+  gRaspberryPiTokenSpaceGuid.PcdBootEpochSeconds|$(BUILD_EPOCH)
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack|TRUE
+
+[PcdsDynamicHii.common.DEFAULT]
+
+  #
+  # Clock overrides.
+  #
+
+  gRaspberryPiTokenSpaceGuid.PcdCpuClock|L"CpuClock"|gConfigDxeFormSetGuid|0x0|0
+
+  #
+  # SD-related.
+  #
+
+  gRaspberryPiTokenSpaceGuid.PcdSdIsArasan|L"SdIsArasan"|gConfigDxeFormSetGuid|0x0|0
+  gRaspberryPiTokenSpaceGuid.PcdMmcForce1Bit|L"MmcForce1Bit"|gConfigDxeFormSetGuid|0x0|0
+  gRaspberryPiTokenSpaceGuid.PcdMmcForceDefaultSpeed|L"MmcForceDefaultSpeed"|gConfigDxeFormSetGuid|0x0|0
+  gRaspberryPiTokenSpaceGuid.PcdMmcSdDefaultSpeedMHz|L"MmcSdDefaultSpeedMHz"|gConfigDxeFormSetGuid|0x0|25
+  gRaspberryPiTokenSpaceGuid.PcdMmcSdHighSpeedMHz|L"MmcSdHighSpeedMHz"|gConfigDxeFormSetGuid|0x0|50
+  gRaspberryPiTokenSpaceGuid.PcdMmcDisableMulti|L"MmcDisableMulti"|gConfigDxeFormSetGuid|0x0|0
+
+  #
+  # Debug-related.
+  #
+
+  gRaspberryPiTokenSpaceGuid.PcdDebugEnableJTAG|L"DebugEnableJTAG"|gConfigDxeFormSetGuid|0x0|0
+  gRaspberryPiTokenSpaceGuid.PcdDebugShowUEFIExit|L"DebugShowUEFIExit"|gConfigDxeFormSetGuid|0x0|0
+
+  #
+  # Display-related.
+  #
+  gRaspberryPiTokenSpaceGuid.PcdDisplayEnableVModes|L"DisplayEnableVModes"|gConfigDxeFormSetGuid|0x0|0
+  gRaspberryPiTokenSpaceGuid.PcdDisplayEnableSShot|L"DisplayEnableSShot"|gConfigDxeFormSetGuid|0x0|1
+
+  #
+  # Common UEFI ones.
+  #
+
+  gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut|L"Timeout"|gEfiGlobalVariableGuid|0x0|5
+  #
+  # This is silly, but by pointing SetupConXXX and ConXXX PCDs to
+  # the same variables, I can use the graphical configuration to
+  # change the mode used by ConSplitter.
+  #
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSetupConOutColumn|L"Columns"|gRaspberryPiTokenSpaceGuid|0x0|80
+  gEfiMdeModulePkgTokenSpaceGuid.PcdConOutColumn|L"Columns"|gRaspberryPiTokenSpaceGuid|0x0|80
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSetupConOutRow|L"Rows"|gRaspberryPiTokenSpaceGuid|0x0|25
+  gEfiMdeModulePkgTokenSpaceGuid.PcdConOutRow|L"Rows"|gRaspberryPiTokenSpaceGuid|0x0|25
+
+[PcdsDynamicDefault.common]
+  #
+  # Set video resolution for boot options and for text setup.
+  #
+  gEfiMdeModulePkgTokenSpaceGuid.PcdVideoHorizontalResolution|0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdVideoVerticalResolution|0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSetupVideoHorizontalResolution|640
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSetupVideoVerticalResolution|480
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64|0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase|0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase|0
+
+################################################################################
+#
+# Components Section - list of all EDK II Modules needed by this Platform
+#
+################################################################################
+[Components.common]
+  #
+  # PEI Phase modules
+  #
+  ArmPlatformPkg/PrePi/PeiUniCore.inf
+
+  #
+  # DXE
+  #
+  MdeModulePkg/Core/Dxe/DxeMain.inf {
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/DxeCrc32GuidedSectionExtractLib/DxeCrc32GuidedSectionExtractLib.inf
+  }
+  MdeModulePkg/Universal/PCD/Dxe/Pcd.inf {
+    <LibraryClasses>
+      PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+  }
+
+  #
+  # Architectural Protocols
+  #
+  ArmPkg/Drivers/CpuDxe/CpuDxe.inf
+  MdeModulePkg/Core/RuntimeDxe/RuntimeDxe.inf
+  Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockServiceDxe.inf
+  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
+  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf {
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/VarCheckUefiLib/VarCheckUefiLib.inf
+      DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
+  }
+!if $(SECURE_BOOT_ENABLE) == TRUE
+  MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf {
+    <LibraryClasses>
+      NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
+  }
+  SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf
+!else
+  MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf
+!endif
+  MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf
+  MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
+  EmbeddedPkg/ResetRuntimeDxe/ResetRuntimeDxe.inf
+  EmbeddedPkg/RealTimeClockRuntimeDxe/RealTimeClockRuntimeDxe.inf {
+    <LibraryClasses>
+      RealTimeClockLib|Platform/Broadcom/Bcm283x/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf
+  }
+  EmbeddedPkg/MetronomeDxe/MetronomeDxe.inf
+
+  MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.inf
+  MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.inf
+  Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsoleDxe.inf
+  MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf
+  MdeModulePkg/Universal/SerialDxe/SerialDxe.inf
+  Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.inf
+
+  MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
+
+  UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf
+  Platform/Broadcom/Bcm283x/Drivers/Bcm2836InterruptDxe/Bcm2836InterruptDxe.inf
+  Platform/Broadcom/Bcm283x/Drivers/RpiFirmwareDxe/RpiFirmwareDxe.inf
+  Platform/Broadcom/Bcm283x/Drivers/RpiFdtDxe/RpiFdtDxe.inf
+  Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxe.inf
+  ArmPkg/Drivers/TimerDxe/TimerDxe.inf
+  MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
+  MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
+
+  #
+  # FAT filesystem + GPT/MBR partitioning
+  #
+  MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
+  MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
+  MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
+  FatPkg/EnhancedFatDxe/Fat.inf
+
+  #
+  # ACPI Support
+  #
+  MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
+  MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatformDxe.inf
+  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
+  Platform/Broadcom/Bcm283x/AcpiTables/AcpiTables.inf
+
+  #
+  # SMBIOS Support
+  #
+  Platform/Broadcom/Bcm283x/Drivers/PlatformSmbiosDxe/PlatformSmbiosDxe.inf
+  MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf
+
+  #
+  # Bds
+  #
+  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
+  MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf
+  MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
+  MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf
+  MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
+  Platform/Broadcom/Bcm283x/Drivers/Logo/LogoDxe.inf
+  MdeModulePkg/Application/UiApp/UiApp.inf {
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/DeviceManagerUiLib/DeviceManagerUiLib.inf
+      NULL|MdeModulePkg/Library/BootManagerUiLib/BootManagerUiLib.inf
+      NULL|MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenanceManagerUiLib.inf
+  }
+
+  #
+  # SCSI Bus and Disk Driver
+  #
+  MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
+  MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
+
+  #
+  # USB Support
+  #
+  Platform/Broadcom/Bcm283x/Drivers/DwUsbHostDxe/DwUsbHostDxe.inf
+  MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf
+  MdeModulePkg/Bus/Usb/UsbKbDxe/UsbKbDxe.inf
+  MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf
+  OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772b.inf
+
+  #
+  # SD/MMC support
+  #
+  Platform/Broadcom/Bcm283x/Drivers/SdHostDxe/SdHostDxe.inf
+  Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf
+  Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDxe.inf
+
+  #
+  # Networking stack
+  #
+  MdeModulePkg/Universal/Network/DpcDxe/DpcDxe.inf
+  MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf
+  MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf
+  MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf
+  MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf
+  MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxe.inf
+  MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf
+  MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf
+  MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf
+  MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxeBcDxe.inf
+  MdeModulePkg/Universal/Network/IScsiDxe/IScsiDxe.inf
+
+  #
+  # UEFI application (Shell Embedded Boot Loader)
+  #
+  ShellPkg/Application/Shell/Shell.inf {
+    <LibraryClasses>
+      ShellCommandLib|ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.inf
+      NULL|ShellPkg/Library/UefiShellLevel2CommandsLib/UefiShellLevel2CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellLevel1CommandsLib/UefiShellLevel1CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellLevel3CommandsLib/UefiShellLevel3CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellDriver1CommandsLib/UefiShellDriver1CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellDebug1CommandsLib/UefiShellDebug1CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellInstall1CommandsLib/UefiShellInstall1CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellNetwork1CommandsLib/UefiShellNetwork1CommandsLib.inf
+      HandleParsingLib|ShellPkg/Library/UefiHandleParsingLib/UefiHandleParsingLib.inf
+      PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
+      BcfgCommandLib|ShellPkg/Library/UefiShellBcfgCommandLib/UefiShellBcfgCommandLib.inf
+
+    <PcdsFixedAtBuild>
+      gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0xFF
+      gEfiShellPkgTokenSpaceGuid.PcdShellLibAutoInitialize|FALSE
+      gEfiMdePkgTokenSpaceGuid.PcdUefiLibMaxPrintBufferSize|8000
+      gEfiShellPkgTokenSpaceGuid.PcdShellFileOperationSize|0x200000
+  }
diff --git a/Platform/Broadcom/Bcm283x/RaspberryPiPkg.fdf b/Platform/Broadcom/Bcm283x/RaspberryPiPkg.fdf
new file mode 100644
index 000000000000..4da8849b3646
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/RaspberryPiPkg.fdf
@@ -0,0 +1,450 @@
+## @file
+#
+#  Copyright (c) 2011-2015, ARM Limited. All rights reserved.
+#  Copyright (c) 2014, Linaro Limited. All rights reserved.
+#  Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+#  Copyright (c) 2017 - 2018, Andrei Warkentin <andrey.warkentin at gmail.com>
+#
+#  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.
+#
+##
+
+################################################################################
+#
+# FD Section
+# The [FD] Section is made up of the definition statements and a
+# description of what goes into  the Flash Device Image.  Each FD section
+# defines one flash "device" image.  A flash device image may be one of
+# the following: Removable media bootable image (like a boot floppy
+# image,) an Option ROM image (that would be "flashed" into an add-in
+# card,) a System "Flash"  image (that would be burned into a system's
+# flash) or an Update ("Capsule") image that will be used to update and
+# existing system flash.
+#
+################################################################################
+
+[FD.RPI_EFI]
+BaseAddress   = 0x00000000|gArmTokenSpaceGuid.PcdFdBaseAddress
+Size          = 0x00200000|gArmTokenSpaceGuid.PcdFdSize
+ErasePolarity = 1
+
+BlockSize     = 0x00001000|gRaspberryPiTokenSpaceGuid.PcdFirmwareBlockSize
+NumBlocks     = 0x200
+
+################################################################################
+#
+# Following are lists of FD Region layout which correspond to the locations of different
+# images within the flash device.
+#
+# Regions must be defined in ascending order and may not overlap.
+#
+# A Layout Region start with a eight digit hex offset (leading "0x" required) followed by
+# the pipe "|" character, followed by the size of the region, also in hex with the leading
+# "0x" characters. Like:
+# Offset|Size
+# PcdOffsetCName|PcdSizeCName
+# RegionType <FV, DATA, or FILE>
+#
+################################################################################
+
+#
+# ATF primary boot image
+#
+0x00000000|0x00010000
+FILE = Platform/Broadcom/Bcm283x/Binary/bl1.bin
+
+#
+# DTB.
+#
+0x00010000|0x00010000
+DATA = { 0x00 }
+
+#
+# ATF secondary boot image in FIP format (BL2 + BL31)
+#
+0x00020000|0x00010000
+FILE = Platform/Broadcom/Bcm283x/Binary/fip.bin
+
+#
+# UEFI image
+#
+0x00030000|0x001b0000
+gArmTokenSpaceGuid.PcdFvBaseAddress|gArmTokenSpaceGuid.PcdFvSize
+FV = FVMAIN_COMPACT
+
+#
+# Variables (0x20000 overall).
+#
+# 0x001e0000 - 0x001edfff EFI_FIRMWARE_VOLUME_HEADER
+# 0x001ee000 - 0x001eefff Event log
+# 0x001ef000 - 0x001effff EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER-
+# 0x001f0000 - 0x001fffff Data
+#
+
+# NV_VARIABLE_STORE
+0x001e0000|0x0000e000
+gRaspberryPiTokenSpaceGuid.PcdNvStorageVariableBase|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+
+DATA = {
+  ## This is the EFI_FIRMWARE_VOLUME_HEADER
+  # ZeroVector []
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  # FileSystemGuid: gEfiSystemNvDataFvGuid         =
+  #   { 0xFFF12B8D, 0x7696, 0x4C8B,
+  #     { 0xA9, 0x85, 0x27, 0x47, 0x07, 0x5B, 0x4F, 0x50 }}
+  0x8D, 0x2B, 0xF1, 0xFF, 0x96, 0x76, 0x8B, 0x4C,
+  0xA9, 0x85, 0x27, 0x47, 0x07, 0x5B, 0x4F, 0x50,
+  # FvLength: 0x20000
+  0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+  # Signature "_FVH"       # Attributes
+  0x5f, 0x46, 0x56, 0x48, 0xff, 0xfe, 0x04, 0x00,
+  # HeaderLength
+  0x48, 0x00,
+  # CheckSum
+  0x19, 0xF9,
+  # ExtHeaderOffset #Reserved #Revision
+  0x00, 0x00, 0x00, 0x02,
+  # Blockmap[0]: 0x20 Blocks * 0x1000 Bytes / Block
+  0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+  # Blockmap[1]: End
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  ## This is the VARIABLE_STORE_HEADER
+  # It is compatible with SECURE_BOOT_ENABLE == FALSE as well.
+  # Signature: gEfiAuthenticatedVariableGuid =
+  #   { 0xaaf32c78, 0x947b, 0x439a,
+  #     { 0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92 }}
+  0x78, 0x2c, 0xf3, 0xaa, 0x7b, 0x94, 0x9a, 0x43,
+  0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92,
+  # Size: 0xe000 (gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize) -
+  #         0x48 (size of EFI_FIRMWARE_VOLUME_HEADER) = 0xdfb8
+  # This can speed up the Variable Dispatch a bit.
+  0xB8, 0xDF, 0x00, 0x00,
+  # FORMATTED: 0x5A #HEALTHY: 0xFE #Reserved: UINT16 #Reserved1: UINT32
+  0x5A, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+}
+
+# NV_EVENT_LOG
+0x001ee000|0x00001000
+gRaspberryPiTokenSpaceGuid.PcdNvStorageEventLogBase|gRaspberryPiTokenSpaceGuid.PcdNvStorageEventLogSize
+
+# NV_FTW_WORKING header
+0x001ef000|0x00001000
+gRaspberryPiTokenSpaceGuid.PcdNvStorageFtwWorkingBase|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
+
+DATA = {
+  # EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER->Signature = gEdkiiWorkingBlockSignatureGuid         =
+  #  { 0x9e58292b, 0x7c68, 0x497d, { 0xa0, 0xce, 0x65,  0x0, 0xfd, 0x9f, 0x1b, 0x95 }}
+  0x2b, 0x29, 0x58, 0x9e, 0x68, 0x7c, 0x7d, 0x49,
+  0xa0, 0xce, 0x65,  0x0, 0xfd, 0x9f, 0x1b, 0x95,
+  # Crc:UINT32            #WorkingBlockValid:1, WorkingBlockInvalid:1, Reserved
+  0x2c, 0xaf, 0x2c, 0x64, 0xFE, 0xFF, 0xFF, 0xFF,
+  # WriteQueueSize: UINT64
+  0xE0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+}
+
+# NV_FTW_WORKING data
+0x001f0000|0x00010000
+gRaspberryPiTokenSpaceGuid.PcdNvStorageFtwSpareBase|gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
+
+################################################################################
+#
+# FV Section
+#
+# [FV] section is used to define what components or modules are placed within a flash
+# device file.  This section also defines order the components and modules are positioned
+# within the image.  The [FV] section consists of define statements, set statements and
+# module statements.
+#
+################################################################################
+
+[FV.FvMain]
+FvNameGuid         = 9a15aa37-d555-4a4e-b541-86391ff68164
+BlockSize          = 0x40
+NumBlocks          = 0         # This FV gets compressed so make it just big enough
+FvAlignment        = 16        # FV alignment and FV attributes setting.
+ERASE_POLARITY     = 1
+MEMORY_MAPPED      = TRUE
+STICKY_WRITE       = TRUE
+LOCK_CAP           = TRUE
+LOCK_STATUS        = TRUE
+WRITE_DISABLED_CAP = TRUE
+WRITE_ENABLED_CAP  = TRUE
+WRITE_STATUS       = TRUE
+WRITE_LOCK_CAP     = TRUE
+WRITE_LOCK_STATUS  = TRUE
+READ_DISABLED_CAP  = TRUE
+READ_ENABLED_CAP   = TRUE
+READ_STATUS        = TRUE
+READ_LOCK_CAP      = TRUE
+READ_LOCK_STATUS   = TRUE
+
+  INF MdeModulePkg/Core/Dxe/DxeMain.inf
+  INF MdeModulePkg/Universal/PCD/Dxe/Pcd.inf
+
+  #
+  # PI DXE Drivers producing Architectural Protocols (EFI Services)
+  #
+  INF ArmPkg/Drivers/CpuDxe/CpuDxe.inf
+  INF MdeModulePkg/Core/RuntimeDxe/RuntimeDxe.inf
+  INF MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf
+  INF MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf
+  INF Platform/Broadcom/Bcm283x/Drivers/VarBlockServiceDxe/VarBlockServiceDxe.inf
+  INF MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
+  INF MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
+!if $(SECURE_BOOT_ENABLE) == TRUE
+  INF SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf
+!endif
+  INF MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
+  INF EmbeddedPkg/ResetRuntimeDxe/ResetRuntimeDxe.inf
+  INF EmbeddedPkg/RealTimeClockRuntimeDxe/RealTimeClockRuntimeDxe.inf
+  INF EmbeddedPkg/MetronomeDxe/MetronomeDxe.inf
+  INF MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
+
+  #
+  # Multiple Console IO support
+  #
+  INF MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.inf
+  INF MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.inf
+  INF Platform/Broadcom/Bcm283x/Drivers/GraphicsConsoleDxe/GraphicsConsoleDxe.inf
+  INF MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf
+  INF MdeModulePkg/Universal/SerialDxe/SerialDxe.inf
+  INF Platform/Broadcom/Bcm283x/Drivers/DisplayDxe/DisplayDxe.inf
+
+  INF UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf
+  INF Platform/Broadcom/Bcm283x/Drivers/Bcm2836InterruptDxe/Bcm2836InterruptDxe.inf
+  INF Platform/Broadcom/Bcm283x/Drivers/RpiFirmwareDxe/RpiFirmwareDxe.inf
+  INF Platform/Broadcom/Bcm283x/Drivers/RpiFdtDxe/RpiFdtDxe.inf
+  INF Platform/Broadcom/Bcm283x/Drivers/ConfigDxe/ConfigDxe.inf
+  INF ArmPkg/Drivers/TimerDxe/TimerDxe.inf
+  INF MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
+  INF MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
+
+  #
+  # FAT filesystem + GPT/MBR partitioning
+  #
+  INF MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
+  INF MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
+  INF FatPkg/EnhancedFatDxe/Fat.inf
+  INF MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
+
+  #
+  # UEFI application (Shell Embedded Boot Loader)
+  #
+  INF ShellPkg/Application/Shell/Shell.inf
+
+  #
+  # ACPI Support
+  #
+  INF MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
+  INF MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatformDxe.inf
+  INF MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
+  INF RuleOverride = ACPITABLE Platform/Broadcom/Bcm283x/AcpiTables/AcpiTables.inf
+
+  #
+  # SMBIOS Support
+  #
+  INF Platform/Broadcom/Bcm283x/Drivers/PlatformSmbiosDxe/PlatformSmbiosDxe.inf
+  INF MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf
+
+  #
+  # Bds
+  #
+  INF MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
+  INF MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf
+  INF MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
+  INF MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf
+  INF MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
+  INF MdeModulePkg/Application/UiApp/UiApp.inf
+
+  #
+  # Networking stack
+  #
+  INF MdeModulePkg/Universal/Network/DpcDxe/DpcDxe.inf
+  INF MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf
+  INF MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf
+  INF MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Dxe.inf
+  INF MdeModulePkg/Universal/Network/MnpDxe/MnpDxe.inf
+  INF MdeModulePkg/Universal/Network/VlanConfigDxe/VlanConfigDxe.inf
+  INF MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Dxe.inf
+  INF MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf
+  INF MdeModulePkg/Universal/Network/Udp4Dxe/Udp4Dxe.inf
+  INF MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxeBcDxe.inf
+  INF MdeModulePkg/Universal/Network/IScsiDxe/IScsiDxe.inf
+
+  #
+  # SCSI Bus and Disk Driver
+  #
+  INF MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
+  INF MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
+
+  #
+  # USB Support
+  #
+  INF Platform/Broadcom/Bcm283x/Drivers/DwUsbHostDxe/DwUsbHostDxe.inf
+  INF MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf
+  INF MdeModulePkg/Bus/Usb/UsbKbDxe/UsbKbDxe.inf
+  INF MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf
+  INF OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772b.inf
+
+  #
+  # SD/MMC support
+  #
+  INF Platform/Broadcom/Bcm283x/Drivers/SdHostDxe/SdHostDxe.inf
+  INF Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf
+  INF Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDxe.inf
+
+  #
+  # Pi logo (splash screen)
+  #
+  INF Platform/Broadcom/Bcm283x/Drivers/Logo/LogoDxe.inf
+
+  #
+  # FDT (GUID matches mRaspberryPiFfsFileGuid in RaspberryPiPlatformDxe)
+  #
+  FILE FREEFORM = DF5DA223-1D27-47C3-8D1B-9A41B55A18BC {
+    SECTION RAW = Platform/Broadcom/Bcm283x/DeviceTree/bcm2710-rpi-3-b.dtb
+  }
+
+[FV.FVMAIN_COMPACT]
+FvAlignment        = 16
+ERASE_POLARITY     = 1
+MEMORY_MAPPED      = TRUE
+STICKY_WRITE       = TRUE
+LOCK_CAP           = TRUE
+LOCK_STATUS        = TRUE
+WRITE_DISABLED_CAP = TRUE
+WRITE_ENABLED_CAP  = TRUE
+WRITE_STATUS       = TRUE
+WRITE_LOCK_CAP     = TRUE
+WRITE_LOCK_STATUS  = TRUE
+READ_DISABLED_CAP  = TRUE
+READ_ENABLED_CAP   = TRUE
+READ_STATUS        = TRUE
+READ_LOCK_CAP      = TRUE
+READ_LOCK_STATUS   = TRUE
+
+  INF ArmPlatformPkg/PrePi/PeiUniCore.inf
+  FILE FV_IMAGE = 9E21FD93-9C72-4c15-8C4B-E77F1DB2D792 {
+    SECTION GUIDED EE4E5898-3914-4259-9D6E-DC7BD79403CF PROCESSING_REQUIRED = TRUE {
+      SECTION FV_IMAGE = FVMAIN
+    }
+  }
+
+################################################################################
+#
+# Rules are use with the [FV] section's module INF type to define
+# how an FFS file is created for a given INF file. The following Rule are the default
+# rules for the different module type. User can add the customized rules to define the
+# content of the FFS file.
+#
+################################################################################
+
+
+############################################################################
+# Example of a DXE_DRIVER FFS file with a Checksum encapsulation section   #
+############################################################################
+#
+#[Rule.Common.DXE_DRIVER]
+#  FILE DRIVER = $(NAMED_GUID) {
+#    DXE_DEPEX    DXE_DEPEX               Optional $(INF_OUTPUT)/$(MODULE_NAME).depex
+#    COMPRESS PI_STD {
+#      GUIDED {
+#        PE32     PE32                    $(INF_OUTPUT)/$(MODULE_NAME).efi
+#        UI       STRING="$(MODULE_NAME)" Optional
+#        VERSION  STRING="$(INF_VERSION)" Optional BUILD_NUM=$(BUILD_NUMBER)
+#      }
+#    }
+#  }
+#
+############################################################################
+
+[Rule.Common.SEC]
+  FILE SEC = $(NAMED_GUID) RELOCS_STRIPPED FIXED {
+    TE  TE Align = Auto                 $(INF_OUTPUT)/$(MODULE_NAME).efi
+  }
+
+[Rule.Common.PEI_CORE]
+  FILE PEI_CORE = $(NAMED_GUID) FIXED {
+    TE     TE Align = Auto              $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI     STRING ="$(MODULE_NAME)" Optional
+  }
+
+[Rule.Common.PEIM]
+  FILE PEIM = $(NAMED_GUID) FIXED {
+     PEI_DEPEX PEI_DEPEX Optional       $(INF_OUTPUT)/$(MODULE_NAME).depex
+     TE       TE Align = Auto           $(INF_OUTPUT)/$(MODULE_NAME).efi
+     UI       STRING="$(MODULE_NAME)" Optional
+  }
+
+[Rule.Common.PEIM.TIANOCOMPRESSED]
+  FILE PEIM = $(NAMED_GUID) DEBUG_MYTOOLS_IA32 {
+    PEI_DEPEX PEI_DEPEX Optional        $(INF_OUTPUT)/$(MODULE_NAME).depex
+    GUIDED A31280AD-481E-41B6-95E8-127F4C984779 PROCESSING_REQUIRED = TRUE {
+      PE32      PE32                    $(INF_OUTPUT)/$(MODULE_NAME).efi
+      UI        STRING="$(MODULE_NAME)" Optional
+    }
+  }
+
+[Rule.Common.DXE_CORE]
+  FILE DXE_CORE = $(NAMED_GUID) {
+    PE32     PE32                       $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI       STRING="$(MODULE_NAME)" Optional
+  }
+
+[Rule.Common.UEFI_DRIVER]
+  FILE DRIVER = $(NAMED_GUID) {
+    DXE_DEPEX    DXE_DEPEX              Optional $(INF_OUTPUT)/$(MODULE_NAME).depex
+    PE32         PE32                   $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI           STRING="$(MODULE_NAME)" Optional
+  }
+
+[Rule.Common.DXE_DRIVER]
+  FILE DRIVER = $(NAMED_GUID) {
+    DXE_DEPEX    DXE_DEPEX              Optional $(INF_OUTPUT)/$(MODULE_NAME).depex
+    PE32         PE32                   $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI           STRING="$(MODULE_NAME)" Optional
+    RAW          ACPI  Optional               |.acpi
+    RAW          ASL   Optional               |.aml
+  }
+
+[Rule.Common.DXE_RUNTIME_DRIVER]
+  FILE DRIVER = $(NAMED_GUID) {
+    DXE_DEPEX    DXE_DEPEX              Optional $(INF_OUTPUT)/$(MODULE_NAME).depex
+    PE32         PE32                   $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI           STRING="$(MODULE_NAME)" Optional
+  }
+
+[Rule.Common.UEFI_APPLICATION]
+  FILE APPLICATION = $(NAMED_GUID) {
+    UI     STRING ="$(MODULE_NAME)"     Optional
+    PE32   PE32                         $(INF_OUTPUT)/$(MODULE_NAME).efi
+  }
+
+[Rule.Common.UEFI_DRIVER.BINARY]
+  FILE DRIVER = $(NAMED_GUID) {
+    DXE_DEPEX DXE_DEPEX Optional      |.depex
+    PE32      PE32                    |.efi
+    UI        STRING="$(MODULE_NAME)" Optional
+    VERSION   STRING="$(INF_VERSION)" Optional BUILD_NUM=$(BUILD_NUMBER)
+  }
+
+[Rule.Common.UEFI_APPLICATION.BINARY]
+  FILE APPLICATION = $(NAMED_GUID) {
+    PE32      PE32                    |.efi
+    UI        STRING="$(MODULE_NAME)" Optional
+    VERSION   STRING="$(INF_VERSION)" Optional BUILD_NUM=$(BUILD_NUMBER)
+  }
+
+[Rule.Common.USER_DEFINED.ACPITABLE]
+  FILE FREEFORM = $(NAMED_GUID) {
+    RAW ACPI               |.acpi
+    RAW ASL                |.aml
+  }
diff --git a/Platform/Broadcom/Bcm283x/Readme.md b/Platform/Broadcom/Bcm283x/Readme.md
new file mode 100644
index 000000000000..a2633434c7bf
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Readme.md
@@ -0,0 +1,263 @@
+Raspberry Pi (Broadcom BCM283x) EDK2 Platform Support
+=====================================================
+
+# Summary
+
+This is a port of 64-bit Tiano Core UEFI firmware for the Raspberry Pi 3/3B+ platforms,
+based on [Ard Bisheuvel's 64-bit](http://www.workofard.com/2017/02/uefi-on-the-pi/)
+and [Microsoft's 32-bit](https://github.com/ms-iot/RPi-UEFI/tree/ms-iot/Pi3BoardPkg)
+implementations, as maintained by [Andrei Warkentin](https://github.com/andreiw/RaspberryPiPkg).
+
+This is meant as a generally useful 64-bit ATF + UEFI implementation for the Raspberry
+Pi 3/3B+ which should be good enough for most kind of UEFI development, as well as for
+running consummer Operating Systems in such as Linux or Windows.
+
+Raspberry Pi is a trademark of the [Raspberry Pi Foundation](http://www.raspberrypi.org).
+
+# Status
+
+This firmware, that has been validated to compile against the current
+[edk2](https://github.com/tianocore/edk2)/[edk2-platforms](https://github.com/tianocore/edk2-platforms),
+should be able to boot Linux (SUSE, Ubuntu), NetBSD, FreeBSD as well as Windows 10 ARM64
+(full GUI version).
+
+It also provides support for ATF ([Arm Trusted Platform](https://github.com/ARM-software/arm-trusted-firmware)).
+
+HDMI and the mini-UART serial port can be used for output devices, with mirrored output.
+USB keyboards and the mini-UART serial port can be used as input.
+
+The boot order is currently hardcoded, first to the USB ports and then to the uSD card.
+If there no bootable media media is found, the UEFI Shell is launched.
+<kbd>Esc</kbd> enters platform setup. <kbd>F1</kbd> boots the UEFI Shell.
+
+# Building
+
+(These instructions were validated against the latest edk2 / edk2-platforms /
+edk2-non-osi as of 2018.12.06, on a Debian 9.6 x64 system).
+
+You may need to install the relevant compilation tools. Especially you should have the
+ACPI Source Language (ASL) compiler, `nasm` as well as a native compiler installed.
+On a Debian system, you can get these prerequisites installed with:
+```
+sudo apt-get install build-essential acpica-tools nasm uuid-dev
+```
+
+**IMPORTANT:** Do not be tempted to install Debian's ARM64 GCC cross compiler package,
+as this currently results in GCC 6.x being installed, which is known to create issues.
+Instead, you should stick with GCC 5.5, such as the one provided by Linaro, as per the
+instructions below.
+
+You can then build the firmware as follows:
+
+```
+mkdir ~/workspace
+cd ~/workspace
+git clone https://github.com/tianocore/edk2.git
+# The following is only needed once, after you cloned edk2
+make -C edk2/BaseTools
+# You may also have to issue git submodule init/update in edk2 if you want to enable Secure Boot
+git clone https://github.com/tianocore/edk2-platforms.git
+git clone https://github.com/tianocore/edk2-non-osi.git
+# You *MUST* use a GCC5 toolchain (*NOT* GCC6 or later), such as the one from
+# https://releases.linaro.org/components/toolchain/binaries/5.5-2017.10/aarch64-linux-gnu/
+# GCC6 and later toolchains may result in Synchronous Exceptions. You have been warned!
+wget https://releases.linaro.org/components/toolchain/binaries/5.5-2017.10/aarch64-linux-gnu/gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu.tar.xz
+tar -xJvf gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu.tar.xz
+# If you have multiple AARCH64 toolchains, make sure the GCC5 one comes first in your path
+export PATH=$PWD/gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu/bin:$PATH
+export GCC5_AARCH64_PREFIX=aarch64-linux-gnu-
+export WORKSPACE=$PWD
+export PACKAGES_PATH=$WORKSPACE/edk2:$WORKSPACE/edk2-platforms:$WORKSPACE/edk2-non-osi
+. edk2/edksetup.sh
+build -a AARCH64 -t GCC5 -p edk2-platforms/Platform/Broadcom/Bcm283x/RaspberryPiPkg.dsc -b RELEASE -DBUILD_EPOCH=`date +%s`
+```
+
+# Booting the firmware
+
+1. Format a uSD card as FAT32
+2. Copy the generated `RPI_EFI.fd` firmware onto the partition
+3. Download and copy the following files from https://github.com/raspberrypi/firmware/tree/master/boot
+  - `bootcode.bin`
+  - `fixup.dat`
+  - `start.elf`
+4. Create a `config.txt` with the following content:
+  ```
+  arm_control=0x200
+  enable_uart=1
+  armstub=RPI_EFI.fd
+  disable_commandline_tags=1
+  ```
+5. Insert the uSD card and power up the Pi.
+
+Note that if you have a model 3+ or a model 3 where you enabled USB boot through OTP
+(see [here](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/msd.md))
+you may also be able to boot from a FAT32 USB driver rather than uSD.
+
+# Notes
+
+## ARM Trusted Firmware (ATF)
+
+The ATF binaries being used were compiled from the latest ATF source.
+
+For more details on the ATF compilation, see the [README](./Binary/README.md) in the `Binary/` directory.
+
+## Custom Device Tree
+
+The default Device Tree included in the firmware is the one for a Raspberry Pi 3 Model B (not B+).
+If you want to use a different Device Tree, to boot a Pi 3 Model B+ for instance (for which a
+DTB is also provided under `DeviceTree/`), you should copy the relevant `.dtb` into the root of
+the SD or USB, and then edit your `config.txt` so that it looks like:
+
+```
+(...)
+disable_commandline_tags=2
+device_tree_address=0x10000
+device_tree_end=0x20000
+device_tree=bcm2710-rpi-3-b-plus.dtb
+```
+
+Note: the address range **must** be `[0x10000:0x20000]`.
+`dtoverlay` and `dtparam` parameters are also supported **when** providing a Device Tree`.
+
+## Custom `bootargs`
+
+This firmware will honor the command line passed by the GPU via `cmdline.txt`.
+
+Note, that the ultimate contents of `/chosen/bootargs` are a combination of several pieces:
+- Original `/chosen/bootargs` if using the internal DTB. Seems to be completely discarded by GPU when booting with a custom device tree.
+- GPU-passed hardware configuration. This one is always present.
+- Additional boot options passed via `cmdline.txt`.
+
+# Tested Platforms
+
+## Ubuntu
+
+[Ubuntu 18.04 LTS](http://releases.ubuntu.com/18.04/) has been tested and confirmed to work,
+on a Raspberry 3 Model B, including the installation process. Note however that network
+installation and networking may not work on the Model B+, due to the `lan78xx` Linux driver
+still requiring some support.
+
+Below are the steps you can follow to install Ubuntu LTS onto SD/USB:
+* Download the latest Ubuntu LTS ARM64 [`mini.iso`](http://ports.ubuntu.com/ubuntu-ports/dists/bionic/main/installer-arm64/current/images/netboot/mini.iso).
+* Partition the media as MBR and create a ~200 MB FAT32 partition on it with MBR type `0x0c`.
+  Note: Do not be tempted to use GPT partition scheme or `0xef` (EFI System Partition) for the
+  type, as none of these are supported by the Raspberry Pi's internal boot rom.
+* Extract the full content of the ISO onto the partition you created.
+* Also extract the GRUB EFI bootloader `bootaa64.efi` from `/boot/grub/efi.img` to `/boot/grub/`.
+  Note: Do not be tempted to copy this file to another directory (such as `/efi/boot/`) as GRUB looks for its
+  modules and configuration data in the same directory as the EFI loader and also, the installation
+  process will create a `bootaa64.efi` into `/efi/boot/`.
+* If needed, copy the UEFI firmware files (`RPI_EFI.fd`, `bootcode.bin`, `fixup.dat` and `start.elf`)
+  onto the FAT partition.
+* Boot the pi and let it go into the UEFI shell.
+* Navigate to `fs0:` then `/boot/grub/` and launch the GRUB efi loader.
+* Follow the Ubuntu installation process.
+
+Note: Because Ubuntu operates in quiet mode by default (no boot messages), you may think the system is frozen
+on first reboot after installation. However, if you wait long enough you **will** get to a login prompt.
+
+Once Linux is running, if desired, you can disable quiet boot, as well as force the display
+of the GRUB selector, by editing `/etc/default/grub` and changing:
+* `GRUB_TIMEOUT_STYLE=hidden` → `GRUB_TIMEOUT_STYLE=menu`
+* `GRUB_CMDLINE_LINUX_DEFAULT="splash quiet"` → `GRUB_CMDLINE_LINUX_DEFAULT=""`
+
+Then, to have your changes applied run `update-grub` and reboot.
+
+## Other Linux distributions
+
+* Debian ARM64 does not currently work, most likely due to missing required module support
+  in its kernel. However its installation process works, so it may be possible to get it
+  running with a custom kernel.
+
+* OpenSUSE Leap 42.3 has been reported to work on Raspberry 3 Model B.
+
+* Other ARM64 Linux releases, that support UEFI boot and have the required hardware support
+  for Pi hardware are expected to run, though their installation process might require some
+  cajoling.
+
+## Windows
+
+Windows 10 1809 for ARM64 (build 17763) has been tested and confirmed to work (after replacing
+`C:\Windows\System32\Drivers\WppRecorder.sys` with an older version, since the one from 1809
+appears to be buggy across all archs, and results in a similar BSOD when trying to run Windows
+To Go on x64 with native drivers for instance).
+
+Windows 10 1803 for ARM64 and earlier do not work due to the presence of a hardware ASSERT check
+in the Windows kernel, that was removed in later versions.
+
+You probably want to look at https://www.worproject.ml/ as well as the
+[Windows thread in the original RaspberryPiPkg](https://github.com/andreiw/RaspberryPiPkg/issues/12)
+for installation details.
+
+## Other platforms
+
+Details you may need to run other platforms, including FreeBSD, is provided in the
+[Readme from the original RaspberryPiPkg](https://github.com/andreiw/RaspberryPiPkg).
+
+# Limitations
+
+## HDMI
+
+The UEFI HDMI video support relies on the VC (that's the GPU)
+firmware to correctly detect and configure the attached screen.
+Some screens are slow, and this detection may not occur fast
+enough. Finally, you may wish to be able to boot your Pi
+headless, yet be able to attach a display to it later for
+debugging.
+
+To accommodate these issues, the following extra lines
+are recommended for your `config.txt`:
+- `hdmi_force_hotplug=1` to allow plugging in video after system is booted.
+- `hdmi_group=1` and `hdmi_mode=4` to force a specific mode, both to accommodate
+   late-plugged screens or buggy/slow screens. See [official documentation](https://www.raspberrypi.org/documentation/configuration/config-txt/video.md)
+   to make sense of these parameters (example above sets up 720p 60Hz).
+
+## NVRAM
+
+The Raspberry Pi has no NVRAM.
+
+NVRAM is emulated, with the non-volatile store backed by the UEFI image itself. This means
+that any changes made in UEFI proper will be persisted, but changes made in HLOS will not.
+It would be nice to implement ATF-assisted warm reboot, to allow persisting HLOS
+NVRAM changes.
+
+## RTC
+
+The Rasberry Pi has no RTC.
+
+`RtcEpochSeconds` NVRAM variable is used to store the boot time
+This should allow you to set whatever date/time you
+want using the Shell date and time commands. While in UEFI
+or HLOS, the time will tick forward. `RtcEpochSeconds`
+is not updated on reboots.
+
+## uSD
+
+UEFI supports both the Arasan SDHCI and the Broadcom SDHost controllers to access the uSD slot.
+You can use either. The other controller gets routed to the SDIO card. The choice made will
+impact ACPI OSes booted (e.g. Windows 10). Arasan, being an SDIO controller, is usually used
+with the WiFi adapter where available. SDHost cannot be used with SDIO. In UEFI setup screen:
+- go to `Device Manager`
+- go to `Raspberry Pi Configuration`
+- go to `Chipset`
+- configure `Boot uSD Routing`
+
+Known issues:
+- Arasan HS/4bit support is missing.
+- No 8 bit mode support for (e)MMC (irrelevant for the Pi 3).
+- Hacky (e)MMC support (no HS).
+- No card removal/replacement detection, tons of timeouts and slow down during boot without an uSD card present.
+
+## USB
+
+- USB1 BBB mass storage devices untested (USB2 and USB3 devices are fine).
+- USB1 CBI mass storage devices don't work (e.g. HP FD-05PUB floppy).
+
+## ACPI
+
+ACPI should match the MS-IoT one. Both Arasan and SDHost SD controllers are exposed.
+
+## Missing Functionality
+
+- Network booting via onboard NIC.
+- Ability to switch UART use to PL011.
-- 
2.17.0.windows.1



More information about the edk2-devel mailing list