Index: /trunk/src/VBox/Devices/PC/DevSMC.cpp
===================================================================
--- /trunk/src/VBox/Devices/PC/DevSMC.cpp	(revision 29563)
+++ /trunk/src/VBox/Devices/PC/DevSMC.cpp	(revision 29564)
@@ -55,8 +55,16 @@
 #include <VBox/stam.h>
 #include <iprt/assert.h>
+#ifdef IN_RING0
+# include <iprt/asm-amd64-x86.h>
+# include <iprt/once.h>
+#endif
 #include <iprt/string.h>
 
 #include "../Builtins2.h"
 
+
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
 /* data port used by Apple SMC */
 #define APPLESMC_DATA_PORT      0x300
@@ -71,9 +79,14 @@
 #define APPLESMC_GET_KEY_TYPE_CMD       0x13
 
-static char osk[64];
-
 /** The version of the saved state. */
 #define SMC_SAVED_STATE_VERSION 1
 
+/** The ring-0 operation number that attempts to get OSK0 and OSK1 from the real
+ *  SMC. */
+#define SMC_CALLR0_READ_OSK     1
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
 typedef struct AppleSMCData
 {
@@ -82,4 +95,29 @@
     const char    *data;
 } AppleSMCData;
+
+
+typedef struct
+{
+    PPDMDEVINSR3   pDevIns;
+
+    uint8_t cmd;
+    uint8_t status;
+    uint8_t key[4];
+    uint8_t read_pos;
+    uint8_t data_len;
+    uint8_t data_pos;
+    uint8_t data[255];
+
+    /** The OSK0 value. This is currently only used in the constructor. */
+    uint8_t abOsk0[32];
+    /** The OSK1 value. This is currently only used in the constructor */
+    uint8_t abOsk1[32];
+} SMCState;
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+#ifdef IN_RING3
+static char osk[64];
 
 /* See http://www.mactel-linux.org/wiki/AppleSMC */
@@ -95,23 +133,145 @@
     {0, NULL,    NULL }
 };
-
-typedef struct
-{
-    PPDMDEVINSR3   pDevIns;
-
-    uint8_t cmd;
-    uint8_t status;
-    uint8_t key[4];
-    uint8_t read_pos;
-    uint8_t data_len;
-    uint8_t data_pos;
-    uint8_t data[255];
-
-    char*    pszDeviceKey;
-} SMCState;
+#endif /* IN_RING3 */
+#ifdef IN_RING0
+/** Do once for the SMC ring-0 static data (g_abOsk0, g_abOsk1, g_fHaveOsk).  */
+static RTONCE   g_SmcR0Once = RTONCE_INITIALIZER;
+/** Indicates whether we've successfully queried the OSK* keys. */
+static bool     g_fHaveOsk = false;
+/** The OSK0 value.   */
+static uint8_t  g_abOsk0[32];
+/** The OSK1 value.  */
+static uint8_t  g_abOsk1[32];
+#endif /* IN_RING0 */
 
 #ifndef VBOX_DEVICE_STRUCT_TESTCASE
 
+#ifdef IN_RING0
+
+/**
+ * Waits for the specified status on the host SMC.
+ *
+ * @returns success indicator.
+ * @param   bStatus             The desired status.
+ * @param   pszWhat             What we're currently doing. For the log.
+ */
+static bool devR0SmcWaitHostStatus(uint8_t bStatus, const char *pszWhat)
+{
+    uint8_t bCurStatus;
+    for (uint32_t cMsSleep = 1; cMsSleep <= 64; cMsSleep <<= 1)
+    {
+        RTThreadSleep(cMsSleep);
+        bCurStatus = ASMInU8(APPLESMC_CMD_PORT);
+        if ((bCurStatus & 0xf) == bStatus)
+            return true;
+    }
+
+    LogRel(("devR0Smc: %s: bCurStatus=%#x, wanted %#x.\n", pszWhat, bCurStatus, bStatus));
+    return false;
+}
+
+/**
+ * Reads a key by name from the host SMC.
+ *
+ * @returns success indicator.
+ * @param   pszName             The key name, must be exactly 4 chars long.
+ * @param   pbBuf               The output buffer.
+ * @param   cbBuf               The buffer size. Max 32 bytes.
+ */
+static bool devR0SmcQueryHostKey(const char *pszName, uint8_t *pbBuf, size_t cbBuf)
+{
+    Assert(strlen(pszName) == 4);
+    Assert(cbBuf <= 32);
+    Assert(cbBuf > 0);
+
+    /*
+     * Issue the READ command.
+     */
+    uint32_t cMsSleep = 1;
+    for (;;)
+    {
+        ASMOutU8(APPLESMC_CMD_PORT, APPLESMC_READ_CMD);
+        RTThreadSleep(cMsSleep);
+        uint8_t bCurStatus = ASMInU8(APPLESMC_CMD_PORT);
+        if ((bCurStatus & 0xf) == 0xc)
+            break;
+        cMsSleep <<= 1;
+        if (cMsSleep > 64)
+        {
+            LogRel(("devR0Smc: %s: bCurStatus=%#x, wanted %#x.\n", "cmd", bCurStatus, 0xc));
+            return false;
+        }
+    }
+
+    /*
+     * Send it the key.
+     */
+    for (unsigned off = 0; off < 4; off++)
+    {
+        ASMOutU8(APPLESMC_DATA_PORT, pszName[off]);
+        if (!devR0SmcWaitHostStatus(4, "key"))
+            return false;
+    }
+
+    /*
+     * The desired amount of output.
+     */
+    ASMOutU8(APPLESMC_DATA_PORT, (uint8_t)cbBuf);
+
+    /*
+     * Read the output.
+     */
+    for (size_t off = 0; off < cbBuf; off++)
+    {
+        if (!devR0SmcWaitHostStatus(5, off ? "data" : "len"))
+            return false;
+        pbBuf[off] = ASMInU8(APPLESMC_DATA_PORT);
+    }
+
+    return true;
+}
+
+/**
+ * RTOnce callback that initializes g_fHaveOsk, g_abOsk0 and g_abOsk1.
+ *
+ * @returns VINF_SUCCESS.
+ * @param   pvUser1Ignored  Ignored.
+ * @param   pvUser2Ignored  Ignored.
+ */
+static DECLCALLBACK(int) devR0SmcInitOnce(void *pvUser1Ignored, void *pvUser2Ignored)
+{
+    g_fHaveOsk = devR0SmcQueryHostKey("OSK0", &g_abOsk0[0], sizeof(g_abOsk0))
+              && devR0SmcQueryHostKey("OSK1", &g_abOsk1[0], sizeof(g_abOsk1));
+
+    NOREF(pvUser1Ignored); NOREF(pvUser2Ignored);
+    return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{FNPDMDEVREQHANDLERR0}
+ */
+PDMBOTHCBDECL(int) devR0SmcReqHandler(PPDMDEVINS pDevIns, uint32_t uOperation, uint64_t u64Arg)
+{
+    SMCState *pThis = PDMINS_2_DATA(pDevIns, SMCState *);
+    int       rc    = VERR_INVALID_FUNCTION;
+
+    if (uOperation == SMC_CALLR0_READ_OSK)
+    {
+        rc = RTOnce(&g_SmcR0Once, devR0SmcInitOnce, NULL, NULL);
+        if (   RT_SUCCESS(rc)
+            && g_fHaveOsk)
+        {
+            AssertCompile(sizeof(g_abOsk0) == sizeof(pThis->abOsk0));
+            AssertCompile(sizeof(g_abOsk1) == sizeof(pThis->abOsk1));
+            memcpy(pThis->abOsk0, g_abOsk0, sizeof(pThis->abOsk0));
+            memcpy(pThis->abOsk1, g_abOsk1, sizeof(pThis->abOsk1));
+        }
+    }
+    return rc;
+}
+
+#endif /* IN_RING0 */
 #ifdef IN_RING3
+
 /**
  * Saves a state of the SMC device.
@@ -149,17 +309,4 @@
     /** @todo: implement serialization */
     return VINF_SUCCESS;
-}
-
-/**
- * Relocation notification.
- *
- * @returns VBox status.
- * @param   pDevIns     The device instance data.
- * @param   offDelta    The delta relative to the old address.
- */
-static DECLCALLBACK(void) smcRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
-{
-    SMCState *pThis = PDMINS_2_DATA(pDevIns, SMCState *);
-    /* SMC device lives only in R3 now, thus nothing to relocate yet */
 }
 
@@ -335,5 +482,4 @@
 {
     SMCState   *pThis = PDMINS_2_DATA(pDevIns, SMCState *);
-    int         rc;
     Assert(iInstance == 0);
 
@@ -346,28 +492,48 @@
      * Validate and read the configuration.
      */
-    if (!CFGMR3AreValuesValid(pCfg,
-                              "DeviceKey\0"
-                              ))
-        return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
-                                N_("Configuration error: Invalid config value(s) for the SMC device"));
-
-    /*
-     * Query device key
-     */
-    rc = CFGMR3QueryStringAlloc(pCfg, "DeviceKey", &pThis->pszDeviceKey);
-    if (rc == VERR_CFGM_VALUE_NOT_FOUND)
-    {
-        pThis->pszDeviceKey = RTStrDup("Invalid");
-        LogRel(("Invalid SMC device key\n"));
-        if (!pThis->pszDeviceKey)
-            return VERR_NO_MEMORY;
-
-        rc = VINF_SUCCESS;
-    }
-    else if (RT_FAILURE(rc))
+    PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DeviceKey|GetKeyFromRealSMC", "");
+
+    /*
+     * Read the DeviceKey config value.
+     */
+    char *pszDeviceKey;
+    int rc = CFGMR3QueryStringAllocDef(pCfg, "DeviceKey", &pszDeviceKey, "");
+    if (RT_FAILURE(rc))
         return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
                                    N_("Configuration error: Querying \"DeviceKey\" as a string failed"));
 
-    memcpy(osk, pThis->pszDeviceKey, RTStrNLen(pThis->pszDeviceKey, 64));
+    size_t cchDeviceKey = strlen(pszDeviceKey);
+    if (cchDeviceKey > 0)
+        memcpy(&pThis->abOsk0[0], pszDeviceKey, RT_MIN(cchDeviceKey, sizeof(pThis->abOsk0)));
+    if (cchDeviceKey > sizeof(pThis->abOsk0))
+        memcpy(&pThis->abOsk1[0], &pszDeviceKey[sizeof(pThis->abOsk0)],
+               RT_MIN(cchDeviceKey - sizeof(pThis->abOsk0), sizeof(pThis->abOsk1)));
+
+    MMR3HeapFree(pszDeviceKey);
+
+    /*
+     * Query the key from the real hardware if asked to do so.
+     */
+    bool fGetKeyFromRealSMC;
+    rc = CFGMR3QueryBoolDef(pCfg, "GetKeyFromRealSMC", &fGetKeyFromRealSMC, false);
+    if (RT_FAILURE(rc))
+        return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+                                   N_("Configuration error: Querying \"GetKeyFromRealSMC\" as a boolean failed"));
+    if (fGetKeyFromRealSMC)
+    {
+        rc = PDMDevHlpCallR0(pDevIns, SMC_CALLR0_READ_OSK, 0 /*u64Arg*/);
+        if (RT_FAILURE(rc))
+            return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+                                       N_("Failed to query SMC value from the host"));
+    }
+
+    /*
+     * For practical/historical reasons, the OSK[0|1] data is stored in a
+     * global buffer in ring-3.
+     */
+    AssertCompile(sizeof(osk) == sizeof(pThis->abOsk0) + sizeof(pThis->abOsk1));
+    AssertCompile(sizeof(char) == sizeof(uint8_t));
+    memcpy(osk, pThis->abOsk0, sizeof(pThis->abOsk0));
+    memcpy(&osk[sizeof(pThis->abOsk0)], pThis->abOsk1, sizeof(pThis->abOsk1));
 
     /*
@@ -375,11 +541,11 @@
      */
     rc = PDMDevHlpIOPortRegister(pDevIns, APPLESMC_DATA_PORT, 1, NULL,
-                                  smcIOPortWrite, smcIOPortRead,
-                                  NULL, NULL, "SMC Data");
+                                 smcIOPortWrite, smcIOPortRead,
+                                 NULL, NULL, "SMC Data");
     if (RT_FAILURE(rc))
         return rc;
     rc = PDMDevHlpIOPortRegister(pDevIns, APPLESMC_CMD_PORT, 1, NULL,
-                                  smcIOPortWrite, smcIOPortRead,
-                                  NULL, NULL, "SMC Commands");
+                                 smcIOPortWrite, smcIOPortRead,
+                                 NULL, NULL, "SMC Commands");
     if (RT_FAILURE(rc))
         return rc;
@@ -399,29 +565,4 @@
      */
     PDMDevHlpDBGFInfoRegister(pDevIns, "smc", "Display SMC status. (no arguments)", smcInfo);
-
-    return VINF_SUCCESS;
-}
-
-
-/**
- * Destruct a device instance.
- *
- * Most VM resources are freed by the VM. This callback is provided so that any non-VM
- * resources can be freed correctly.
- *
- * @param   pDevIns     The device instance data.
- */
-static DECLCALLBACK(int) smcDestruct(PPDMDEVINS pDevIns)
-{
-    SMCState*  pThis = PDMINS_2_DATA(pDevIns, SMCState*);
-
-    /*
-     * Free MM heap pointers.
-     */
-    if (pThis->pszDeviceKey)
-    {
-        MMR3HeapFree(pThis->pszDeviceKey);
-        pThis->pszDeviceKey = NULL;
-    }
 
     return VINF_SUCCESS;
@@ -444,5 +585,5 @@
     "System Management Controller (SMC) Device",
     /* fFlags */
-    PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36,
+    PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36| PDM_DEVREG_FLAGS_R0,
     /* fClass */
     PDM_DEVREG_CLASS_MISC,
@@ -454,7 +595,7 @@
     smcConstruct,
     /* pfnDestruct */
-    smcDestruct,
+    NULL,
     /* pfnRelocate */
-    smcRelocate,
+    NULL,
     /* pfnIOCtl */
     NULL,
Index: /trunk/src/VBox/Main/ConsoleImpl2.cpp
===================================================================
--- /trunk/src/VBox/Main/ConsoleImpl2.cpp	(revision 29563)
+++ /trunk/src/VBox/Main/ConsoleImpl2.cpp	(revision 29564)
@@ -30,4 +30,5 @@
 #endif
 #include "VMMDev.h"
+#include "Global.h"
 
 // generated header
@@ -38,12 +39,14 @@
 
 #include <iprt/buildconfig.h>
+#include <iprt/ctype.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
 #include <iprt/string.h>
-#include <iprt/path.h>
-#include <iprt/dir.h>
-#include <iprt/param.h>
+#include <iprt/system.h>
 #if 0 /* enable to play with lots of memory. */
 # include <iprt/env.h>
 #endif
-#include <iprt/file.h>
 
 #include <VBox/vmapi.h>
@@ -101,9 +104,9 @@
 #include "DHCPServerRunner.h"
 
-#if defined(RT_OS_DARWIN) && !defined(VBOX_OSE)
+#if defined(RT_OS_DARWIN)
 
 # include "IOKit/IOKitLib.h"
 
-static int DarwinSmcKey(char *aKey, uint32_t iKeySize)
+static int DarwinSmcKey(char *pabKey, uint32_t cbKey)
 {
     /*
@@ -122,10 +125,10 @@
     } AppleSMCBuffer;
 
-    AssertReturn(iKeySize >= 65, VERR_INTERNAL_ERROR);
+    AssertReturn(cbKey >= 65, VERR_INTERNAL_ERROR);
 
     io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault,
                                                        IOServiceMatching("AppleSMC"));
     if (!service)
-        return VERR_INTERNAL_ERROR;
+        return VERR_NOT_FOUND;
 
     io_connect_t    port = (io_connect_t)0;
@@ -134,5 +137,5 @@
 
     if (kr != kIOReturnSuccess)
-        return VERR_INTERNAL_ERROR;
+        return RTErrConvertFromDarwin(kr);
 
     AppleSMCBuffer  inputStruct    = { 0, {0}, 32, {0}, 5, };
@@ -152,14 +155,14 @@
         {
             IOServiceClose(port);
-            return VERR_INTERNAL_ERROR;
+            return RTErrConvertFromDarwin(kr);
         }
 
         for (int j = 0; j < 32; j++)
-            aKey[j + i*32] = outputStruct.data[j];
+            pabKey[j + i*32] = outputStruct.data[j];
     }
 
     IOServiceClose(port);
 
-    aKey[64] = 0;
+    pabKey[64] = 0;
 
     return VINF_SUCCESS;
@@ -236,19 +239,68 @@
 }
 
-static int getSmcDeviceKey(IMachine* pMachine, BSTR * aKey)
+static int getSmcDeviceKey(IMachine *pMachine, BSTR *aKey, bool *pfGetKeyFromRealSMC)
 {
-# if defined(RT_OS_DARWIN) && !defined(VBOX_OSE)
-    int rc;
-    char aKeyBuf[65];
-
-    rc = DarwinSmcKey(aKeyBuf, sizeof aKeyBuf);
+    *pfGetKeyFromRealSMC = false;
+
+    /*
+     * The extra data takes precedence (if non-zero).
+     */
+    HRESULT hrc = pMachine->GetExtraData(Bstr("VBoxInternal2/SmcDeviceKey"), aKey);
+    if (FAILED(hrc))
+        return Global::vboxStatusCodeFromCOM(hrc);
+    if (   SUCCEEDED(hrc)
+        && *aKey
+        && **aKey)
+        return VINF_SUCCESS;
+
+#ifdef RT_OS_DARWIN
+    /*
+     * Query it here and now.
+     */
+    char abKeyBuf[65];
+    int rc = DarwinSmcKey(abKeyBuf, sizeof(abKeyBuf));
     if (SUCCEEDED(rc))
     {
-        Bstr(aKeyBuf).detachTo(aKey);
+        Bstr(abKeyBuf).detachTo(aKey);
         return rc;
     }
+    LogRel(("Warning: DarwinSmcKey failed with rc=%Rrc!\n", rc));
+
+#else
+    /*
+     * Is it apple hardware in bootcamp?
+     */
+    /** @todo implement + test RTSYSDMISTR_MANUFACTURER on all hosts.
+     *        Currently falling back on the product name. */
+    char szManufacturer[256];
+    szManufacturer[0] = '\0';
+    RTSystemQueryDmiString(RTSYSDMISTR_MANUFACTURER, szManufacturer, sizeof(szManufacturer));
+    if (szManufacturer[0] != '\0')
+    {
+        if (   !strcmp(szManufacturer, "Apple Computer, Inc.")
+            || !strcmp(szManufacturer, "Apple Inc.")
+            )
+            *pfGetKeyFromRealSMC = true;
+    }
+    else
+    {
+        char szProdName[256];
+        szProdName[0] = '\0';
+        RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME, szProdName, sizeof(szProdName));
+        if (   (   !strncmp(szProdName, "Mac", 3)
+                || !strncmp(szProdName, "iMac", 4)
+                || !strncmp(szProdName, "iMac", 4)
+                || !strncmp(szProdName, "Xserve", 6)
+               )
+            && !strchr(szProdName, ' ')                             /* no spaces */
+            && RT_C_IS_DIGIT(szProdName[strlen(szProdName) - 1])    /* version number */
+           )
+            *pfGetKeyFromRealSMC = true;
+    }
+
+    int rc = VINF_SUCCESS;
 #endif
 
-    return pMachine->GetExtraData(Bstr("VBoxInternal2/SmcDeviceKey"), aKey);
+    return rc;
 }
 
@@ -674,11 +726,13 @@
     if (fSmcEnabled)
     {
-        Bstr tmpStr2;
         rc = CFGMR3InsertNode(pDevices, "smc", &pDev);                                  RC_CHECK();
         rc = CFGMR3InsertNode(pDev,     "0", &pInst);                                   RC_CHECK();
         rc = CFGMR3InsertInteger(pInst, "Trusted",   1);     /* boolean */              RC_CHECK();
         rc = CFGMR3InsertNode(pInst,    "Config", &pCfg);                               RC_CHECK();
-        rc = getSmcDeviceKey(pMachine,   tmpStr2.asOutParam());                         RC_CHECK();
-        rc = CFGMR3InsertString(pCfg,   "DeviceKey", Utf8Str(tmpStr2).raw());           RC_CHECK();
+        bool fGetKeyFromRealSMC;
+        Bstr bstrKey;
+        rc = getSmcDeviceKey(pMachine, bstrKey.asOutParam(), &fGetKeyFromRealSMC);      RC_CHECK();
+        rc = CFGMR3InsertString(pCfg,   "DeviceKey", Utf8Str(bstrKey).raw());           RC_CHECK();
+        rc = CFGMR3InsertInteger(pCfg,  "GetKeyFromRealSMC", fGetKeyFromRealSMC);       RC_CHECK();
     }
 
