Index: trunk/src/VBox/Frontends/VBoxHeadless/VBoxHeadless.cpp
===================================================================
--- trunk/src/VBox/Frontends/VBoxHeadless/VBoxHeadless.cpp	(revision 25900)
+++ trunk/src/VBox/Frontends/VBoxHeadless/VBoxHeadless.cpp	(revision 25901)
@@ -403,4 +403,9 @@
 
     STDMETHOD(OnMediumChange)(IMediumAttachment * /* aMediumAttachment */)
+    {
+        return S_OK;
+    }
+
+    STDMETHOD(OnCPUChange)(ULONG /*aCPU*/, BOOL /* aRemove */)
     {
         return S_OK;
Index: trunk/src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp
===================================================================
--- trunk/src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp	(revision 25900)
+++ trunk/src/VBox/Frontends/VBoxManage/VBoxManageControlVM.cpp	(revision 25901)
@@ -116,4 +116,30 @@
             CHECK_ERROR_BREAK(console, Reset());
         }
+        else if (!strcmp(a->argv[1], "unplugcpu"))
+        {
+            if (a->argc <= 1 + 1)
+            {
+                errorArgument("Missing argument to '%s'. Expected CPU number.", a->argv[1]);
+                rc = E_FAIL;
+                break;
+            }
+
+            unsigned n = parseNum(a->argv[2], 32, "CPU");
+
+            CHECK_ERROR_BREAK(sessionMachine, HotUnplugCPU(n));
+        }
+        else if (!strcmp(a->argv[1], "plugcpu"))
+        {
+            if (a->argc <= 1 + 1)
+            {
+                errorArgument("Missing argument to '%s'. Expected CPU number.", a->argv[1]);
+                rc = E_FAIL;
+                break;
+            }
+
+            unsigned n = parseNum(a->argv[2], 32, "CPU");
+
+            CHECK_ERROR_BREAK(sessionMachine, HotPlugCPU(n));
+        }
         else if (!strcmp(a->argv[1], "poweroff"))
         {
Index: trunk/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp
===================================================================
--- trunk/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp	(revision 25900)
+++ trunk/src/VBox/Frontends/VBoxManage/VBoxManageModifyVM.cpp	(revision 25901)
@@ -71,4 +71,7 @@
     MODIFYVM_VTXVPID,
     MODIFYVM_CPUS,
+    MODIFYVM_CPUHOTPLUG,
+    MODIFYVM_PLUGCPU,
+    MODIFYVM_UNPLUGCPU,
     MODIFYVM_SETCPUID,
     MODIFYVM_DELCPUID,
@@ -153,4 +156,7 @@
     { "--cpuidremoveall",           MODIFYVM_DELALLCPUID,               RTGETOPT_REQ_NOTHING},
     { "--cpus",                     MODIFYVM_CPUS,                      RTGETOPT_REQ_UINT32 },
+    { "--cpuhotplug",               MODIFYVM_CPUHOTPLUG,                RTGETOPT_REQ_BOOL_ONOFF },
+    { "--plugcpu",                  MODIFYVM_PLUGCPU,                   RTGETOPT_REQ_UINT32 },
+    { "--unplugcpu",                MODIFYVM_UNPLUGCPU,                 RTGETOPT_REQ_UINT32 },
     { "--rtcuseutc",                MODIFYVM_RTCUSEUTC,                 RTGETOPT_REQ_BOOL_ONOFF },
     { "--monitorcount",             MODIFYVM_MONITORCOUNT,              RTGETOPT_REQ_UINT32 },
@@ -423,4 +429,22 @@
             }
 
+            case MODIFYVM_CPUHOTPLUG:
+            {
+                CHECK_ERROR(machine, COMSETTER(CPUHotPlugEnabled)(ValueUnion.f));
+                break;
+            }
+
+            case MODIFYVM_PLUGCPU:
+            {
+                CHECK_ERROR(machine, HotPlugCPU(ValueUnion.u32));
+                break;
+            }
+
+            case MODIFYVM_UNPLUGCPU:
+            {
+                CHECK_ERROR(machine, HotUnplugCPU(ValueUnion.u32));
+                break;
+            }
+
             case MODIFYVM_MONITORCOUNT:
             {
Index: trunk/src/VBox/Frontends/VBoxSDL/VBoxSDL.cpp
===================================================================
--- trunk/src/VBox/Frontends/VBoxSDL/VBoxSDL.cpp	(revision 25900)
+++ trunk/src/VBox/Frontends/VBoxSDL/VBoxSDL.cpp	(revision 25901)
@@ -542,4 +542,9 @@
 
     STDMETHOD(OnMediumChange)(IMediumAttachment * /*aMediumAttachment*/)
+    {
+        return S_OK;
+    }
+
+    STDMETHOD(OnCPUChange)(ULONG /*aCPU*/, BOOL /* aRemove */)
     {
         return S_OK;
Index: trunk/src/VBox/Frontends/VirtualBox/src/VBoxConsoleView.cpp
===================================================================
--- trunk/src/VBox/Frontends/VirtualBox/src/VBoxConsoleView.cpp	(revision 25900)
+++ trunk/src/VBox/Frontends/VirtualBox/src/VBoxConsoleView.cpp	(revision 25901)
@@ -536,4 +536,11 @@
                 break;
         }
+        return S_OK;
+    }
+
+    STDMETHOD(OnCPUChange)(ULONG aCPU, BOOL aRemove)
+    {
+        NOREF(aCPU);
+        NOREF(aRemove);
         return S_OK;
     }
Index: trunk/src/VBox/Main/ConsoleImpl.cpp
===================================================================
--- trunk/src/VBox/Main/ConsoleImpl.cpp	(revision 25900)
+++ trunk/src/VBox/Main/ConsoleImpl.cpp	(revision 25901)
@@ -1730,4 +1730,247 @@
 }
 
+DECLCALLBACK(int) Console::unplugCpu(Console *pThis, unsigned uCpu)
+{
+    LogFlowFunc(("pThis=%p uCpu=%u\n", pThis, uCpu));
+
+    AssertReturn(pThis, VERR_INVALID_PARAMETER);
+
+    int vrc = PDMR3DeviceDetach(pThis->mpVM, "acpi", 0, uCpu, 0);
+    Log(("UnplugCpu: rc=%Rrc\n", vrc));
+
+    return vrc;
+}
+
+STDMETHODIMP Console::doCPURemove(ULONG aCpu)
+{
+    HRESULT rc = S_OK;
+
+    LogFlowThisFuncEnter();
+    LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    AutoWriteLock alock(this);
+
+    if (   mMachineState != MachineState_Running
+        && mMachineState != MachineState_Teleporting
+        && mMachineState != MachineState_LiveSnapshotting
+       )
+        return setError(VBOX_E_INVALID_VM_STATE,
+            tr("Invalid machine state: %s"),
+            Global::stringifyMachineState(mMachineState));
+
+    /* protect mpVM */
+    AutoVMCaller autoVMCaller(this);
+    if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
+
+    /* Check if the CPU is present */
+    BOOL fCpuAttached;
+    rc = mMachine->GetCPUStatus(aCpu, &fCpuAttached);
+    if (FAILED(rc)) return rc;
+
+    if (!fCpuAttached)
+        return setError(E_FAIL,
+                        tr("CPU %d is not attached"), aCpu);
+
+    /* Check if the CPU is unlocked */
+    PPDMIBASE pBase;
+    int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, aCpu, &pBase);
+    bool fLocked = true;
+    if (RT_SUCCESS(vrc))
+    {
+        uint32_t idCpuCore, idCpuPackage;
+
+        /* Notify the guest if possible. */
+        vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(mpVM, aCpu, &idCpuCore, &idCpuPackage);
+        AssertRC(vrc);
+
+        Assert(pBase);
+
+        PPDMIACPIPORT pPort =
+            (PPDMIACPIPORT) pBase->pfnQueryInterface(pBase, PDMINTERFACE_ACPI_PORT);
+
+        vrc = getVMMDev()->getVMMDevPort()->pfnCpuHotUnplug(getVMMDev()->getVMMDevPort(), idCpuCore, idCpuPackage);
+        if (RT_SUCCESS(vrc))
+        {
+            unsigned cTries = 10;
+
+            do
+            {
+                /* It will take some time until the event is processed in the guest. Wait  */
+                vrc = pPort ? pPort->pfnGetCpuStatus(pPort, aCpu, &fLocked) : VERR_INVALID_POINTER;
+
+                if (RT_SUCCESS(vrc) && !fLocked)
+                    break;
+
+                /* Sleep a bit */
+                RTThreadSleep(100);
+            } while (cTries-- > 0);
+        }
+        else if (vrc == VERR_CPU_HOTPLUG_NOT_MONITORED_BY_GUEST)
+        {
+            /* Query one time. It is possible that the user ejected the CPU. */
+            vrc = pPort ? pPort->pfnGetCpuStatus(pPort, aCpu, &fLocked) : VERR_INVALID_POINTER;
+        }
+    }
+
+    /* If the CPU was unlocked we can detach it now. */
+    if (RT_SUCCESS(vrc) && !fLocked)
+    {
+        /*
+         * Call worker in EMT, that's faster and safer than doing everything
+         * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
+         * here to make requests from under the lock in order to serialize them.
+         */
+        PVMREQ pReq;
+        vrc = VMR3ReqCall(mpVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
+                          (PFNRT)Console::unplugCpu, 2,
+                          this, aCpu);
+
+        /* leave the lock before a VMR3* call (EMT will call us back)! */
+        alock.leave();
+
+        if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
+        {
+            vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
+            AssertRC(vrc);
+            if (RT_SUCCESS(vrc))
+                vrc = pReq->iStatus;
+        }
+        VMR3ReqFree(pReq);
+
+        if (RT_SUCCESS(vrc))
+        {
+            /* Detach it from the VM  */
+            vrc = VMR3HotUnplugCpu(mpVM, aCpu);
+            AssertRC(vrc);
+        }
+        else
+           rc = setError(VBOX_E_VM_ERROR,
+                         tr("Hot-Remove failed (rc=%Rrc)"), vrc);
+    }
+    else
+        rc = setError(VBOX_E_VM_ERROR,
+                      tr("Hot-Remove was aborted because the CPU may still be used by the guest"), VERR_RESOURCE_BUSY);
+
+    LogFlowThisFunc(("mMachineState=%d, rc=%08X\n", mMachineState, rc));
+    LogFlowThisFuncLeave();
+    return rc;
+}
+
+DECLCALLBACK(int) Console::plugCpu(Console *pThis, unsigned uCpu)
+{
+    LogFlowFunc(("pThis=%p uCpu=%u\n", pThis, uCpu));
+
+    AssertReturn(pThis, VERR_INVALID_PARAMETER);
+
+    int rc = VMR3HotPlugCpu(pThis->mpVM, uCpu);
+    AssertRC(rc);
+
+    PCFGMNODE pInst = CFGMR3GetChild(CFGMR3GetRoot(pThis->mpVM), "Devices/acpi/0/");
+    AssertRelease(pInst);
+    /* nuke anything which might have been left behind. */
+    CFGMR3RemoveNode(CFGMR3GetChildF(pInst, "LUN#%d", uCpu));
+
+#define RC_CHECK() do { if (RT_FAILURE(rc)) { AssertReleaseRC(rc); break; } } while (0)
+
+    PCFGMNODE pLunL0;
+    PCFGMNODE pCfg;
+    rc = CFGMR3InsertNodeF(pInst, &pLunL0, "LUN#%d", uCpu);     RC_CHECK();
+    rc = CFGMR3InsertString(pLunL0, "Driver",       "ACPICpu"); RC_CHECK();
+    rc = CFGMR3InsertNode(pLunL0,   "Config",       &pCfg);     RC_CHECK();
+
+    /*
+     * Attach the driver.
+     */
+    PPDMIBASE pBase;
+    rc = PDMR3DeviceAttach(pThis->mpVM, "acpi", 0, uCpu, 0, &pBase); RC_CHECK();
+
+    Log(("PlugCpu: rc=%Rrc\n", rc));
+
+    CFGMR3Dump(pInst);
+
+    return VINF_SUCCESS;
+}
+
+STDMETHODIMP Console::doCPUAdd(ULONG aCpu)
+{
+    HRESULT rc = S_OK;
+
+    LogFlowThisFuncEnter();
+    LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    AutoWriteLock alock(this);
+
+    if (   mMachineState != MachineState_Running
+        && mMachineState != MachineState_Teleporting
+        && mMachineState != MachineState_LiveSnapshotting
+        /** @todo r=bird: This should be allowed on paused VMs as well. Later.  */
+       )
+        return setError(VBOX_E_INVALID_VM_STATE,
+            tr("Invalid machine state: %s"),
+            Global::stringifyMachineState(mMachineState));
+
+    /* protect mpVM */
+    AutoVMCaller autoVMCaller(this);
+    if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
+
+    /* Check if the CPU is present */
+    BOOL fCpuAttached;
+    rc = mMachine->GetCPUStatus(aCpu, &fCpuAttached);
+    if (FAILED(rc)) return rc;
+
+    if (fCpuAttached)
+        return setError(E_FAIL,
+                        tr("CPU %d is already attached"), aCpu);
+
+    /*
+     * Call worker in EMT, that's faster and safer than doing everything
+     * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait
+     * here to make requests from under the lock in order to serialize them.
+     */
+    PVMREQ pReq;
+    int vrc = VMR3ReqCall(mpVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS,
+                          (PFNRT)Console::plugCpu, 2,
+                          this, aCpu);
+
+    /* leave the lock before a VMR3* call (EMT will call us back)! */
+    alock.leave();
+
+    if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc))
+    {
+        vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT);
+        AssertRC(vrc);
+        if (RT_SUCCESS(vrc))
+            vrc = pReq->iStatus;
+    }
+    VMR3ReqFree(pReq);
+
+    rc = RT_SUCCESS(vrc) ? S_OK :
+        setError(VBOX_E_VM_ERROR,
+            tr("Could not add CPU to the machine (%Rrc)"),
+            vrc);
+
+    if (RT_SUCCESS(vrc))
+    {
+        uint32_t idCpuCore, idCpuPackage;
+
+        /* Notify the guest if possible. */
+        vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(mpVM, aCpu, &idCpuCore, &idCpuPackage);
+        AssertRC(vrc);
+
+        vrc = getVMMDev()->getVMMDevPort()->pfnCpuHotPlug(getVMMDev()->getVMMDevPort(), idCpuCore, idCpuPackage);
+        /** @todo warning if the guest doesn't support it */
+    }
+
+    LogFlowThisFunc(("mMachineState=%d, rc=%08X\n", mMachineState, rc));
+    LogFlowThisFuncLeave();
+    return rc;
+}
+
 STDMETHODIMP Console::Pause()
 {
@@ -3730,4 +3973,45 @@
         while (it != mCallbacks.end())
             (*it++)->OnMediumChange(aMediumAttachment);
+    }
+
+    LogFlowThisFunc(("Leaving rc=%#x\n", rc));
+    return rc;
+}
+
+/**
+ * Called by IInternalSessionControl::OnCPUChange().
+ *
+ * @note Locks this object for writing.
+ */
+HRESULT Console::onCPUChange(ULONG aCPU, BOOL aRemove)
+{
+    LogFlowThisFunc(("\n"));
+
+    AutoCaller autoCaller(this);
+    AssertComRCReturnRC(autoCaller.rc());
+
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    /* Don't do anything if the VM isn't running */
+    if (!mpVM)
+        return S_OK;
+
+    HRESULT rc = S_OK;
+
+    /* protect mpVM */
+    AutoVMCaller autoVMCaller(this);
+    if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc();
+
+    if (aRemove)
+        rc = doCPURemove(aCPU);
+    else
+        rc = doCPUAdd(aCPU);
+
+    /* notify console callbacks on success */
+    if (SUCCEEDED(rc))
+    {
+        CallbackList::iterator it = mCallbacks.begin();
+        while (it != mCallbacks.end())
+            (*it++)->OnCPUChange(aCPU, aRemove);
     }
 
Index: trunk/src/VBox/Main/ConsoleImpl2.cpp
===================================================================
--- trunk/src/VBox/Main/ConsoleImpl2.cpp	(revision 25900)
+++ trunk/src/VBox/Main/ConsoleImpl2.cpp	(revision 25901)
@@ -1851,4 +1851,5 @@
     if (fACPI)
     {
+        BOOL fCpuHotPlug = false;
         BOOL fShowCpu = fExtProfile;
         /* Always show the CPU leafs when we have multiple VCPUs or when the IO-APIC is enabled.
@@ -1856,6 +1857,8 @@
          * intelppm driver refuses to register an idle state handler.
          */
-        if ((cCpus > 1) ||  fIOAPIC)
+        if ((cCpus > 1) || fIOAPIC)
             fShowCpu = true;
+
+        hrc = pMachine->COMGETTER(CPUHotPlugEnabled)(&fCpuHotPlug);                 H();
 
         rc = CFGMR3InsertNode(pDevices, "acpi", &pDev);                             RC_CHECK();
@@ -1878,4 +1881,5 @@
 
         rc = CFGMR3InsertInteger(pCfg,  "ShowCpu", fShowCpu);                       RC_CHECK();
+        rc = CFGMR3InsertInteger(pCfg,  "CpuHotPlug", fCpuHotPlug);                 RC_CHECK();
         rc = CFGMR3InsertInteger(pInst, "PCIDeviceNo",          7);                 RC_CHECK();
         Assert(!afPciDeviceNo[7]);
@@ -1886,4 +1890,22 @@
         rc = CFGMR3InsertString(pLunL0, "Driver",               "ACPIHost");        RC_CHECK();
         rc = CFGMR3InsertNode(pLunL0,   "Config", &pCfg);                           RC_CHECK();
+
+        /* Attach the dummy CPU drivers */
+        for (ULONG iCpuCurr = 1; iCpuCurr < cCpus; iCpuCurr++)
+        {
+            BOOL fCpuAttached = true;
+
+            if (fCpuHotPlug)
+            {
+                hrc = pMachine->GetCPUStatus(iCpuCurr, &fCpuAttached);              H();
+            }
+
+            if (fCpuAttached)
+            {
+                rc = CFGMR3InsertNodeF(pInst, &pLunL0, "LUN#%u", iCpuCurr);             RC_CHECK();
+                rc = CFGMR3InsertString(pLunL0, "Driver",           "ACPICpu");         RC_CHECK();
+                rc = CFGMR3InsertNode(pLunL0,   "Config", &pCfg);                       RC_CHECK();
+            }
+        }
     }
 
Index: trunk/src/VBox/Main/ConsoleVRDPServer.cpp
===================================================================
--- trunk/src/VBox/Main/ConsoleVRDPServer.cpp	(revision 25900)
+++ trunk/src/VBox/Main/ConsoleVRDPServer.cpp	(revision 25901)
@@ -119,4 +119,9 @@
 
     STDMETHOD(OnMediumChange)(IMediumAttachment *aAttachment)
+    {
+        return S_OK;
+    }
+
+    STDMETHOD(OnCPUChange)(ULONG aCPU, BOOL aRemove)
     {
         return S_OK;
Index: trunk/src/VBox/Main/MachineImpl.cpp
===================================================================
--- trunk/src/VBox/Main/MachineImpl.cpp	(revision 25900)
+++ trunk/src/VBox/Main/MachineImpl.cpp	(revision 25901)
@@ -156,4 +156,5 @@
     mMemorySize = 128;
     mCPUCount = 1;
+    mCPUHotPlugEnabled = false;
     mMemoryBalloonSize = 0;
     mStatisticsUpdateInterval = 0;
@@ -189,4 +190,7 @@
 
     mFirmwareType = FirmwareType_BIOS;
+
+    for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
+        mCPUAttached[i] = false;
 }
 
@@ -217,4 +221,5 @@
         mSyntheticCpu != that.mSyntheticCpu ||
         mCPUCount != that.mCPUCount ||
+        mCPUHotPlugEnabled != that.mCPUHotPlugEnabled ||
         mClipboardMode != that.mClipboardMode)
         return false;
@@ -223,4 +228,11 @@
         if (mBootOrder [i] != that.mBootOrder [i])
             return false;
+
+
+    for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
+    {
+        if (mCPUAttached[i] != that.mCPUAttached[i])
+            return false;
+    }
 
     if (mSharedFolders.size() != that.mSharedFolders.size())
@@ -1075,5 +1087,5 @@
 STDMETHODIMP Machine::COMSETTER(CPUCount) (ULONG CPUCount)
 {
-    /* check RAM limits */
+    /* check CPU limits */
     if (    CPUCount < SchemaDefs::MinCPUCount
          || CPUCount > SchemaDefs::MaxCPUCount
@@ -1088,4 +1100,16 @@
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
+    /* We cant go below the current number of CPUs if hotplug is enabled*/
+    if (mHWData->mCPUHotPlugEnabled)
+    {
+        for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
+        {
+            if (mHWData->mCPUAttached[idx])
+                return setError(E_INVALIDARG,
+                                tr(": %lu (must be higher than or equal to %lu)"),
+                                CPUCount, idx+1);
+        }
+    }
+
     HRESULT rc = checkStateDependency(MutableStateDep);
     if (FAILED(rc)) return rc;
@@ -1095,4 +1119,75 @@
 
     return S_OK;
+}
+
+STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled) (BOOL *enabled)
+{
+    if (!enabled)
+        return E_POINTER;
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    AutoReadLock alock(this);
+
+    *enabled = mHWData->mCPUHotPlugEnabled;
+
+    return S_OK;
+}
+
+STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled) (BOOL enabled)
+{
+    HRESULT rc = S_OK;
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    AutoWriteLock alock(this);
+
+    rc = checkStateDependency(MutableStateDep);
+    if (FAILED(rc)) return rc;
+
+    if (mHWData->mCPUHotPlugEnabled != enabled)
+    {
+        if (enabled)
+        {
+            mHWData.backup();
+
+            /* Add the amount of CPUs currently attached */
+            for (unsigned i = 0; i < mHWData->mCPUCount; i++)
+            {
+                mHWData->mCPUAttached[i] = true;
+            }
+        }
+        else
+        {
+            /*
+             * We can disable hotplug only if the amount of maximum CPUs is equal
+             * to the amount of attached CPUs
+             */
+            unsigned cCpusAttached = 0;
+            unsigned iHighestId = 0;
+
+            for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
+            {
+                if (mHWData->mCPUAttached[i])
+                {
+                    cCpusAttached++;
+                    iHighestId = i;
+                }
+            }
+
+            if (   (cCpusAttached != mHWData->mCPUCount)
+                || (iHighestId >= mHWData->mCPUCount))
+                return setError(E_INVALIDARG,
+                                tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached\n"));
+
+            mHWData.backup();
+        }
+    }
+
+    mHWData->mCPUHotPlugEnabled = enabled;
+
+    return rc;
 }
 
@@ -4257,4 +4352,106 @@
 }
 
+STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
+{
+    HRESULT rc = S_OK;
+    LogFlowThisFunc(("\n"));
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    AutoWriteLock alock(this);
+
+    if (!mHWData->mCPUHotPlugEnabled)
+        return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
+
+    if (aCpu >= mHWData->mCPUCount)
+        return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
+
+    if (mHWData->mCPUAttached[aCpu])
+        return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
+
+    alock.leave();
+    rc = onCPUChange(aCpu, false);
+    alock.enter();
+    if (FAILED(rc)) return rc;
+
+    mHWData.backup();
+    mHWData->mCPUAttached[aCpu] = true;
+
+    /* Save settings if online */
+    if (Global::IsOnline(mData->mMachineState))
+        SaveSettings();
+
+    return S_OK;
+}
+
+STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
+{
+    HRESULT rc = S_OK;
+    LogFlowThisFunc(("\n"));
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    AutoWriteLock alock(this);
+
+    if (!mHWData->mCPUHotPlugEnabled)
+        return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
+
+    if (aCpu >= SchemaDefs::MaxCPUCount)
+        return setError(E_INVALIDARG,
+                        tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
+                        SchemaDefs::MaxCPUCount);
+
+    if (!mHWData->mCPUAttached[aCpu])
+        return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
+
+    /* CPU 0 can't be detached */
+    if (aCpu == 0)
+        return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
+
+    alock.leave();
+    rc = onCPUChange(aCpu, true);
+    alock.enter();
+    if (FAILED(rc)) return rc;
+
+    mHWData.backup();
+    mHWData->mCPUAttached[aCpu] = false;
+
+    /* Save settings if online */
+    if (Global::IsOnline(mData->mMachineState))
+        SaveSettings();
+
+    return S_OK;
+}
+
+STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
+{
+    LogFlowThisFunc(("\n"));
+
+    CheckComArgNotNull(aCpuAttached);
+
+    *aCpuAttached = false;
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    AutoReadLock alock(this);
+
+    /* If hotplug is enabled the CPU is always enabled. */
+    if (!mHWData->mCPUHotPlugEnabled)
+    {
+        if (aCpu < mHWData->mCPUCount)
+            *aCpuAttached = true;
+    }
+    else
+    {
+        if (aCpu < SchemaDefs::MaxCPUCount)
+            *aCpuAttached = mHWData->mCPUAttached[aCpu];
+    }
+
+    return S_OK;
+}
+
 // public methods for internal purposes
 /////////////////////////////////////////////////////////////////////////////
@@ -5960,5 +6157,19 @@
         mHWData->mSyntheticCpu                = data.fSyntheticCpu;
 
-        mHWData->mCPUCount = data.cCPUs;
+        mHWData->mCPUCount          = data.cCPUs;
+        mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
+
+        // cpu
+        if (mHWData->mCPUHotPlugEnabled)
+        {
+            for (settings::CpuList::const_iterator it = data.llCpus.begin();
+                it != data.llCpus.end();
+                ++it)
+            {
+                const settings::Cpu &cpu = *it;
+
+                mHWData->mCPUAttached[cpu.ulId] = true;
+            }
+        }
 
         // cpuid leafs
@@ -6983,5 +7194,20 @@
         }
 
-        data.cCPUs = mHWData->mCPUCount;
+        data.cCPUs       = mHWData->mCPUCount;
+        data.fCpuHotPlug = mHWData->mCPUHotPlugEnabled;
+
+        data.llCpus.clear();
+        if (data.fCpuHotPlug)
+        {
+            for (unsigned idx = 0; idx < data.cCPUs; idx++)
+            {
+                if (mHWData->mCPUAttached[idx])
+                {
+                    settings::Cpu cpu;
+                    cpu.ulId = idx;
+                    data.llCpus.push_back(cpu);
+                }
+            }
+        }
 
         // memory
@@ -9659,4 +9885,27 @@
  *  @note Locks this object for reading.
  */
+HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
+{
+    LogFlowThisFunc(("\n"));
+
+    AutoCaller autoCaller(this);
+    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
+
+    ComPtr<IInternalSessionControl> directControl;
+    {
+        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+        directControl = mData->mSession.mDirectControl;
+    }
+
+    /* ignore notifications sent after #OnSessionEnd() is called */
+    if (!directControl)
+        return S_OK;
+
+    return directControl->OnCPUChange(aCPU, aRemove);
+}
+
+/**
+ *  @note Locks this object for reading.
+ */
 HRESULT SessionMachine::onVRDPServerChange()
 {
Index: trunk/src/VBox/Main/SessionImpl.cpp
===================================================================
--- trunk/src/VBox/Main/SessionImpl.cpp	(revision 25900)
+++ trunk/src/VBox/Main/SessionImpl.cpp	(revision 25901)
@@ -571,4 +571,18 @@
 
     return mConsole->onMediumChange(aMediumAttachment, aForce);
+}
+
+STDMETHODIMP Session::OnCPUChange(ULONG aCPU, BOOL aRemove)
+{
+    LogFlowThisFunc(("\n"));
+
+    AutoCaller autoCaller(this);
+    AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
+
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+    AssertReturn(mState == SessionState_Open, VBOX_E_INVALID_VM_STATE);
+    AssertReturn(mType == SessionType_Direct, VBOX_E_INVALID_OBJECT_STATE);
+
+    return mConsole->onCPUChange(aCPU, aRemove);
 }
 
Index: trunk/src/VBox/Main/VirtualBoxCallbackImpl.cpp
===================================================================
--- trunk/src/VBox/Main/VirtualBoxCallbackImpl.cpp	(revision 25900)
+++ trunk/src/VBox/Main/VirtualBoxCallbackImpl.cpp	(revision 25901)
@@ -289,4 +289,12 @@
 }
 
+STDMETHODIMP CallbackWrapper::OnCPUChange(ULONG aCPU, BOOL aRemove)
+{
+    if (mConsoleCallback.isNull())
+        return S_OK;
+
+    return mConsoleCallback->OnCPUChange(aCPU, aRemove);
+}
+
 STDMETHODIMP CallbackWrapper::OnRuntimeError(BOOL fFatal, IN_BSTR id, IN_BSTR message)
 {
Index: trunk/src/VBox/Main/idl/VirtualBox.xidl
===================================================================
--- trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 25900)
+++ trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 25901)
@@ -459,6 +459,11 @@
            -->
     </const>
-    <const name="Future"     value="13">
-      <desc>Settings version greater than "1.10", written by a future VirtualBox version.</desc>
+    <const name="v1_11"     value="13">
+      <desc>Settings version "1.10", written by VirtualBox 3.2.x.</desc>
+      <!-- CPU hot-plug support
+           -->
+    </const>
+    <const name="Future"     value="14">
+      <desc>Settings version greater than "1.11", written by a future VirtualBox version.</desc>
     </const>
   </enum>
@@ -4356,4 +4361,10 @@
     </attribute>
 
+    <attribute name="CPUHotPlugEnabled" type="boolean">
+      <desc>
+        This setting determines whether VirtualBox allows CPU
+        hotplugging for this machine.</desc>
+    </attribute>
+
     <attribute name="memorySize" type="unsigned long">
       <desc>System memory size in megabytes.</desc>
@@ -5932,4 +5943,42 @@
       </param>
     </method>
+
+    <method name="HotPlugCPU">
+      <desc>
+        Plugs a CPU into the machine.
+      </desc>
+      <param name="cpu" type="unsigned long" dir="in">
+        <desc>
+          The CPU id to insert.
+        </desc>
+      </param>
+    </method>
+
+    <method name="HotUnplugCPU">
+      <desc>
+        Removes a CPU from the machine.
+      </desc>
+      <param name="cpu" type="unsigned long" dir="in">
+        <desc>
+          The CPU id to remove.
+        </desc>
+      </param>
+    </method>
+
+    <method name="GetCPUStatus">
+      <desc>
+        Plugs a CPU into the machine.
+      </desc>
+      <param name="cpu" type="unsigned long" dir="in">
+        <desc>
+          The CPU id to check for.
+        </desc>
+      </param>
+      <param name="attached" type="boolean" dir="return">
+        <desc>
+          Status of the CPU.
+        </desc>
+      </param>
+    </method>
 </interface>
 
@@ -6116,4 +6165,16 @@
       <param name="mediumAttachment" type="IMediumAttachment" dir="in">
         <desc>Medium attachment that is subject to change.</desc>
+      </param>
+    </method>
+
+    <method name="onCPUChange">
+      <desc>
+        Notification when a CPU changes.
+      </desc>
+      <param name="cpu" type="unsigned long" dir="in">
+        <desc>The CPU which changed</desc>
+      </param>
+      <param name="add" type="boolean" dir="in">
+        <desc>Flag whether the CPU was added or removed</desc>
       </param>
     </method>
@@ -12706,4 +12767,16 @@
       <param name="mediumAttachment" type="IMediumAttachment" dir="in"/>
       <param name="force" type="boolean" dir="in"/>
+    </method>
+
+    <method name="onCPUChange">
+      <desc>
+        Notification when a CPU changes.
+      </desc>
+      <param name="cpu" type="unsigned long" dir="in">
+        <desc>The CPU which changed</desc>
+      </param>
+      <param name="add" type="boolean" dir="in">
+        <desc>Flag whether the CPU was added or removed</desc>
+      </param>
     </method>
 
Index: trunk/src/VBox/Main/include/ConsoleImpl.h
===================================================================
--- trunk/src/VBox/Main/include/ConsoleImpl.h	(revision 25900)
+++ trunk/src/VBox/Main/include/ConsoleImpl.h	(revision 25901)
@@ -179,4 +179,5 @@
     HRESULT onStorageControllerChange ();
     HRESULT onMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce);
+    HRESULT onCPUChange(ULONG aCPU, BOOL aRemove);
     HRESULT onVRDPServerChange();
     HRESULT onUSBControllerChange();
@@ -448,5 +449,9 @@
                                           const char *pszFormat, bool fPassthrough,
                                           bool fForce);
+    static DECLCALLBACK(int) unplugCpu (Console *pThis, unsigned uCpu);
+    static DECLCALLBACK(int) plugCpu (Console *pThis, unsigned uCpu);
     HRESULT doMediumChange(IMediumAttachment *aMediumAttachment, bool fForce);
+    HRESULT doCPURemove(ULONG aCpu);
+    HRESULT doCPUAdd(ULONG aCpu);
 
 #ifdef VBOX_DYNAMIC_NET_ATTACH
Index: trunk/src/VBox/Main/include/DisplayImpl.h
===================================================================
--- trunk/src/VBox/Main/include/DisplayImpl.h	(revision 25900)
+++ trunk/src/VBox/Main/include/DisplayImpl.h	(revision 25901)
@@ -194,4 +194,9 @@
     }
 
+    STDMETHOD(OnCPUChange)(ULONG aCPU, BOOL aRemove)
+    {
+        return S_OK;
+    }
+
     STDMETHOD(OnVRDPServerChange)()
     {
Index: trunk/src/VBox/Main/include/MachineImpl.h
===================================================================
--- trunk/src/VBox/Main/include/MachineImpl.h	(revision 25900)
+++ trunk/src/VBox/Main/include/MachineImpl.h	(revision 25901)
@@ -296,5 +296,8 @@
         BOOL           mSyntheticCpu;
         ULONG          mCPUCount;
+        BOOL           mCPUHotPlugEnabled;
         BOOL           mAccelerate3DEnabled;
+
+        BOOL           mCPUAttached[SchemaDefs::MaxCPUCount];
 
         settings::CpuIdLeaf mCpuIdStdLeafs[10];
@@ -531,4 +534,6 @@
     STDMETHOD(COMGETTER(CPUCount))(ULONG *cpuCount);
     STDMETHOD(COMSETTER(CPUCount))(ULONG cpuCount);
+    STDMETHOD(COMGETTER(CPUHotPlugEnabled))(BOOL *enabled);
+    STDMETHOD(COMSETTER(CPUHotPlugEnabled))(BOOL enabled);
     STDMETHOD(COMGETTER(MemoryBalloonSize))(ULONG *memoryBalloonSize);
     STDMETHOD(COMSETTER(MemoryBalloonSize))(ULONG memoryBalloonSize);
@@ -634,4 +639,7 @@
     STDMETHOD(QuerySavedScreenshotPNGSize)(ULONG *aSize, ULONG *aWidth, ULONG *aHeight);
     STDMETHOD(ReadSavedScreenshotPNGToArray)(ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData));
+    STDMETHOD(HotPlugCPU(ULONG aCpu));
+    STDMETHOD(HotUnplugCPU(ULONG aCpu));
+    STDMETHOD(GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached));
 
     // public methods only for internal purposes
@@ -717,4 +725,5 @@
     virtual HRESULT onUSBControllerChange() { return S_OK; }
     virtual HRESULT onStorageControllerChange() { return S_OK; }
+    virtual HRESULT onCPUChange(ULONG /* aCPU */, BOOL /* aRemove */) { return S_OK; }
     virtual HRESULT onMediumChange(IMediumAttachment * /* mediumAttachment */, BOOL /* force */) { return S_OK; }
     virtual HRESULT onSharedFolderChange() { return S_OK; }
@@ -1036,4 +1045,5 @@
     HRESULT onSerialPortChange(ISerialPort *serialPort);
     HRESULT onParallelPortChange(IParallelPort *parallelPort);
+    HRESULT onCPUChange(ULONG aCPU, BOOL aRemove);
     HRESULT onVRDPServerChange();
     HRESULT onUSBControllerChange();
Index: trunk/src/VBox/Main/include/SessionImpl.h
===================================================================
--- trunk/src/VBox/Main/include/SessionImpl.h	(revision 25900)
+++ trunk/src/VBox/Main/include/SessionImpl.h	(revision 25901)
@@ -96,4 +96,5 @@
     STDMETHOD(OnStorageControllerChange)();
     STDMETHOD(OnMediumChange)(IMediumAttachment *aMediumAttachment, BOOL aForce);
+    STDMETHOD(OnCPUChange)(ULONG aCPU, BOOL aRemove);
     STDMETHOD(OnVRDPServerChange)();
     STDMETHOD(OnUSBControllerChange)();
Index: trunk/src/VBox/Main/include/VirtualBoxCallbackImpl.h
===================================================================
--- trunk/src/VBox/Main/include/VirtualBoxCallbackImpl.h	(revision 25900)
+++ trunk/src/VBox/Main/include/VirtualBoxCallbackImpl.h	(revision 25901)
@@ -94,4 +94,5 @@
     STDMETHOD(OnStorageControllerChange) ();
     STDMETHOD(OnMediumChange)(IMediumAttachment *iMediumAttachment);
+    STDMETHOD(OnCPUChange)(ULONG aCPU, BOOL aRemove);
     STDMETHOD(OnRuntimeError)(BOOL fFatal, IN_BSTR id, IN_BSTR message);
     STDMETHOD(OnCanShowWindow)(BOOL *canShow);
Index: trunk/src/VBox/Main/xml/Settings.cpp
===================================================================
--- trunk/src/VBox/Main/xml/Settings.cpp	(revision 25900)
+++ trunk/src/VBox/Main/xml/Settings.cpp	(revision 25901)
@@ -30,5 +30,5 @@
  *   3) In the settings writer method, write the setting _only_ if the current settings
  *      version (stored in m->sv) is high enough. That is, for VirtualBox 3.2, write it
- *      only if (m->sv >= SettingsVersion_v1_10).
+ *      only if (m->sv >= SettingsVersion_v1_11).
  *
  *   4) In MachineConfigFile::bumpSettingsVersionIfNeeded(), check if the new setting has
@@ -82,5 +82,5 @@
 
 /** VirtualBox XML settings version number substring ("x.y")  */
-#define VBOX_XML_VERSION        "1.9"
+#define VBOX_XML_VERSION        "1.11"
 
 /** VirtualBox XML settings version platform substring */
@@ -282,5 +282,7 @@
                 else if (ulMinor == 10)
                     m->sv = SettingsVersion_v1_10;
-                else if (ulMinor > 10)
+                else if (ulMinor == 11)
+                    m->sv = SettingsVersion_v1_11;
+                else if (ulMinor > 11)
                     m->sv = SettingsVersion_Future;
             }
@@ -302,5 +304,5 @@
         // creating new settings file:
         m->strSettingsVersionFull = VBOX_XML_VERSION_FULL;
-        m->sv = SettingsVersion_v1_10;
+        m->sv = SettingsVersion_v1_11;
     }
 }
@@ -556,8 +558,13 @@
 
         case SettingsVersion_v1_10:
+            pcszVersion = "1.10";
+            m->sv = SettingsVersion_v1_10;
+            break;
+
+        case SettingsVersion_v1_11:
         case SettingsVersion_Future:                // can be set if this code runs on XML files that were created by a future version of VBox;
                                                     // in that case, downgrade to current version when writing since we can't write future versions...
-            pcszVersion = "1.10";
-            m->sv = SettingsVersion_v1_10;
+            pcszVersion = "1.11";
+            m->sv = SettingsVersion_v1_11;
             break;
 
@@ -1263,4 +1270,5 @@
           fPAE(false),
           cCPUs(1),
+          fCpuHotPlug(false),
           ulMemorySizeMB((uint32_t)-1),
           ulVRAMSizeMB(8),
@@ -1284,4 +1292,25 @@
     fPAE = true;
 #endif
+}
+
+/**
+ * Called from MachineConfigFile::readHardware() to read cpu information.
+ * @param elmCpuid
+ * @param ll
+ */
+void MachineConfigFile::readCpuTree(const xml::ElementNode &elmCpu,
+                                    CpuList &ll)
+{
+    xml::NodesLoop nl1(elmCpu, "Cpu");
+    const xml::ElementNode *pelmCpu;
+    while ((pelmCpu = nl1.forAllNodes()))
+    {
+        Cpu cpu;
+
+        if (!pelmCpu->getAttributeValue("id", cpu.ulId))
+            throw ConfigFileError(this, pelmCpu, N_("Required Cpu/@id attribute is missing"));
+
+        ll.push_back(cpu);
+    }
 }
 
@@ -1560,5 +1589,13 @@
             }
 
+            pelmHwChild->getAttributeValue("hotplug", hw.fCpuHotPlug);
+
             const xml::ElementNode *pelmCPUChild;
+            if (hw.fCpuHotPlug)
+            {
+                if ((pelmCPUChild = pelmHwChild->findChildElement("CpuTree")))
+                    readCpuTree(*pelmCPUChild, hw.llCpus);
+            }
+
             if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx")))
             {
@@ -2468,4 +2505,24 @@
         pelmCPU->createChild("SyntheticCpu")->setAttribute("enabled", hw.fSyntheticCpu);
     pelmCPU->setAttribute("count", hw.cCPUs);
+
+    if (m->sv >= SettingsVersion_v1_11)
+    {
+        pelmCPU->setAttribute("hotplug", hw.fCpuHotPlug);
+
+        xml::ElementNode *pelmCpuTree = NULL;
+        for (CpuList::const_iterator it = hw.llCpus.begin();
+             it != hw.llCpus.end();
+             ++it)
+        {
+            const Cpu &cpu = *it;
+
+            if (pelmCpuTree == NULL)
+                 pelmCpuTree = pelmCPU->createChild("CpuTree");
+
+            xml::ElementNode *pelmCpu = pelmCpuTree->createChild("Cpu");
+            pelmCpu->setAttribute("id", cpu.ulId);
+        }
+    }
+
     xml::ElementNode *pelmCpuIdTree = NULL;
     for (CpuIdLeafsList::const_iterator it = hw.llCpuIdLeafs.begin();
@@ -3081,4 +3138,10 @@
        )
         m->sv = SettingsVersion_v1_10;
+
+    // Version 1.11 is required for CPU hotplugging
+    if (   (m->sv < SettingsVersion_v1_11)
+        && (hardwareMachine.fCpuHotPlug)
+       )
+        m->sv = SettingsVersion_v1_11;
 }
 
