Index: /trunk/src/VBox/Devices/EFI/DevFlash.cpp
===================================================================
--- /trunk/src/VBox/Devices/EFI/DevFlash.cpp	(revision 81249)
+++ /trunk/src/VBox/Devices/EFI/DevFlash.cpp	(revision 81250)
@@ -35,4 +35,5 @@
 
 #include "VBoxDD.h"
+#include "FlashCore.h"
 
 
@@ -40,30 +41,4 @@
 *   Defined Constants And Macros                                                                                                 *
 *********************************************************************************************************************************/
-/** The current version of the saved state. */
-#define FLASH_SAVED_STATE_VERSION           1
-
-
-/** @name CUI (Command User Interface) Commands.
- *  @{ */
-#define FLASH_CMD_ALT_WRITE             0x10
-#define FLASH_CMD_ERASE_SETUP           0x20
-#define FLASH_CMD_WRITE                 0x40
-#define FLASH_CMD_STS_CLEAR             0x50
-#define FLASH_CMD_STS_READ              0x70
-#define FLASH_CMD_READ_ID               0x90
-#define FLASH_CMD_ERASE_SUS_RES         0xB0
-#define FLASH_CMD_ERASE_CONFIRM         0xD0
-#define FLASH_CMD_ARRAY_READ            0xFF
-/** @} */
-
-/** @name Status register bits.
- *  @{ */
-#define FLASH_STATUS_WSMS               0x80    /* Write State Machine Status, 1=Ready */
-#define FLASH_STATUS_ESS                0x40    /* Erase Suspend Status, 1=Suspended */
-#define FLASH_STATUS_ES                 0x20    /* Erase Status, 1=Error */
-#define FLASH_STATUS_BWS                0x10    /* Byte Write Status, 1=Error */
-#define FLASH_STATUS_VPPS               0x08    /* Vpp Status, 1=Low Vpp */
-/* The remaining bits 0-2 are reserved/unused */
-/** @} */
 
 
@@ -76,28 +51,11 @@
 typedef struct DEVFLASH
 {
-    /** The current command. */
-    uint8_t             bCmd;
-    /** The status register. */
-    uint8_t             bStatus;
-    /** Current bus cycle. */
-    uint8_t             cBusCycle;
-
-    uint8_t             uPadding0;
-
-    /* The following state does not change at runtime.*/
-    /** Manufacturer (high byte) and device (low byte) ID. */
-    uint16_t            u16FlashId;
-    /** The configured block size of the device. */
-    uint16_t            cbBlockSize;
+    /** The flash core device instance.*/
+    FLASHCORE           Core;
     /** The guest physical memory base address. */
     RTGCPHYS            GCPhysFlashBase;
-    /** The flash memory region size.  */
-    uint32_t            cbFlashSize;
-    /** The actual flash memory data.  */
-    uint8_t             *pbFlash;
     /** When set, indicates the state was saved. */
     bool                fStateSaved;
-    /** The backing file. */
-    RTFILE              hFlashFile;
+    /** The file conaining the flash content. */
     char                *pszFlashFile;
 } DEVFLASH;
@@ -111,153 +69,11 @@
 #ifdef IN_RING3 /* for now */
 
-static int flashMemWriteByte(PDEVFLASH pThis, RTGCPHYS GCPhysAddr, uint8_t bCmd)
-{
-    int rc = VINF_SUCCESS;
-    unsigned uOffset;
-
-    /* NB: Older datasheets (e.g. 28F008SA) suggest that for two-cycle commands like byte write or
-     * erase setup, the address is significant in both cycles, but do not explain what happens
-     * should the addresses not match. Newer datasheets (e.g. 28F008B3) clearly say that the address
-     * in the first byte cycle never matters. We prefer the latter interpretation.
-     */
-
-    if (pThis->cBusCycle == 0)
-    {
-        /* First bus write cycle, start processing a new command. Address is ignored. */
-        switch (bCmd)
-        {
-        case FLASH_CMD_ARRAY_READ:
-        case FLASH_CMD_STS_READ:
-        case FLASH_CMD_ERASE_SUS_RES:
-        case FLASH_CMD_READ_ID:
-            /* Single-cycle write commands, only change the current command. */
-            pThis->bCmd = bCmd;
-            break;
-        case FLASH_CMD_STS_CLEAR:
-            /* Status clear continues in read mode. */
-            pThis->bStatus = 0;
-            pThis->bCmd = FLASH_CMD_ARRAY_READ;
-            break;
-        case FLASH_CMD_WRITE:
-        case FLASH_CMD_ALT_WRITE:
-        case FLASH_CMD_ERASE_SETUP:
-            /* Two-cycle commands, advance the bus write cycle. */
-            pThis->bCmd = bCmd;
-            pThis->cBusCycle++;
-            break;
-        default:
-            LogFunc(("1st cycle command %02X, current cmd %02X\n", bCmd, pThis->bCmd));
-            break;
-        }
-    }
-    else
-    {
-        /* Second write of a two-cycle command. */
-        Assert(pThis->cBusCycle == 1);
-        switch (pThis->bCmd)
-        {
-        case FLASH_CMD_WRITE:
-        case FLASH_CMD_ALT_WRITE:
-            uOffset = GCPhysAddr & (pThis->cbFlashSize - 1);
-            if (uOffset < pThis->cbFlashSize)
-            {
-                pThis->pbFlash[uOffset] = bCmd;
-                /* NB: Writes are instant and never fail. */
-                LogFunc(("wrote byte to flash at %08RGp: %02X\n", GCPhysAddr, bCmd));
-            }
-            else
-                LogFunc(("ignoring write at %08RGp: %02X\n", GCPhysAddr, bCmd));
-            break;
-        case FLASH_CMD_ERASE_SETUP:
-            if (bCmd == FLASH_CMD_ERASE_CONFIRM)
-            {
-                /* The current address determines the block to erase. */
-                uOffset = GCPhysAddr & (pThis->cbFlashSize - 1);
-                uOffset = uOffset & ~(pThis->cbBlockSize - 1);
-                memset(pThis->pbFlash + uOffset, 0xff, pThis->cbBlockSize);
-                LogFunc(("Erasing block at offset %u\n", uOffset));
-            }
-            else
-            {
-                /* Anything else is a command erorr. Transition to status read mode. */
-                LogFunc(("2st cycle erase command is %02X, should be confirm (%02X)\n", bCmd, FLASH_CMD_ERASE_CONFIRM));
-                pThis->bCmd = FLASH_CMD_STS_READ;
-                pThis->bStatus |= FLASH_STATUS_BWS | FLASH_STATUS_ES;
-            }
-            break;
-        default:
-            LogFunc(("2st cycle bad command %02X, current cmd %02X\n", bCmd, pThis->bCmd));
-            break;
-        }
-        pThis->cBusCycle = 0;
-    }
-    LogFlow(("flashMemWriteByte: write access at %08RGp: %#x rc=%Rrc\n", GCPhysAddr, bCmd, rc));
-//LogRel(("flashMemWriteByte: write access at %08RGp: %#x (cmd=%02X) rc=%Rrc\n", GCPhysAddr, bCmd, pThis->bCmd, rc));
-    return rc;
-}
-
-
-static int flashMemReadByte(PDEVFLASH pThis, RTGCPHYS GCPhysAddr, uint8_t *pbData)
-{
-    uint8_t bValue;
-    unsigned uOffset;
-    int rc = VINF_SUCCESS;
-
-    /* Reads are only defined in three states: Array read, status register read,
-     * and ID read.
-     */
-    switch (pThis->bCmd)
-    {
-    case FLASH_CMD_ARRAY_READ:
-        uOffset = GCPhysAddr & (pThis->cbFlashSize - 1);
-        bValue = pThis->pbFlash[uOffset];
-        LogFunc(("read byte at %08RGp: %02X\n", GCPhysAddr, bValue));
-        break;
-    case FLASH_CMD_STS_READ:
-        bValue = pThis->bStatus;
-        break;
-    case FLASH_CMD_READ_ID:
-        bValue = GCPhysAddr & 1 ?  RT_HI_U8(pThis->u16FlashId) : RT_LO_U8(pThis->u16FlashId);
-        break;
-    default:
-        bValue = 0xff;
-        break;
-    }
-    *pbData = bValue;
-
-    LogFlow(("flashMemReadByte: read access at %08RGp: %02X (cmd=%02X) rc=%Rrc\n", GCPhysAddr, bValue, pThis->bCmd, rc));
-//LogRel(("flashMemReadByte: read access at %08RGp: %02X (cmd=%02X) rc=%Rrc\n", GCPhysAddr, bValue, pThis->bCmd, rc));
-    return rc;
-}
-
 /** @callback_method_impl{FNIOMMIWRITE, Flash memory write} */
 PDMBOTHCBDECL(int) flashMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
 {
     PDEVFLASH pThis = PDMINS_2_DATA(pDevIns, PDEVFLASH);
-    int rc = VINF_SUCCESS;
-    const uint8_t *pu8Mem = (const uint8_t *)pv;
-    unsigned uOffset;
     RT_NOREF1(pvUser);
 
-    /* Writes may need to go back to R3. If more than one byte is being written (not likely!),
-     * just suck it up and take the trip to R3 immediately.
-     */
-    /** @todo Idea: We could buffer all writes in R0 and flush them out on a
-     *        timer. Probably not worth it.
-     */
-#ifndef IN_RING3
-    if (cb > 1)
-        return VINF_IOM_R3_IOPORT_WRITE;
-#endif
-
-    for (uOffset = 0; uOffset < cb; ++uOffset)
-    {
-        rc = flashMemWriteByte(pThis, GCPhysAddr + uOffset, pu8Mem[uOffset]);
-        if (!RT_SUCCESS(rc))
-            break;
-    }
-
-    LogFlow(("FlashMMIOWrite: completed write at %08RGp (LB %u): rc=%Rrc\n", GCPhysAddr, cb, rc));
-    return rc;
+    return flashWrite(&pThis->Core, GCPhysAddr - pThis->GCPhysFlashBase, pv, cb);
 }
 
@@ -267,22 +83,7 @@
 {
     PDEVFLASH pThis = PDMINS_2_DATA(pDevIns, PDEVFLASH);
-    int rc = VINF_SUCCESS;
-    unsigned uOffset;
-    uint8_t *pu8Mem;
     RT_NOREF1(pvUser);
 
-    /* Reading can always be done witout going back to R3. Reads do not
-     * change the device state and we always have the data.
-     */
-    pu8Mem = (uint8_t *)pv;
-    for (uOffset = 0; uOffset < cb; ++uOffset, ++pu8Mem)
-    {
-        rc = flashMemReadByte(pThis, GCPhysAddr + uOffset, pu8Mem);
-        if (!RT_SUCCESS(rc))
-            break;
-    }
-
-    LogFlow(("flashMMIORead: completed read at %08RGp (LB %u): rc=%Rrc\n", GCPhysAddr, cb, rc));
-    return rc;
+    return flashRead(&pThis->Core, GCPhysAddr - pThis->GCPhysFlashBase, pv, cb);
 }
 
@@ -296,20 +97,9 @@
     PDEVFLASH pThis = PDMINS_2_DATA(pDevIns, PDEVFLASH);
 
-    /* Save the device state. */
-    SSMR3PutU8(pSSM, pThis->bCmd);
-    SSMR3PutU8(pSSM, pThis->bStatus);
-    SSMR3PutU8(pSSM, pThis->cBusCycle);
-
-    /* Save the current configuration for validation purposes. */
-    SSMR3PutU16(pSSM, pThis->cbBlockSize);
-    SSMR3PutU16(pSSM, pThis->u16FlashId);
-
-    /* Save the current flash contents. */
-    SSMR3PutU32(pSSM, pThis->cbFlashSize);
-    SSMR3PutMem(pSSM, pThis->pbFlash, pThis->cbFlashSize);
-
-    pThis->fStateSaved = true;
-
-    return VINF_SUCCESS;
+    int rc = flashR3SsmSaveExec(&pThis->Core, pSSM);
+    if (RT_SUCCESS(rc))
+        pThis->fStateSaved = true;
+
+    return rc;
 }
 
@@ -325,32 +115,5 @@
         return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
 
-    /*
-     * Do the actual restoring.
-     */
-    if (uVersion == FLASH_SAVED_STATE_VERSION)
-    {
-        uint16_t    u16Val;
-        uint32_t    u32Val;
-
-        SSMR3GetU8(pSSM, &pThis->bCmd);
-        SSMR3GetU8(pSSM, &pThis->bStatus);
-        SSMR3GetU8(pSSM, &pThis->cBusCycle);
-
-        /* Make sure configuration didn't change behind our back. */
-        SSMR3GetU16(pSSM, &u16Val);
-        if (u16Val != pThis->cbBlockSize)
-            return VERR_SSM_LOAD_CONFIG_MISMATCH;
-        SSMR3GetU16(pSSM, &u16Val);
-        if (u16Val != pThis->u16FlashId)
-            return VERR_SSM_LOAD_CONFIG_MISMATCH;
-        SSMR3GetU32(pSSM, &u32Val);
-        if (u16Val != pThis->cbFlashSize)
-            return VERR_SSM_LOAD_CONFIG_MISMATCH;
-
-        /* Suck in the flash contents. */
-        SSMR3GetMem(pSSM, pThis->pbFlash, pThis->cbFlashSize);
-    }
-
-    return VINF_SUCCESS;
+    return flashR3SsmLoadExec(&pThis->Core, pSSM);
 }
 
@@ -362,10 +125,5 @@
     PDEVFLASH pThis = PDMINS_2_DATA(pDevIns, PDEVFLASH);
 
-    /*
-     * Initialize the device state.
-     */
-    pThis->bCmd      = FLASH_CMD_ARRAY_READ;
-    pThis->bStatus   = 0;
-    pThis->cBusCycle = 0;
+    flashR3Reset(&pThis->Core);
 }
 
@@ -380,14 +138,7 @@
     if (!pThis->fStateSaved)
     {
-        rc = RTFileSeek(pThis->hFlashFile, 0, RTFILE_SEEK_BEGIN, NULL);
-        rc = RTFileWrite(pThis->hFlashFile, pThis->pbFlash, pThis->cbFlashSize, NULL);
+        rc = flashR3SaveToFile(&pThis->Core, pThis->pszFlashFile);
         if (RT_FAILURE(rc))
-            LogRel(("flash: Failed to save flash file"));
-    }
-
-    if (pThis->pbFlash)
-    {
-        PDMDevHlpMMHeapFree(pDevIns, pThis->pbFlash);
-        pThis->pbFlash = NULL;
+            LogRel(("Flash: Failed to save flash file"));
     }
 
@@ -398,17 +149,7 @@
     }
 
+    flashR3Destruct(&pThis->Core);
     return VINF_SUCCESS;
 }
-
-/** @todo this does not really belong here; workaround for EFI failing to init empty flash. */
-static const uint8_t aHdrBegin[] = {
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x8D, 0x2B, 0xF1, 0xFF, 0x96, 0x76, 0x8B, 0x4C, 0xA9, 0x85, 0x27, 0x47, 0x07, 0x5B, 0x4F, 0x50,
-    0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x46, 0x56, 0x48, 0xFF, 0xFE, 0x04, 0x00,
-    0x48, 0x00, 0x19, 0xF9, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x36, 0xCF, 0xDD, 0x75, 0x32, 0x64, 0x41,
-    0x98, 0xB6, 0xFE, 0x85, 0x70, 0x7F, 0xFE, 0x7D, 0xB8, 0xDF, 0x00, 0x00, 0x5A, 0xFE, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
-};
 
 /**
@@ -432,5 +173,6 @@
 
     /* The default device ID is Intel 28F800SA. */
-    int rc = CFGMR3QueryU16Def(pCfg, "DeviceId", &pThis->u16FlashId, 0xA289);
+    uint16_t u16FlashId = 0;
+    int rc = CFGMR3QueryU16Def(pCfg, "DeviceId", &u16FlashId, 0xA289);
     if (RT_FAILURE(rc))
         return PDMDEV_SET_ERROR(pDevIns, rc,
@@ -444,5 +186,6 @@
 
     /* The default flash device size is 128K. */
-    rc = CFGMR3QueryU32Def(pCfg, "Size", &pThis->cbFlashSize, 128 * _1K);
+    uint32_t cbFlash = 0;
+    rc = CFGMR3QueryU32Def(pCfg, "Size", &cbFlash, 128 * _1K);
     if (RT_FAILURE(rc))
         return PDMDEV_SET_ERROR(pDevIns, rc,
@@ -450,15 +193,10 @@
 
     /* The default flash device block size is 4K. */
-    rc = CFGMR3QueryU16Def(pCfg, "BlockSize", &pThis->cbBlockSize, 4 * _1K);
+    uint16_t cbBlock = 0;
+    rc = CFGMR3QueryU16Def(pCfg, "BlockSize", &cbBlock, _4K);
     if (RT_FAILURE(rc))
         return PDMDEV_SET_ERROR(pDevIns, rc,
                                 N_("Configuration error: Querying \"BlockSize\" as an integer failed"));
 
-    /* The default flash device block size is 4K. */
-    rc = CFGMR3QueryU16Def(pCfg, "BlockSize", &pThis->cbBlockSize, 4 * _1K);
-    if (RT_FAILURE(rc))
-        return PDMDEV_SET_ERROR(pDevIns, rc,
-                                N_("Configuration error: Querying \"BlockSize\" as an integer failed"));
-
     rc = CFGMR3QueryStringAlloc(pCfg, "FlashFile", &pThis->pszFlashFile);
     if (RT_FAILURE(rc))
@@ -466,42 +204,24 @@
                                 N_("Configuration error: Querying \"FlashFile\" as a string failed"));
 
-    /* Try opening the backing file. */
-    rc = RTFileOpen(&pThis->hFlashFile, pThis->pszFlashFile, RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE);
-    if (RT_FAILURE(rc))
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to open flash file"));
-
-    /* Set up the static state, immutable at run-time. */
-    pThis->pbFlash = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbFlashSize);
-    if (!pThis->pbFlash)
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to allocate heap memory"));
-
-    size_t cbRead = 0;
-    rc = RTFileRead(pThis->hFlashFile, pThis->pbFlash, pThis->cbFlashSize, &cbRead);
-    if (RT_FAILURE(rc))
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to read flash file"));
-    Log(("Read %zu bytes from file (asked for %u)\n.", cbRead, pThis->cbFlashSize));
-
-    /* If the file didn't exist, or someone truncated it, we'll initialize
-     * the storage with default contents.
-     */
-    if (cbRead != pThis->cbFlashSize)
-    {
-        memset(pThis->pbFlash, 0xff, pThis->cbFlashSize);
-        memcpy(pThis->pbFlash, aHdrBegin, sizeof(aHdrBegin));
-        LogRel(("Only read %zu bytes from flash file (asked for %u). Initializing with defaults.\n", cbRead, pThis->cbFlashSize));
-    }
-
-    /* Reset the dynamic state.*/
-    flashReset(pDevIns);
+    rc = flashR3Init(&pThis->Core, pDevIns, u16FlashId, cbFlash, cbBlock);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("Flash: Failed to initialize core flash device"));
+
+    /* Try to load the flash content from file. */
+    rc = flashR3LoadFromFile(&pThis->Core, pThis->pszFlashFile);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("Flash: Failed to load flash content from given file"));
 
     /*
      * Register MMIO region.
      */
-    rc = PDMDevHlpMMIORegister(pDevIns, pThis->GCPhysFlashBase, pThis->cbFlashSize, NULL /*pvUser*/,
+    rc = PDMDevHlpMMIORegister(pDevIns, pThis->GCPhysFlashBase, cbFlash, NULL /*pvUser*/,
                                IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
                                flashMMIOWrite, flashMMIORead,
                                "Flash Memory");
     AssertRCReturn(rc, rc);
-    LogRel(("Registered %uKB flash at %RGp\n", pThis->cbFlashSize / _1K, pThis->GCPhysFlashBase));
+    LogRel(("Registered %uKB flash at %RGp\n", pThis->Core.cbFlashSize / _1K, pThis->GCPhysFlashBase));
 
     /*
Index: /trunk/src/VBox/Devices/EFI/FlashCore.cpp
===================================================================
--- /trunk/src/VBox/Devices/EFI/FlashCore.cpp	(revision 81250)
+++ /trunk/src/VBox/Devices/EFI/FlashCore.cpp	(revision 81250)
@@ -0,0 +1,388 @@
+/* $Id$ */
+/** @file
+ * DevFlash - A simple Flash device
+ *
+ * A simple non-volatile byte-wide (x8) memory device modeled after Intel 28F008
+ * FlashFile. See 28F008SA datasheet, Intel order number 290429-007.
+ *
+ * Implemented as an MMIO device attached directly to the CPU, not behind any
+ * bus. Typically mapped as part of the firmware image.
+ */
+
+/*
+ * Copyright (C) 2018-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_FLASH
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/file.h>
+
+#include "VBoxDD.h"
+#include "FlashCore.h"
+
+
+/*********************************************************************************************************************************
+*   Defined Constants And Macros                                                                                                 *
+*********************************************************************************************************************************/
+/** @name CUI (Command User Interface) Commands.
+ *  @{ */
+#define FLASH_CMD_ALT_WRITE             0x10
+#define FLASH_CMD_ERASE_SETUP           0x20
+#define FLASH_CMD_WRITE                 0x40
+#define FLASH_CMD_STS_CLEAR             0x50
+#define FLASH_CMD_STS_READ              0x70
+#define FLASH_CMD_READ_ID               0x90
+#define FLASH_CMD_ERASE_SUS_RES         0xB0
+#define FLASH_CMD_ERASE_CONFIRM         0xD0
+#define FLASH_CMD_ARRAY_READ            0xFF
+/** @} */
+
+/** @name Status register bits.
+ *  @{ */
+#define FLASH_STATUS_WSMS               0x80    /* Write State Machine Status, 1=Ready */
+#define FLASH_STATUS_ESS                0x40    /* Erase Suspend Status, 1=Suspended */
+#define FLASH_STATUS_ES                 0x20    /* Erase Status, 1=Error */
+#define FLASH_STATUS_BWS                0x10    /* Byte Write Status, 1=Error */
+#define FLASH_STATUS_VPPS               0x08    /* Vpp Status, 1=Low Vpp */
+/* The remaining bits 0-2 are reserved/unused */
+/** @} */
+
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+
+#ifdef IN_RING3 /* for now */
+
+static int flashMemWriteByte(PFLASHCORE pThis, uint32_t off, uint8_t bCmd)
+{
+    int rc = VINF_SUCCESS;
+    unsigned uOffset;
+
+    /* NB: Older datasheets (e.g. 28F008SA) suggest that for two-cycle commands like byte write or
+     * erase setup, the address is significant in both cycles, but do not explain what happens
+     * should the addresses not match. Newer datasheets (e.g. 28F008B3) clearly say that the address
+     * in the first byte cycle never matters. We prefer the latter interpretation.
+     */
+
+    if (pThis->cBusCycle == 0)
+    {
+        /* First bus write cycle, start processing a new command. Address is ignored. */
+        switch (bCmd)
+        {
+            case FLASH_CMD_ARRAY_READ:
+            case FLASH_CMD_STS_READ:
+            case FLASH_CMD_ERASE_SUS_RES:
+            case FLASH_CMD_READ_ID:
+                /* Single-cycle write commands, only change the current command. */
+                pThis->bCmd = bCmd;
+                break;
+            case FLASH_CMD_STS_CLEAR:
+                /* Status clear continues in read mode. */
+                pThis->bStatus = 0;
+                pThis->bCmd = FLASH_CMD_ARRAY_READ;
+                break;
+            case FLASH_CMD_WRITE:
+            case FLASH_CMD_ALT_WRITE:
+            case FLASH_CMD_ERASE_SETUP:
+                /* Two-cycle commands, advance the bus write cycle. */
+                pThis->bCmd = bCmd;
+                pThis->cBusCycle++;
+                break;
+            default:
+                LogFunc(("1st cycle command %02X, current cmd %02X\n", bCmd, pThis->bCmd));
+                break;
+        }
+    }
+    else
+    {
+        /* Second write of a two-cycle command. */
+        Assert(pThis->cBusCycle == 1);
+        switch (pThis->bCmd)
+        {
+            case FLASH_CMD_WRITE:
+            case FLASH_CMD_ALT_WRITE:
+                uOffset = off & (pThis->cbFlashSize - 1);
+                if (uOffset < pThis->cbFlashSize)
+                {
+                    pThis->pbFlash[uOffset] = bCmd;
+                    /* NB: Writes are instant and never fail. */
+                    LogFunc(("wrote byte to flash at %08RX32: %02X\n", off, bCmd));
+                }
+                else
+                    LogFunc(("ignoring write at %08RX32: %02X\n", off, bCmd));
+                break;
+            case FLASH_CMD_ERASE_SETUP:
+                if (bCmd == FLASH_CMD_ERASE_CONFIRM)
+                {
+                    /* The current address determines the block to erase. */
+                    uOffset = off & (pThis->cbFlashSize - 1);
+                    uOffset = uOffset & ~(pThis->cbBlockSize - 1);
+                    memset(pThis->pbFlash + uOffset, 0xff, pThis->cbBlockSize);
+                    LogFunc(("Erasing block at offset %u\n", uOffset));
+                }
+                else
+                {
+                    /* Anything else is a command erorr. Transition to status read mode. */
+                    LogFunc(("2st cycle erase command is %02X, should be confirm (%02X)\n", bCmd, FLASH_CMD_ERASE_CONFIRM));
+                    pThis->bCmd = FLASH_CMD_STS_READ;
+                    pThis->bStatus |= FLASH_STATUS_BWS | FLASH_STATUS_ES;
+                }
+                break;
+            default:
+                LogFunc(("2st cycle bad command %02X, current cmd %02X\n", bCmd, pThis->bCmd));
+                break;
+        }
+        pThis->cBusCycle = 0;
+    }
+    LogFlow(("flashMemWriteByte: write access at %08RX32: %#x rc=%Rrc\n", off, bCmd, rc));
+    return rc;
+}
+
+
+static int flashMemReadByte(PFLASHCORE pThis, uint32_t off, uint8_t *pbData)
+{
+    uint8_t bValue;
+    unsigned uOffset;
+    int rc = VINF_SUCCESS;
+
+    /*
+     * Reads are only defined in three states: Array read, status register read,
+     * and ID read.
+     */
+    switch (pThis->bCmd)
+    {
+        case FLASH_CMD_ARRAY_READ:
+            uOffset = off & (pThis->cbFlashSize - 1);
+            bValue = pThis->pbFlash[uOffset];
+            LogFunc(("read byte at %08RX32: %02X\n", off, bValue));
+            break;
+        case FLASH_CMD_STS_READ:
+            bValue = pThis->bStatus;
+            break;
+        case FLASH_CMD_READ_ID:
+            bValue = off & 1 ?  RT_HI_U8(pThis->u16FlashId) : RT_LO_U8(pThis->u16FlashId);
+            break;
+        default:
+            bValue = 0xff;
+            break;
+    }
+    *pbData = bValue;
+
+    LogFlow(("flashMemReadByte: read access at %08RX: %02X (cmd=%02X) rc=%Rrc\n", bValue, pThis->bCmd, rc));
+    return rc;
+}
+
+DECLHIDDEN(int) flashWrite(PFLASHCORE pThis, uint32_t off, const void *pv, size_t cb)
+{
+    int rc = VINF_SUCCESS;
+    const uint8_t *pu8Mem = (const uint8_t *)pv;
+
+#ifndef IN_RING3
+    if (cb > 1)
+        return VINF_IOM_R3_IOPORT_WRITE;
+#endif
+
+    for (uint32_t uOffset = 0; uOffset < cb; ++uOffset)
+    {
+        rc = flashMemWriteByte(pThis, off + uOffset, pu8Mem[uOffset]);
+        if (!RT_SUCCESS(rc))
+            break;
+    }
+
+    LogFlow(("flashWrite: completed write at %08RX32 (LB %u): rc=%Rrc\n", off, cb, rc));
+    return rc;
+}
+
+DECLHIDDEN(int) flashRead(PFLASHCORE pThis, uint32_t off, void *pv, size_t cb)
+{
+    int rc = VINF_SUCCESS;
+    uint8_t *pu8Mem = (uint8_t *)pv;
+
+    /*
+     * Reading can always be done witout going back to R3. Reads do not
+     * change the device state and we always have the data.
+     */
+    for (uint32_t uOffset = 0; uOffset < cb; ++uOffset, ++pu8Mem)
+    {
+        rc = flashMemReadByte(pThis, off + uOffset, pu8Mem);
+        if (!RT_SUCCESS(rc))
+            break;
+    }
+
+    LogFlow(("flashRead: completed read at %08RX32 (LB %u): rc=%Rrc\n", off, cb, rc));
+    return rc;
+}
+
+#endif /* IN_RING3 for now */
+
+#ifdef IN_RING3
+
+DECLHIDDEN(int) flashR3Init(PFLASHCORE pThis, PPDMDEVINS pDevIns, uint16_t idFlashDev, uint32_t cbFlash, uint16_t cbBlock)
+{
+    pThis->pDevIns     = pDevIns;
+    pThis->u16FlashId  = idFlashDev;
+    pThis->cbBlockSize = cbBlock;
+    pThis->cbFlashSize = cbFlash;
+
+    /* Set up the flash data. */
+    pThis->pbFlash = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbFlashSize);
+    if (!pThis->pbFlash)
+        return PDMDEV_SET_ERROR(pDevIns, VERR_NO_MEMORY, N_("Failed to allocate heap memory"));
+
+    /* Default value for empty flash. */
+    memset(pThis->pbFlash, 0xff, pThis->cbFlashSize);
+
+    /* Reset the dynamic state.*/
+    flashR3Reset(pThis);
+    return VINF_SUCCESS;
+}
+
+DECLHIDDEN(void) flashR3Destruct(PFLASHCORE pThis)
+{
+    if (pThis->pbFlash)
+    {
+        PDMDevHlpMMHeapFree(pThis->pDevIns, pThis->pbFlash);
+        pThis->pbFlash = NULL;
+    }
+}
+
+DECLHIDDEN(int) flashR3LoadFromFile(PFLASHCORE pThis, const char *pszFilename)
+{
+    RTFILE hFlashFile = NIL_RTFILE;
+
+    int rc = RTFileOpen(&hFlashFile, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pThis->pDevIns, rc, N_("Failed to open flash file"));
+
+    size_t cbRead = 0;
+    rc = RTFileRead(hFlashFile, pThis->pbFlash, pThis->cbFlashSize, &cbRead);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pThis->pDevIns, rc, N_("Failed to read flash file"));
+    Log(("Read %zu bytes from file (asked for %u)\n.", cbRead, pThis->cbFlashSize));
+
+    RTFileClose(hFlashFile);
+    return VINF_SUCCESS;
+}
+
+DECLHIDDEN(int) flashR3LoadFromBuf(PFLASHCORE pThis, void *pvBuf, size_t cbBuf)
+{
+    AssertReturn(pThis->cbFlashSize >= cbBuf, VERR_BUFFER_OVERFLOW);
+
+    memcpy(pThis->pbFlash, pvBuf, RT_MIN(cbBuf, pThis->cbFlashSize));
+    return VINF_SUCCESS;
+}
+
+DECLHIDDEN(int) flashR3SaveToFile(PFLASHCORE pThis, const char *pszFilename)
+{
+    RTFILE hFlashFile = NIL_RTFILE;
+
+    int rc = RTFileOpen(&hFlashFile, pszFilename, RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pThis->pDevIns, rc, N_("Failed to open flash file"));
+
+    rc = RTFileWrite(hFlashFile, pThis->pbFlash, pThis->cbFlashSize, NULL);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pThis->pDevIns, rc, N_("Failed to write flash file"));
+
+    RTFileClose(hFlashFile);
+    return VINF_SUCCESS;
+}
+
+DECLHIDDEN(int) flashR3SaveToBuf(PFLASHCORE pThis, void *pvBuf, size_t cbBuf)
+{
+    AssertReturn(pThis->cbFlashSize <= cbBuf, VERR_BUFFER_OVERFLOW);
+
+    memcpy(pvBuf, pThis->pbFlash, RT_MIN(cbBuf, pThis->cbFlashSize));
+    return VINF_SUCCESS;
+}
+
+DECLHIDDEN(void) flashR3Reset(PFLASHCORE pThis)
+{
+    /*
+     * Initialize the device state.
+     */
+    pThis->bCmd      = FLASH_CMD_ARRAY_READ;
+    pThis->bStatus   = 0;
+    pThis->cBusCycle = 0;
+}
+
+DECLHIDDEN(int) flashR3SsmSaveExec(PFLASHCORE pThis, PSSMHANDLE pSSM)
+{
+    SSMR3PutU32(pSSM, FLASH_SAVED_STATE_VERSION);
+
+    /* Save the device state. */
+    SSMR3PutU8(pSSM, pThis->bCmd);
+    SSMR3PutU8(pSSM, pThis->bStatus);
+    SSMR3PutU8(pSSM, pThis->cBusCycle);
+
+    /* Save the current configuration for validation purposes. */
+    SSMR3PutU16(pSSM, pThis->cbBlockSize);
+    SSMR3PutU16(pSSM, pThis->u16FlashId);
+
+    /* Save the current flash contents. */
+    SSMR3PutU32(pSSM, pThis->cbFlashSize);
+    SSMR3PutMem(pSSM, pThis->pbFlash, pThis->cbFlashSize);
+
+    return VINF_SUCCESS;
+}
+
+DECLHIDDEN(int) flashR3SsmLoadExec(PFLASHCORE pThis, PSSMHANDLE pSSM)
+{
+    uint32_t uVersion = FLASH_SAVED_STATE_VERSION;
+    int rc = SSMR3GetU32(pSSM, &uVersion);
+    AssertRCReturn(rc, rc);
+
+    /*
+     * Do the actual restoring.
+     */
+    if (uVersion == FLASH_SAVED_STATE_VERSION)
+    {
+        uint16_t    u16Val;
+        uint32_t    u32Val;
+
+        SSMR3GetU8(pSSM, &pThis->bCmd);
+        SSMR3GetU8(pSSM, &pThis->bStatus);
+        SSMR3GetU8(pSSM, &pThis->cBusCycle);
+
+        /* Make sure configuration didn't change behind our back. */
+        SSMR3GetU16(pSSM, &u16Val);
+        if (u16Val != pThis->cbBlockSize)
+            return VERR_SSM_LOAD_CONFIG_MISMATCH;
+        SSMR3GetU16(pSSM, &u16Val);
+        if (u16Val != pThis->u16FlashId)
+            return VERR_SSM_LOAD_CONFIG_MISMATCH;
+        SSMR3GetU32(pSSM, &u32Val);
+        if (u16Val != pThis->cbFlashSize)
+            return VERR_SSM_LOAD_CONFIG_MISMATCH;
+
+        /* Suck in the flash contents. */
+        SSMR3GetMem(pSSM, pThis->pbFlash, pThis->cbFlashSize);
+    }
+    else
+        rc = VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+
+    return rc;
+}
+
+#endif /* IN_RING3 */
+
+#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
Index: /trunk/src/VBox/Devices/EFI/FlashCore.h
===================================================================
--- /trunk/src/VBox/Devices/EFI/FlashCore.h	(revision 81250)
+++ /trunk/src/VBox/Devices/EFI/FlashCore.h	(revision 81250)
@@ -0,0 +1,204 @@
+/* $Id$ */
+/** @file
+ * A simple Flash device
+ *
+ * A simple non-volatile byte-wide (x8) memory device modeled after Intel 28F008
+ * FlashFile. See 28F008SA datasheet, Intel order number 290429-007.
+ *
+ * Implemented as an MMIO device attached directly to the CPU, not behind any
+ * bus. Typically mapped as part of the firmware image.
+ */
+
+/*
+ * Copyright (C) 2018-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_EFI_FlashCore_h
+#define VBOX_INCLUDED_SRC_EFI_FlashCore_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/file.h>
+
+#include "VBoxDD.h"
+
+RT_C_DECLS_BEGIN
+
+/*********************************************************************************************************************************
+*   Defined Constants And Macros                                                                                                 *
+*********************************************************************************************************************************/
+/** The current version of the saved state. */
+#define FLASH_SAVED_STATE_VERSION           1
+
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
+/**
+ * The flash device core structure.
+ */
+typedef struct FLASHCORE
+{
+    /** Owning device instance. */
+    PPDMDEVINS          pDevIns;
+    /** The current command. */
+    uint8_t             bCmd;
+    /** The status register. */
+    uint8_t             bStatus;
+    /** Current bus cycle. */
+    uint8_t             cBusCycle;
+
+    uint8_t             uPadding0;
+
+    /* The following state does not change at runtime.*/
+    /** Manufacturer (high byte) and device (low byte) ID. */
+    uint16_t            u16FlashId;
+    /** The configured block size of the device. */
+    uint16_t            cbBlockSize;
+    /** The flash memory region size.  */
+    uint32_t            cbFlashSize;
+    /** The actual flash memory data.  */
+    uint8_t             *pbFlash;
+    /** When set, indicates the state was saved. */
+    bool                fStateSaved;
+} FLASHCORE;
+
+/** Pointer to the Flash device state. */
+typedef FLASHCORE *PFLASHCORE;
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+/**
+ * Performs a write to the given flash offset.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The UART core instance.
+ * @param   off                 Offset to start writing to.
+ * @param   pv                  The value to write.
+ * @param   cb                  Number of bytes to write.
+ */
+DECLHIDDEN(int) flashWrite(PFLASHCORE pThis, uint32_t off, const void *pv, size_t cb);
+
+/**
+ * Performs a read from the given flash offset.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The UART core instance.
+ * @param   off                 Offset to start reading from.
+ * @param   pv                  Where to store the read data.
+ * @param   cb                  Number of bytes to read.
+ */
+DECLHIDDEN(int) flashRead(PFLASHCORE pThis, uint32_t off, void *pv, size_t cb);
+
+# ifdef IN_RING3
+
+/**
+ * Initialiizes the given flash device instance.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The flash device core instance.
+ * @param   pDevIns             Pointer to the owning device instance.
+ * @param   idFlashDev          The flash device ID.
+ * @param   GCPhysFlashBase     Base MMIO address where the flash is located.
+ * @param   cbFlash             Size of the flash device in bytes.
+ * @param   cbBlock             Size of a flash block.
+ */
+DECLHIDDEN(int) flashR3Init(PFLASHCORE pThis, PPDMDEVINS pDevIns, uint16_t idFlashDev, uint32_t cbFlash, uint16_t cbBlock);
+
+/**
+ * Destroys the given flash device instance.
+ *
+ * @returns nothing.
+ * @param   pThis               The flash device core instance.
+ */
+DECLHIDDEN(void) flashR3Destruct(PFLASHCORE pThis);
+
+/**
+ * Loads the flash content from the given file.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The flash device core instance.
+ * @param   pszFilename         The file to load the flash content from.
+ */
+DECLHIDDEN(int) flashR3LoadFromFile(PFLASHCORE pThis, const char *pszFilename);
+
+/**
+ * Loads the flash content from the given buffer.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The flash device core instance.
+ * @param   pvBuf               The buffer to load the content from.
+ * @param   cbBuf               Size of the buffer in bytes.
+ */
+DECLHIDDEN(int) flashR3LoadFromBuf(PFLASHCORE pThis, void *pvBuf, size_t cbBuf);
+
+/**
+ * Saves the flash content to the given file.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The flash device core instance.
+ * @param   pszFilename         The file to save the flash content to.
+ */
+DECLHIDDEN(int) flashR3SaveToFile(PFLASHCORE pThis, const char *pszFilename);
+
+/**
+ * Saves the flash content to the given buffer.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The flash device core instance.
+ * @param   pvBuf               The buffer to save the content to.
+ * @param   cbBuf               Size of the buffer in bytes.
+ */
+DECLHIDDEN(int) flashR3SaveToBuf(PFLASHCORE pThis, void *pvBuf, size_t cbBuf);
+
+/**
+ * Resets the dynamic part of the flash device state.
+ *
+ * @returns nothing.
+ * @param   pThis               The flash device core instance.
+ */
+DECLHIDDEN(void) flashR3Reset(PFLASHCORE pThis);
+
+/**
+ * Saves the flash device state to the given SSM handle.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The flash device core instance.
+ * @param   pSSM                The SSM handle to save to.
+ */
+DECLHIDDEN(int) flashR3SsmSaveExec(PFLASHCORE pThis, PSSMHANDLE pSSM);
+
+/**
+ * Loads the flash device state from the given SSM handle.
+ *
+ * @returns VBox status code.
+ * @param   pThis               The flash device core instance.
+ * @param   pSSM                The SSM handle to load from.
+ */
+DECLHIDDEN(int) flashR3SsmLoadExec(PFLASHCORE pThis, PSSMHANDLE pSSM);
+
+# endif /* IN_RING3 */
+
+#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_EFI_FlashCore_h */
+
Index: /trunk/src/VBox/Devices/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Devices/Makefile.kmk	(revision 81249)
+++ /trunk/src/VBox/Devices/Makefile.kmk	(revision 81250)
@@ -156,4 +156,5 @@
 	EFI/DevSmc.cpp \
 	EFI/DevFlash.cpp \
+	EFI/FlashCore.cpp \
  	Graphics/DevVGA.cpp \
  	Storage/DevATA.cpp \
