Index: /trunk/src/VBox/Devices/Misc/DevFlashCFI.cpp
===================================================================
--- /trunk/src/VBox/Devices/Misc/DevFlashCFI.cpp	(revision 99942)
+++ /trunk/src/VBox/Devices/Misc/DevFlashCFI.cpp	(revision 99943)
@@ -41,4 +41,5 @@
 #include <iprt/string.h>
 #include <iprt/file.h>
+#include <iprt/uuid.h>
 
 #include "VBoxDD.h"
@@ -80,4 +81,7 @@
 /** @} */
 
+/** The namespace the NVRAM content is stored under (historical reasons). */
+#define FLASH_CFI_VFS_NAMESPACE                 "efi"
+
 
 /*********************************************************************************************************************************
@@ -118,4 +122,24 @@
     /** The handle to the MMIO region. */
     IOMMMIOHANDLE           hMmio;
+
+    /** The offset of the block to write. */
+    uint32_t                offBlock;
+    /** Number of bytes to write. */
+    uint32_t                cbWrite;
+    /** The word buffer for the buffered program command (32 16-bit words for each emulated chip). */
+    uint8_t                 abProgramBuf[2 * 32 * sizeof(uint16_t)];
+
+    /**
+     * NVRAM port - LUN\#0.
+     */
+    struct
+    {
+        /** The base interface we provide the NVRAM driver. */
+        PDMIBASE            IBase;
+        /** The NVRAM driver base interface. */
+        PPDMIBASE           pDrvBase;
+        /** The VFS interface of the driver below for NVRAM state loading and storing. */
+        PPDMIVFSCONNECTOR   pDrvVfs;
+    } Lun0;
 } DEVFLASHCFI;
 /** Pointer to the Flash device state. */
@@ -158,5 +182,6 @@
             case FLASH_CFI_CMD_BUFFERED_PROGRAM_SETUP:
                 Assert(pThis->bStatus & FLASH_CFI_SR_PROGRAM_ERASE);
-                pThis->u16Cmd = u32Val;
+                pThis->u16Cmd   = u32Val;
+                pThis->offBlock = (uint32_t)off;
                 pThis->cBusCycle++;
                 break;
@@ -178,4 +203,5 @@
                 pThis->u16Cmd = FLASH_CFI_CMD_READ_STATUS_REG;
                 pThis->cBusCycle = 0;
+                memset(pThis->pbFlash + off, 0xff, RT_MIN(pThis->cbFlashSize - off, pThis->cbBlockSize));
                 break;
             }
@@ -183,6 +209,10 @@
             {
                 /* Receives the number of words to be transfered. */
-                pThis->cWordsTransfered = u32Val & 0xffff;
-                pThis->cBusCycle++;
+                pThis->cWordsTransfered = (u32Val & 0xffff) + 1;
+                pThis->cbWrite = pThis->cWordsTransfered * 2 * sizeof(uint16_t);
+                if (pThis->cbWrite <= sizeof(pThis->abProgramBuf))
+                    pThis->cBusCycle++;
+                else
+                    AssertReleaseFailed();
                 break;
             }
@@ -201,12 +231,23 @@
                     if ((u32Val & 0xffff) == FLASH_CFI_CMD_BUFFERED_PROGRAM_CONFIRM)
                     {
-                        /** @todo Write out data to flash. */
+                        if (pThis->offBlock + pThis->cbWrite <= pThis->cbFlashSize)
+                            memcpy(pThis->pbFlash + pThis->offBlock, &pThis->abProgramBuf[0], pThis->cbWrite);
                         /* Reset to read array. */
                         pThis->cBusCycle = 0;
                         pThis->u16Cmd    = FLASH_CFI_CMD_ARRAY_READ;
                     }
+                    else
+                        AssertReleaseFailed();
                 }
                 else
+                {
+                    if (   off >= pThis->offBlock
+                        && (off + cb >= pThis->offBlock)
+                        && ((off + cb) - pThis->offBlock) <= sizeof(pThis->abProgramBuf))
+                        memcpy(&pThis->abProgramBuf[off - pThis->offBlock], pv, cb);
+                    else
+                        AssertReleaseFailed();
                     pThis->cWordsTransfered--;
+                }
                 break;
             }
@@ -265,4 +306,17 @@
 
 
+/**
+ * @copydoc(PDMIBASE::pfnQueryInterface)
+ */
+static DECLCALLBACK(void *) flashR3QueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+    LogFlowFunc(("ENTER: pIBase=%p pszIID=%p\n", pInterface, pszIID));
+    PDEVFLASHCFI pThis = RT_FROM_MEMBER(pInterface, DEVFLASHCFI, Lun0.IBase);
+
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Lun0.IBase);
+    return NULL;
+}
+
+
 #if 0 /** @todo Later */
 /**
@@ -305,4 +359,37 @@
     pThis->bStatus   = FLASH_CFI_SR_PROGRAM_ERASE; /* Prgram/Erase controller is inactive. */
     pThis->cBusCycle = 0;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnPowerOff}
+ */
+static DECLCALLBACK(void) flashR3PowerOff(PPDMDEVINS pDevIns)
+{
+    PDEVFLASHCFI pThis = PDMDEVINS_2_DATA(pDevIns, PDEVFLASHCFI);
+
+    if (pThis->Lun0.pDrvVfs)
+    {
+        AssertPtr(pThis->pszFlashFile);
+        int rc = pThis->Lun0.pDrvVfs->pfnWriteAll(pThis->Lun0.pDrvVfs, FLASH_CFI_VFS_NAMESPACE, pThis->pszFlashFile,
+                                                  pThis->pbFlash, pThis->cbFlashSize);
+        if (RT_FAILURE(rc))
+            LogRel(("EFI: Failed to save flash file to NVRAM store: %Rrc\n", rc));
+    }
+    else if (pThis->pszFlashFile)
+    {
+        RTFILE hFlashFile = NIL_RTFILE;
+
+        int rc = RTFileOpen(&hFlashFile, pThis->pszFlashFile, RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE);
+        if (RT_SUCCESS(rc))
+        {
+            rc = RTFileWrite(hFlashFile, pThis->pbFlash, pThis->cbFlashSize, NULL);
+            RTFileClose(hFlashFile);
+            if (RT_FAILURE(rc))
+                PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to write flash file"));
+        }
+        else
+            PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to open flash file"));
+    }
 }
 
@@ -353,4 +440,10 @@
 
     Assert(iInstance == 0); RT_NOREF1(iInstance);
+
+    /*
+     * Initalize the basic variables so that the destructor always works.
+     */
+    pThis->Lun0.IBase.pfnQueryInterface = flashR3QueryInterface;
+
 
     /*
@@ -376,6 +469,6 @@
 
     /* The default flash device size is 128K. */
-    uint32_t cbFlash = 0;
-    rc = pHlp->pfnCFGMQueryU32Def(pCfg, "Size", &cbFlash, 128 * _1K);
+    uint64_t cbFlash = 0;
+    rc = pHlp->pfnCFGMQueryU64Def(pCfg, "Size", &cbFlash, 128 * _1K);
     if (RT_FAILURE(rc))
         return PDMDEV_SET_ERROR(pDevIns, rc,
@@ -412,16 +505,47 @@
     flashR3Reset(pDevIns);
 
-    RTFILE hFlashFile = NIL_RTFILE;
-    rc = RTFileOpen(&hFlashFile, pThis->pszFlashFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
-    if (RT_FAILURE(rc))
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to open flash file"));
-
-    size_t cbRead = 0;
-    rc = RTFileRead(hFlashFile, pThis->pbFlash, pThis->cbFlashSize, &cbRead);
-    RTFileClose(hFlashFile);
-    if (RT_FAILURE(rc))
-        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to read flash file"));
-    LogRel(("flash-cfi#%u: Read %zu bytes from file (asked for %u)\n.", cbRead, pThis->cbFlashSize, iInstance));
-
+    /*
+     * NVRAM storage.
+     */
+    rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->Lun0.IBase, &pThis->Lun0.pDrvBase, "NvramStorage");
+    if (RT_SUCCESS(rc))
+    {
+        pThis->Lun0.pDrvVfs = PDMIBASE_QUERY_INTERFACE(pThis->Lun0.pDrvBase, PDMIVFSCONNECTOR);
+        if (!pThis->Lun0.pDrvVfs)
+            return PDMDevHlpVMSetError(pDevIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("NVRAM storage driver is missing VFS interface below"));
+    }
+    else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
+        rc = VINF_SUCCESS; /* Missing driver is no error condition. */
+    else
+        return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, N_("Can't attach Nvram Storage driver"));
+
+    if (pThis->Lun0.pDrvVfs)
+    {
+        AssertPtr(pThis->pszFlashFile);
+        rc = pThis->Lun0.pDrvVfs->pfnQuerySize(pThis->Lun0.pDrvVfs, FLASH_CFI_VFS_NAMESPACE, pThis->pszFlashFile, &cbFlash);
+        if (RT_SUCCESS(rc))
+        {
+            if (cbFlash <= pThis->cbFlashSize)
+                rc = pThis->Lun0.pDrvVfs->pfnReadAll(pThis->Lun0.pDrvVfs, FLASH_CFI_VFS_NAMESPACE, pThis->pszFlashFile,
+                                                     pThis->pbFlash, pThis->cbFlashSize);
+            else
+                return PDMDEV_SET_ERROR(pDevIns, VERR_BUFFER_OVERFLOW, N_("Configured flash size is too small to fit the saved NVRAM content"));
+        }
+    }
+    else if (pThis->pszFlashFile)
+    {
+        RTFILE hFlashFile = NIL_RTFILE;
+        rc = RTFileOpen(&hFlashFile, pThis->pszFlashFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+        if (RT_FAILURE(rc))
+            return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to open flash file"));
+
+        size_t cbRead = 0;
+        rc = RTFileRead(hFlashFile, pThis->pbFlash, pThis->cbFlashSize, &cbRead);
+        RTFileClose(hFlashFile);
+        if (RT_FAILURE(rc))
+            return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to read flash file"));
+
+        LogRel(("flash-cfi#%u: Read %zu bytes from file (asked for %u)\n.", cbRead, pThis->cbFlashSize, iInstance));
+    }
 
     /*
@@ -479,5 +603,5 @@
     /* .pfnQueryInterface = */      NULL,
     /* .pfnInitComplete = */        NULL,
-    /* .pfnPowerOff = */            NULL,
+    /* .pfnPowerOff = */            flashR3PowerOff,
     /* .pfnSoftReset = */           NULL,
     /* .pfnReserved0 = */           NULL,
