Index: /trunk/include/VBox/vmapi.h
===================================================================
--- /trunk/include/VBox/vmapi.h	(revision 233)
+++ /trunk/include/VBox/vmapi.h	(revision 234)
@@ -88,5 +88,5 @@
  *    @code
  *    return VM_SET_ERROR(pVM, VERR_OF_YOUR_CHOICE, "descriptive message");
- *    @codeend
+ *    @endcode
  * @param   pVM             VM handle. Must be non-NULL.
  * @param   rc              VBox status code.
@@ -104,5 +104,5 @@
  *    @code
  *    return VM_SET_ERROR(pVM, VERR_OF_YOUR_CHOICE, "descriptive message");
- *    @codeend
+ *    @endcode
  * @param   pVM             VM handle. Must be non-NULL.
  * @param   rc              VBox status code.
@@ -121,5 +121,5 @@
  *    @code
  *    return VM_SET_ERROR(pVM, VERR_OF_YOUR_CHOICE, "descriptive message");
- *    @codeend
+ *    @endcode
  * @param   pVM             VM handle.
  * @param   rc              VBox status code.
@@ -128,4 +128,79 @@
  */
 #define VM_SET_ERROR(pVM, rc, pszMessage)   (VMSetError(pVM, rc, RT_SRC_POS, pszMessage))
+
+
+/**
+ * VM runtime error callback function.
+ * See VMSetRuntimeError for the detailed description of parameters.
+ *
+ * @param   pVM             The VM handle.
+ * @param   pvUser          The user argument.
+ * @param   fFatal          Whether it is a fatal error or not.
+ * @param   pszErrorID      Error ID string.
+ * @param   pszFormat       Error message format string.
+ * @param   args            Error message arguments.
+ */
+typedef DECLCALLBACK(void) FNVMATRUNTIMEERROR(PVM pVM, void *pvUser, bool fFatal,
+                                              const char *pszErrorID,
+                                              const char *pszFormat, va_list args);
+/** Pointer to a VM runtime error callback. */
+typedef FNVMATRUNTIMEERROR *PFNVMATRUNTIMEERROR;
+
+/**
+ * Sets the runtime error message.
+ * As opposed VMSetError(), this method is intended to inform the VM user about
+ * errors and error-like conditions that happen at an arbitrary point during VM
+ * execution (like "host memory low" or "out of host disk space").
+ *
+ * The @a fFatal parameter defines whether the error is fatal or not. If it is
+ * true, then it is expected that the caller has already paused the VM execution
+ * before calling this method. The VM user is supposed to power off the VM
+ * immediately after it has received the runtime error notification via the
+ * FNVMATRUNTIMEERROR callback.
+ *
+ * If @a fFatal is false, then the paused state of the VM defines the kind of
+ * the error. If the VM is paused before calling this method, it means that
+ * the VM user may try to fix the error condition (i.e. free more host memory)
+ * and then resume the VM execution. If the VM is not paused before calling
+ * this method, it means that the given error is a warning about an error
+ * condition that may happen soon but that doesn't directly affect the
+ * VM execution by the time of the call.
+ *
+ * The @a pszErrorID parameter defines an unique error identificator.
+ * It is used by the front-ends to show a proper message to the end user
+ * containig possible actions (for example, Retry/Ignore). For this reason,
+ * an error ID assigned once to some particular error condition should not
+ * change in the future. The format of this parameter is "SomeErrorCondition". 
+ *
+ * @param   pVM             VM handle. Must be non-NULL.
+ * @param   fFatal          Whether it is a fatal error or not.
+ * @param   pszErrorID      Error ID string.
+ * @param   pszFormat       Error message format string.
+ * @param   ...             Error message arguments.
+ *
+ * @return  VBox status code (whether the error has been successfully set
+ *          and delivered to callbacks or not).
+ *
+ * @thread  Any
+ */
+VMDECL(int) VMSetRuntimeError(PVM pVM, bool fFatal, const char *pszErrorID,
+                              const char *pszFormat, ...);
+
+/**
+ * va_list version of VMSetRuntimeError.
+ *
+ * @param   pVM             VM handle. Must be non-NULL.
+ * @param   fFatal          Whether it is a fatal error or not.
+ * @param   pszErrorID      Error ID string.
+ * @param   pszFormat       Error message format string.
+ * @param   args            Error message arguments.
+ *
+ * @return  VBox status code (whether the error has been successfully set
+ *          and delivered to callbacks or not).
+ *
+ * @thread  Any
+ */
+VMDECL(int) VMSetRuntimeErrorV(PVM pVM, bool fFatal, const char *pszErrorID,
+                               const char *pszFormat, va_list args);
 
 
@@ -632,4 +707,35 @@
 VMR3DECL(void) VMR3SetErrorWorker(PVM pVM);
 
+/**
+ * Registers a VM runtime error callback.
+ *
+ * @returns VBox status code.
+ * @param   pVM                 The VM handle.
+ * @param   pfnAtRuntimeError   Pointer to callback.
+ * @param   pvUser              User argument.
+ * @thread  Any.
+ */
+VMR3DECL(int)   VMR3AtRuntimeErrorRegister(PVM pVM, PFNVMATRUNTIMEERROR pfnAtRuntimeError, void *pvUser);
+
+/**
+ * Deregisters a VM runtime error callback.
+ *
+ * @returns VBox status code.
+ * @param   pVM                 The VM handle.
+ * @param   pfnAtRuntimeError   Pointer to callback.
+ * @param   pvUser              User argument.
+ * @thread  Any.
+ */
+VMR3DECL(int)   VMR3AtRuntimeErrorDeregister(PVM pVM, PFNVMATRUNTIMEERROR pfnAtRuntimeError, void *pvUser);
+
+/**
+ * This is a worker function for GC and Ring-0 calls to VMSetRuntimeError and VMSetRuntimeErrorV.
+ * The message is found in VMINT.
+ *
+ * @param   pVM             The VM handle.
+ * @thread  EMT.
+ */
+VMR3DECL(void) VMR3SetRuntimeErrorWorker(PVM pVM);
+
 
 /**
Index: /trunk/include/VBox/vmm.h
===================================================================
--- /trunk/include/VBox/vmm.h	(revision 233)
+++ /trunk/include/VBox/vmm.h	(revision 234)
@@ -86,4 +86,6 @@
     /** Set the VM error message. */
     VMMCALLHOST_VM_SET_ERROR,
+    /** Set the VM runtime error message. */
+    VMMCALLHOST_VM_SET_RUNTIME_ERROR,
     /** The usual 32-bit hack. */
     VMMCALLHOST_32BIT_HACK = 0x7fffffff
Index: /trunk/src/VBox/VMM/EM.cpp
===================================================================
--- /trunk/src/VBox/VMM/EM.cpp	(revision 233)
+++ /trunk/src/VBox/VMM/EM.cpp	(revision 234)
@@ -3385,4 +3385,8 @@
                  */
                 case EMSTATE_REM:
+#if 0
+                    /* simulate a runtime error */
+                    VMSetRuntimeError (pVM, true, "simulatedError", "pVM=%p", pVM);
+#endif
                     rc = emR3RemExecute(pVM, &fFFDone);
                     Log2(("EMR3ExecuteVM: emR3RemExecute -> %Vrc\n", rc));
Index: /trunk/src/VBox/VMM/VM.cpp
===================================================================
--- /trunk/src/VBox/VMM/VM.cpp	(revision 233)
+++ /trunk/src/VBox/VMM/VM.cpp	(revision 234)
@@ -116,4 +116,6 @@
 static DECLCALLBACK(int) vmR3AtErrorRegister(PVM pVM, PFNVMATERROR pfnAtError, void *pvUser);
 static DECLCALLBACK(int) vmR3AtErrorDeregister(PVM pVM, PFNVMATERROR pfnAtError, void *pvUser);
+static DECLCALLBACK(int) vmR3AtRuntimeErrorRegister(PVM pVM, PFNVMATRUNTIMEERROR pfnAtRuntimeError, void *pvUser);
+static DECLCALLBACK(int) vmR3AtRuntimeErrorDeregister(PVM pVM, PFNVMATRUNTIMEERROR pfnAtRuntimeError, void *pvUser);
 
 
@@ -202,4 +204,5 @@
             pVM->vm.s.ppAtStateNext = &pVM->vm.s.pAtState;
             pVM->vm.s.ppAtErrorNext = &pVM->vm.s.pAtError;
+            pVM->vm.s.ppAtRuntimeErrorNext = &pVM->vm.s.pAtRuntimeError;
             rc = RTSemEventCreate(&pVM->vm.s.EventSemWait);
             if (VBOX_FAILURE(rc))
@@ -2480,4 +2483,5 @@
         if (pErr->offFile)
             pszFile = (const char *)pErr + pErr->offFile;
+        iLine = pErr->iLine;
         if (pErr->offFunction)
             pszFunction = (const char *)pErr + pErr->offFunction;
@@ -2527,2 +2531,253 @@
 }
 
+
+/**
+ * Registers a VM runtime error callback.
+ *
+ * @returns VBox status code.
+ * @param   pVM                 The VM handle.
+ * @param   pfnAtRuntimeError   Pointer to callback.
+ * @param   pvUser              User argument.
+ * @thread  Any.
+ */
+VMR3DECL(int)   VMR3AtRuntimeErrorRegister(PVM pVM, PFNVMATRUNTIMEERROR pfnAtRuntimeError, void *pvUser)
+{
+    LogFlow(("VMR3AtRuntimeErrorRegister: pfnAtRuntimeError=%p pvUser=%p\n", pfnAtRuntimeError, pvUser));
+
+    /*
+     * Validate input.
+     */
+    if (!pfnAtRuntimeError)
+    {
+        AssertMsgFailed(("callback is required\n"));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    /*
+     * Make sure we're in EMT (to avoid the logging).
+     */
+    PVMREQ pReq;
+    int rc = VMR3ReqCall(pVM, &pReq, RT_INDEFINITE_WAIT, (PFNRT)vmR3AtRuntimeErrorRegister, 3, pVM, pfnAtRuntimeError, pvUser);
+    if (VBOX_FAILURE(rc))
+        return rc;
+    rc = pReq->iStatus;
+    VMR3ReqFree(pReq);
+
+    LogFlow(("VMR3AtRuntimeErrorRegister: returns %Vrc\n", rc));
+    return rc;
+}
+
+
+/**
+ * Registers a VM runtime error callback.
+ *
+ * @returns VBox status code.
+ * @param   pVM                 The VM handle.
+ * @param   pfnAtRuntimeError   Pointer to callback.
+ * @param   pvUser              User argument.
+ * @thread  EMT
+ */
+static DECLCALLBACK(int)    vmR3AtRuntimeErrorRegister(PVM pVM, PFNVMATRUNTIMEERROR pfnAtRuntimeError, void *pvUser)
+{
+    /*
+     * Allocate a new record.
+     */
+
+    PVMATRUNTIMEERROR pNew = (PVMATRUNTIMEERROR)MMR3HeapAlloc(pVM, MM_TAG_VM, sizeof(*pNew));
+    if (!pNew)
+        return VERR_NO_MEMORY;
+
+    /* fill */
+    pNew->pfnAtRuntimeError = pfnAtRuntimeError;
+    pNew->pvUser            = pvUser;
+    pNew->pNext             = NULL;
+
+    /* insert */
+    *pVM->vm.s.ppAtRuntimeErrorNext = pNew;
+    pVM->vm.s.ppAtRuntimeErrorNext = &pNew->pNext;
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Deregisters a VM runtime error callback.
+ *
+ * @returns VBox status code.
+ * @param   pVM                 The VM handle.
+ * @param   pfnAtRuntimeError   Pointer to callback.
+ * @param   pvUser              User argument.
+ * @thread  Any.
+ */
+VMR3DECL(int)   VMR3AtRuntimeErrorDeregister(PVM pVM, PFNVMATRUNTIMEERROR pfnAtRuntimeError, void *pvUser)
+{
+    LogFlow(("VMR3AtRuntimeErrorDeregister: pfnAtRuntimeError=%p pvUser=%p\n", pfnAtRuntimeError, pvUser));
+
+    /*
+     * Validate input.
+     */
+    if (!pfnAtRuntimeError)
+    {
+        AssertMsgFailed(("callback is required\n"));
+        return VERR_INVALID_PARAMETER;
+    }
+
+    /*
+     * Make sure we're in EMT (to avoid the logging).
+     */
+    PVMREQ pReq;
+    int rc = VMR3ReqCall(pVM, &pReq, RT_INDEFINITE_WAIT, (PFNRT)vmR3AtRuntimeErrorDeregister, 3, pVM, pfnAtRuntimeError, pvUser);
+    if (VBOX_FAILURE(rc))
+        return rc;
+    rc = pReq->iStatus;
+    VMR3ReqFree(pReq);
+
+    LogFlow(("VMR3AtRuntimeErrorDeregister: returns %Vrc\n", rc));
+    return rc;
+}
+
+
+/**
+ * Deregisters a VM runtime error callback.
+ *
+ * @returns VBox status code.
+ * @param   pVM                 The VM handle.
+ * @param   pfnAtRuntimeError   Pointer to callback.
+ * @param   pvUser              User argument.
+ * @thread  EMT
+ */
+static DECLCALLBACK(int)    vmR3AtRuntimeErrorDeregister(PVM pVM, PFNVMATRUNTIMEERROR pfnAtRuntimeError, void *pvUser)
+{
+    LogFlow(("vmR3AtRuntimeErrorDeregister: pfnAtRuntimeError=%p pvUser=%p\n", pfnAtRuntimeError, pvUser));
+
+    /*
+     * Search the list for the entry.
+     */
+    PVMATRUNTIMEERROR pPrev = NULL;
+    PVMATRUNTIMEERROR pCur = pVM->vm.s.pAtRuntimeError;
+    while (     pCur
+           &&   pCur->pfnAtRuntimeError == pfnAtRuntimeError
+           &&   pCur->pvUser == pvUser)
+    {
+        pPrev = pCur;
+        pCur = pCur->pNext;
+    }
+    if (!pCur)
+    {
+        AssertMsgFailed(("pfnAtRuntimeError=%p was not found\n", pfnAtRuntimeError));
+        return VERR_FILE_NOT_FOUND;
+    }
+
+    /*
+     * Unlink it.
+     */
+    if (pPrev)
+    {
+        pPrev->pNext = pCur->pNext;
+        if (!pCur->pNext)
+            pVM->vm.s.ppAtRuntimeErrorNext = &pPrev->pNext;
+    }
+    else
+    {
+        pVM->vm.s.pAtRuntimeError = pCur->pNext;
+        if (!pCur->pNext)
+            pVM->vm.s.ppAtRuntimeErrorNext = &pVM->vm.s.pAtRuntimeError;
+    }
+
+    /*
+     * Free it.
+     */
+    pCur->pfnAtRuntimeError = NULL;
+    pCur->pNext = NULL;
+    MMR3HeapFree(pCur);
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Ellipsis to va_list wrapper for calling pfnAtRuntimeError.
+ */
+static void vmR3SetRuntimeErrorWorkerDoCall(PVM pVM, PVMATRUNTIMEERROR pCur, bool fFatal,
+                                            const char *pszErrorID, 
+                                            const char *pszFormat, ...)
+{
+    va_list va;
+    va_start(va, pszFormat);
+    pCur->pfnAtRuntimeError(pVM, pCur->pvUser, fFatal, pszErrorID, pszFormat, va);
+    va_end(va);
+}
+
+
+/**
+ * This is a worker function for GC and Ring-0 calls to VMSetError and VMSetErrorV.
+ * The message is found in VMINT.
+ *
+ * @param   pVM             The VM handle.
+ * @thread  EMT.
+ */
+VMR3DECL(void) VMR3SetRuntimeErrorWorker(PVM pVM)
+{
+    VM_ASSERT_EMT(pVM);
+    AssertReleaseMsgFailed(("And we have a winner! You get to implement Ring-0 and GC VMSetRuntimeErrorV! Contrats!\n"));
+
+    /*
+     * Unpack the error (if we managed to format one).
+     */
+    PVMRUNTIMEERROR pErr = pVM->vm.s.pRuntimeErrorR3;
+    const char *pszErrorID = NULL;
+    const char *pszMessage;
+    bool        fFatal = false;
+    if (pErr)
+    {
+        AssertCompile(sizeof(const char) == sizeof(uint8_t));
+        if (pErr->offErrorID)
+            pszErrorID = (const char *)pErr + pErr->offErrorID;
+        if (pErr->offMessage)
+            pszMessage = (const char *)pErr + pErr->offMessage;
+        else
+            pszMessage = "No message!";
+        fFatal = pErr->fFatal;
+    }
+    else
+        pszMessage = "No message! (Failed to allocate memory to put the error message in!)";
+
+    /*
+     * Call the at runtime error callbacks.
+     */
+    for (PVMATRUNTIMEERROR pCur = pVM->vm.s.pAtRuntimeError; pCur; pCur = pCur->pNext)
+        vmR3SetRuntimeErrorWorkerDoCall(pVM, pCur, fFatal, pszErrorID, "%s", pszMessage);
+}
+
+
+/**
+ * Worker which calls everyone listening to the VM runtime error messages.
+ *
+ * @param   pVM             The VM handle.
+ * @param   fFatal          Whether it is a fatal error or not.
+ * @param   pszErrorID      Error ID string.
+ * @param   pszFormat       Format string.
+ * @param   pArgs           Pointer to the format arguments.
+ * @thread  EMT
+ */
+DECLCALLBACK(void) vmR3SetRuntimeErrorV(PVM pVM, bool fFatal,
+                                        const char *pszErrorID,
+                                        const char *pszFormat, va_list *pArgs)
+{
+    /*
+     * Make a copy of the message.
+     */
+    vmSetRuntimeErrorCopy(pVM, fFatal, pszErrorID, pszFormat, *pArgs);
+
+    /*
+     * Call the at error callbacks.
+     */
+    for (PVMATRUNTIMEERROR pCur = pVM->vm.s.pAtRuntimeError; pCur; pCur = pCur->pNext)
+    {
+        va_list va2;
+        va_copy(va2, *pArgs);
+        pCur->pfnAtRuntimeError(pVM, pCur->pvUser, fFatal, pszErrorID, pszFormat, va2);
+        va_end(va2);
+    }
+}
+
Index: /trunk/src/VBox/VMM/VMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/VMInternal.h	(revision 233)
+++ /trunk/src/VBox/VMM/VMInternal.h	(revision 234)
@@ -156,4 +156,41 @@
 
 /**
+ * VM runtime error callback.
+ */
+typedef struct VMATRUNTIMEERROR
+{
+    /** Pointer to the next one. */
+    struct VMATRUNTIMEERROR *pNext;
+    /** Pointer to the callback. */
+    PFNVMATRUNTIMEERROR      pfnAtRuntimeError;
+    /** The user argument. */
+    void                    *pvUser;
+} VMATRUNTIMEERROR;
+/** Pointer to a VM error callback. */
+typedef VMATRUNTIMEERROR *PVMATRUNTIMEERROR;
+
+
+/**
+ * Chunk of memory allocated off the hypervisor heap in which
+ * we copy the runtime error details.
+ */
+typedef struct VMRUNTIMEERROR
+{
+    /** The size of the chunk. */
+    uint32_t                cbAllocated;
+    /** The current offset into the chunk.
+     * We start by putting the error ID immediatly
+     * after the end of the buffer. */
+    uint32_t                off;
+    /** Offset from the start of this structure to the error ID. */
+    uint32_t                offErrorID;
+    /** Offset from the start of this structure to the formatted message text. */
+    uint32_t                offMessage;
+    /** Whether the error is fatal or not */
+    bool                    fFatal;
+} VMRUNTIMEERROR, *PVMRUNTIMEERROR;
+
+
+/**
  * Converts a VMM pointer into a VM pointer.
  * @returns Pointer to the VM structure the VMM is part of.
@@ -186,4 +223,9 @@
     /** List of registered error callbacks. */
     HCPTRTYPE(PVMATERROR *)         ppAtErrorNext;
+
+    /** List of registered error callbacks. */
+    HCPTRTYPE(PVMATRUNTIMEERROR)    pAtRuntimeError;
+    /** List of registered error callbacks. */
+    HCPTRTYPE(PVMATRUNTIMEERROR *)  ppAtRuntimeErrorNext;
 
     /** Head of the request queue. Atomic. */
@@ -203,4 +245,7 @@
     /** VM Error Message. */
     R3PTRTYPE(PVMERROR)             pErrorR3;
+
+    /** VM Runtime Error Message. */
+    R3PTRTYPE(PVMRUNTIMEERROR)      pRuntimeErrorR3;
 
     /** Pointer to the DBGC instance data. */
@@ -245,4 +290,6 @@
 DECLCALLBACK(void) vmR3SetErrorV(PVM pVM, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list *args);
 void vmSetErrorCopy(PVM pVM, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list args);
+DECLCALLBACK(void) vmR3SetRuntimeErrorV(PVM pVM, bool fFatal, const char *pszErrorID, const char *pszFormat, va_list *args);
+void vmSetRuntimeErrorCopy(PVM pVM, bool fFatal, const char *pszErrorID, const char *pszFormat, va_list args);
 void vmR3DestroyFinalBit(PVM pVM);
 
Index: /trunk/src/VBox/VMM/VMM.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMM.cpp	(revision 233)
+++ /trunk/src/VBox/VMM/VMM.cpp	(revision 234)
@@ -2121,4 +2121,11 @@
             break;
 
+        /*
+         * Set the VM runtime error message.
+         */
+        case VMMCALLHOST_VM_SET_RUNTIME_ERROR:
+            VMR3SetRuntimeErrorWorker(pVM);
+            break;
+
         default:
             AssertMsgFailed(("enmCallHostOperation=%d\n", pVM->vmm.s.enmCallHostOperation));
Index: /trunk/src/VBox/VMM/VMMAll/VMAll.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/VMAll.cpp	(revision 233)
+++ /trunk/src/VBox/VMM/VMMAll/VMAll.cpp	(revision 234)
@@ -41,5 +41,5 @@
  *    @code
  *    return VM_SET_ERROR(pVM, VERR_OF_YOUR_CHOICE, "descriptive message");
- *    @codeend
+ *    @endcode
  * @param   pVM             VM handle. Must be non-NULL.
  * @param   rc              VBox status code.
@@ -65,5 +65,5 @@
  *    @code
  *    return VM_SET_ERROR(pVM, VERR_OF_YOUR_CHOICE, "descriptive message");
- *    @codeend
+ *    @endcode
  * @param   pVM             VM handle. Must be non-NULL.
  * @param   rc              VBox status code.
@@ -178,2 +178,162 @@
 }
 
+
+/**
+ * Sets the runtime error message.
+ * As opposed VMSetError(), this method is intended to inform the VM user about
+ * errors and error-like conditions that happen at an arbitrary point during VM
+ * execution (like "host memory low" or "out of host disk space").
+ *
+ * The @a fFatal parameter defines whether the error is fatal or not. If it is
+ * true, then it is expected that the caller has already paused the VM execution
+ * before calling this method. The VM user is supposed to power off the VM
+ * immediately after it has received the runtime error notification via the
+ * FNVMATRUNTIMEERROR callback.
+ *
+ * If @a fFatal is false, then the paused state of the VM defines the kind of
+ * the error. If the VM is paused before calling this method, it means that
+ * the VM user may try to fix the error condition (i.e. free more host memory)
+ * and then resume the VM execution. If the VM is not paused before calling
+ * this method, it means that the given error is a warning about an error
+ * condition that may happen soon but that doesn't directly affect the
+ * VM execution by the time of the call.
+ *
+ * The @a pszErrorID parameter defines an unique error identificator.
+ * It is used by the front-ends to show a proper message to the end user
+ * containig possible actions (for example, Retry/Ignore). For this reason,
+ * an error ID assigned once to some particular error condition should not
+ * change in the future. The format of this parameter is "someErrorCondition". 
+ *
+ * @param   pVM             VM handle. Must be non-NULL.
+ * @param   fFatal          Whether it is a fatal error or not.
+ * @param   pszErrorID      Error ID string.
+ * @param   pszFormat       Error message format string.
+ * @param   ...             Error message arguments.
+ *
+ * @return  VBox status code (whether the error has been successfully set
+ *          and delivered to callbacks or not).
+ *
+ * @thread  Any
+ */
+VMDECL(int) VMSetRuntimeError(PVM pVM, bool fFatal, const char *pszErrorID,
+                              const char *pszFormat, ...)
+{
+    va_list args;
+    va_start(args, pszFormat);
+    int rc = VMSetRuntimeErrorV(pVM, fFatal, pszErrorID, pszFormat, args);
+    va_end(args);
+    return rc;
+}
+
+
+/**
+ * va_list version of VMSetRuntimeError.
+ *
+ * @param   pVM             VM handle. Must be non-NULL.
+ * @param   fFatal          Whether it is a fatal error or not.
+ * @param   pszErrorID      Error ID string.
+ * @param   pszFormat       Error message format string.
+ * @param   args            Error message arguments.
+ *
+ * @return  VBox status code (whether the error has been successfully set
+ *          and delivered to callbacks or not).
+ *
+ * @thread  Any
+ */
+VMDECL(int) VMSetRuntimeErrorV(PVM pVM, bool fFatal, const char *pszErrorID,
+                               const char *pszFormat, va_list args)
+{
+#ifdef IN_RING3
+    /*
+     * Switch to EMT.
+     */
+    PVMREQ pReq;
+    VMR3ReqCall(pVM, &pReq, RT_INDEFINITE_WAIT, (PFNRT)vmR3SetRuntimeErrorV, 5,
+                pVM, fFatal, pszErrorID, pszFormat, &args);
+    VMR3ReqFree(pReq);
+
+#else
+    /*
+     * We're already on the EMT thread and can safely create a VMRUNTIMEERROR chunk.
+     */
+    vmSetRuntimeErrorCopy(pVM, fFatal, pszErrorID, pszFormat, args);
+
+# ifdef IN_GC
+    VMMGCCallHost(pVM, VMMCALLHOST_VM_SET_RUNTIME_ERROR, 0);
+# elif defined(IN_RING0)
+    VMMR0CallHost(pVM, VMMCALLHOST_VM_SET_RUNTIME_ERROR, 0);
+# else
+# endif
+#endif
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Copies the error to a VMRUNTIMEERROR structure.
+ *
+ * This is mainly intended for Ring-0 and GC where the error must be copied to
+ * memory accessible from ring-3. But it's just possible that we might add
+ * APIs for retrieving the VMRUNTIMEERROR copy later.
+ *
+ * @param   pVM             VM handle. Must be non-NULL.
+ * @param   fFatal          Whether it is a fatal error or not.
+ * @param   pszErrorID      Error ID string.
+ * @param   pszFormat       Error message format string.
+ * @param   args            Error message arguments.
+ * @thread  EMT
+ */
+void vmSetRuntimeErrorCopy(PVM pVM, bool fFatal, const char *pszErrorID,
+                           const char *pszFormat, va_list args)
+{
+#if 0 /// @todo implement Ring-0 and GC VMSetError
+    /*
+     * Create the untranslated message copy.
+     */
+    /* free any old message. */
+    MMHyperFree(pVM, MMHyperR32Ctx(pVM, pVM->vm.s.pRuntimeErrorR3));
+    pVM->vm.s.pRuntimeErrorR3 = NULL;
+
+    /* calc reasonable start size. */
+    size_t cchErrorID = pszErrorID ? strlen(pszErrorID) : 0;
+    size_t cchFormat = strlen(pszFormat);
+    size_t cb = sizeof(VMRUNTIMEERROR)
+              + cchErrorID + 1
+              + cchFormat + 32;
+
+    /* allocate it */
+    void *pv;
+    int rc2 = MMHyperAlloc(pVM, cb, 0, MM_TAG_VM, &pv);
+    if (VBOX_SUCCESS(rc2))
+    {
+        /* initialize it. */
+        PVMRUNTIMEERROR pErr = (PVMRUNTIMEERROR)pv;
+        pErr->cbAllocated = cb;
+        pErr->off = sizeof(PVMRUNTIMEERROR);
+        pErr->offErrorID = = 0;
+
+        if (cchErrorID)
+        {
+            pErr->offErrorID = pErr->off;
+            memcpy((uint8_t *)pErr + pErr->off, pszErrorID, cchErrorID + 1);
+            pErr->off += cchErrorID + 1;
+        }
+
+        pErr->offMessage = pErr->off;
+
+        /* format the message (pErr might be reallocated) */
+        VMSETRUNTIMEERRORFMTARGS Args;
+        Args.pVM = pVM;
+        Args.pErr = pErr;
+
+        va_list va2;
+        va_copy(va2, args);
+        RTStrFormatV(vmSetRuntimeErrorFmtOut, &pErr, NULL, NULL, &pszFormatTmp, args);
+        va_end(va2);
+
+        /* done. */
+        pVM->vm.s.pErrorRuntimeR3 = MMHyper2HC(pVM, (uintptr_t)pArgs.pErr);
+    }
+#endif
+}
+
Index: /trunk/src/VBox/VMM/VMMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/VMMInternal.h	(revision 233)
+++ /trunk/src/VBox/VMM/VMMInternal.h	(revision 234)
@@ -302,4 +302,5 @@
     STAMCOUNTER                 StatGCRetRemReplay;
     STAMCOUNTER                 StatGCRetVMSetError;
+    STAMCOUNTER                 StatGCRetVMSetRuntimeError;
     STAMCOUNTER                 StatGCRetPGMLock;
 
Index: /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 233)
+++ /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 234)
@@ -365,4 +365,7 @@
                 case VMMCALLHOST_VM_SET_ERROR:
                     STAM_COUNTER_INC(&pVM->vmm.s.StatGCRetVMSetError);
+                    break;
+                case VMMCALLHOST_VM_SET_RUNTIME_ERROR:
+                    STAM_COUNTER_INC(&pVM->vmm.s.StatGCRetVMSetRuntimeError);
                     break;
                 default:
