summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/DevSmc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/DevSmc.cpp')
-rw-r--r--src/VBox/Devices/EFI/DevSmc.cpp1611
1 files changed, 1611 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/DevSmc.cpp b/src/VBox/Devices/EFI/DevSmc.cpp
new file mode 100644
index 00000000..df703287
--- /dev/null
+++ b/src/VBox/Devices/EFI/DevSmc.cpp
@@ -0,0 +1,1611 @@
+/* $Id: DevSmc.cpp $ */
+/** @file
+ * DevSmc - Apple System Management Controller.
+ *
+ * The SMC is controlling power, fans, take measurements (voltage, temperature,
+ * fan speed, ++), and lock Mac OS X to Apple hardware. For more details see:
+ * - http://en.wikipedia.org/wiki/System_Management_Controller
+ * - http://www.parhelia.ch/blog/statics/k3_keys.html
+ * - http://www.nosuchcon.org/talks/D1_02_Alex_Ninjas_and_Harry_Potter.pdf
+ */
+
+/*
+ * Copyright (C) 2013-2022 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_SMC
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#if defined(IN_RING0) && (defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86))
+# include <iprt/asm-amd64-x86.h>
+# include <iprt/once.h>
+#endif
+#if defined(RT_OS_DARWIN) && defined(IN_RING3) && !defined(VBOX_DEVICE_STRUCT_TESTCASE) /* drags in bad page size define */
+# include "IOKit/IOKitLib.h"
+#endif
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The current version of the saved state. */
+#define SMC_SAVED_STATE_VERSION 1 /** @todo later 2 */
+/** Empty saved state version. */
+#define SMC_SAVED_STATE_VERSION_BAKA 1
+
+/** The ring-0 operation number that attempts to get OSK0 and OSK1 from the real
+ * SMC. */
+#define SMC_CALLR0_READ_OSK 1
+
+
+/** @name Apple SMC port and register definitions.
+ * @{ */
+
+/** The first Apple SMC port. */
+#define SMC_PORT_FIRST 0x0300
+/** The number of registers (also ports). */
+#define SMC_REG_COUNT 0x0020
+
+/** The data register. */
+#define SMC_REG_DATA 0x00
+#define SMC_PORT_DATA (SMC_PORT_FIRST + SMC_REG_DATA)
+
+/** The command register. */
+#define SMC_REG_CMD 0x04
+#define SMC_PORT_CMD (SMC_PORT_FIRST + SMC_REG_CMD)
+
+/** Status code register. */
+#define SMC_REG_STATUS_CODE 0x1e
+#define SMC_PORT_STATUS_CODE (SMC_PORT_FIRST + SMC_REG_STATUS_CODE)
+/** @} */
+
+/** @name Apple SMC Commands.
+ * @{ */
+#define SMC_CMD_GET_KEY_VALUE 0x10
+#define SMC_CMD_PUT_KEY 0x11
+#define SMC_CMD_GET_KEY_BY_INDEX 0x12
+#define SMC_CMD_GET_KEY_INFO 0x13
+/** @} */
+
+/** @name Apple SMC Status Codes.
+ * @{ */
+#define SMC_STATUS_CD_SUCCESS UINT8_C(0x00)
+#define SMC_STATUS_CD_COMM_COLLISION UINT8_C(0x80)
+#define SMC_STATUS_CD_SPURIOUS_DATA UINT8_C(0x81)
+#define SMC_STATUS_CD_BAD_COMMAND UINT8_C(0x82)
+#define SMC_STATUS_CD_BAD_PARAMETER UINT8_C(0x83)
+#define SMC_STATUS_CD_KEY_NOT_FOUND UINT8_C(0x84)
+#define SMC_STATUS_CD_KEY_NOT_READABLE UINT8_C(0x85)
+#define SMC_STATUS_CD_KEY_NOT_WRITABLE UINT8_C(0x86)
+#define SMC_STATUS_CD_KEY_SIZE_MISMATCH UINT8_C(0x87)
+#define SMC_STATUS_CD_FRAMING_ERROR UINT8_C(0x88)
+#define SMC_STATUS_CD_BAD_ARGUMENT_ERROR UINT8_C(0x89)
+#define SMC_STATUS_CD_TIMEOUT_ERROR UINT8_C(0xb7)
+#define SMC_STATUS_CD_KEY_INDEX_RANGE_ERROR UINT8_C(0xb8)
+#define SMC_STATUS_CD_BAD_FUNC_PARAMETER UINT8_C(0xc0)
+#define SMC_STATUS_CD_EVENT_BUFF_WRONG_ORDER UINT8_C(0x??)
+#define SMC_STATUS_CD_EVENT_BUFF_READ_ERROR UINT8_C(0x??)
+#define SMC_STATUS_CD_DEVICE_ACCESS_ERROR UINT8_C(0xc7)
+#define SMC_STATUS_CD_UNSUPPORTED_FEATURE UINT8_C(0xcb)
+#define SMC_STATUS_CD_SMB_ACCESS_ERROR UINT8_C(0xcc)
+/** @} */
+
+/** @name Apple SMC Key Attributes.
+ * @{ */
+#define SMC_KEY_ATTR_PRIVATE UINT8_C(0x01)
+#define SMC_KEY_ATTR_UKN_0x02 UINT8_C(0x02)
+#define SMC_KEY_ATTR_UKN_0x04 UINT8_C(0x04)
+#define SMC_KEY_ATTR_CONST UINT8_C(0x08)
+#define SMC_KEY_ATTR_FUNCTION UINT8_C(0x10)
+#define SMC_KEY_ATTR_UKN_0x20 UINT8_C(0x20)
+#define SMC_KEY_ATTR_WRITE UINT8_C(0x40)
+#define SMC_KEY_ATTR_READ UINT8_C(0x80)
+/** @} */
+
+
+/** The index of the first enumerable key in g_aSmcKeys. */
+#define SMC_KEYIDX_FIRST_ENUM 2
+
+/** Macro for emitting a static DEVSMC4CHID initializer. */
+#define SMC4CH(ch1, ch2, ch3, ch4) { { ch1, ch2, ch3, ch4 } }
+
+/**
+ * Macro for comparing DEVSMC4CHID with a string value.
+ * @returns true if equal, false if not.
+ */
+#define SMC4CH_EQ(a_pSmcKey, a_sz4) ( (a_pSmcKey)->u32 == RT_MAKE_U32_FROM_U8(a_sz4[0], a_sz4[1], a_sz4[2], a_sz4[3]) )
+
+/** Indicates the we want a 2.x SMC. */
+#define VBOX_WITH_SMC_2_x
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * 4 char identifier
+ */
+typedef union DEVSMC4CHID
+{
+ /** Byte view. */
+ uint8_t ab[4];
+ /** 32-bit unsigned integer view. */
+ uint32_t u32;
+} DEVSMC4CHID;
+
+
+/**
+ * Current key data area for communicating with the guest.
+ */
+typedef struct DEVSMCCURKEY
+{
+ /** The key. */
+ DEVSMC4CHID Key;
+ /** The data type. */
+ DEVSMC4CHID Type;
+ /** Key attributes. */
+ uint8_t fAttr;
+ /** The value length. */
+ uint8_t cbValue;
+ uint8_t abAlignment[2];
+ /**
+ * The value union. 32 bytes is probably sufficient here, but we provide a
+ * little more room since it doesn't cost us anything. */
+ union
+ {
+ /** Byte view. */
+ uint8_t ab[128];
+ /** 16-bit view. */
+ uint16_t u16;
+ /** 32-bit view. */
+ uint32_t u32;
+ } Value;
+} DEVSMCCURKEY;
+AssertCompileSize(DEVSMCCURKEY, 128+12);
+/** Pointer to the current key buffer. */
+typedef DEVSMCCURKEY *PDEVSMCCURKEY;
+/** Const pointer to the current key buffer. */
+typedef DEVSMCCURKEY const *PCDEVSMCCURKEY;
+
+
+/**
+ * The device
+ */
+typedef struct DEVSMC
+{
+ /** The current command (SMC_PORT_CMD write). */
+ uint8_t bCmd;
+ /** Current key offset. */
+ uint8_t offKey;
+ /** Current value offset. */
+ uint8_t offValue;
+ /** Number of keys in the aKeys array. */
+ uint8_t cKeys;
+
+ /** The current key data the user is accessing. */
+ DEVSMCCURKEY CurKey;
+
+ /**
+ * Generic read/write register values.
+ *
+ * The DATA register entry is not used at all. The CMD register entry contains
+ * the state value.
+ */
+ union
+ {
+ /** Index register view. */
+ uint8_t abRegsRW[SMC_REG_COUNT];
+ /** Named register view. */
+ struct
+ {
+ uint8_t abUnknown0[0x04];
+ /** The current state (SMC_PORT_CMD read). */
+ uint8_t bState;
+ uint8_t abUnknown1[0x1e - 0x05];
+ /** The current status code (SMC_PORT_STATUS_CODE). */
+ uint8_t bStatusCode;
+ uint8_t abUnknown2[1];
+ } s;
+ } u;
+
+ /** @name Key data.
+ * @{ */
+ /** OSK0 and OSK1. */
+ char szOsk0And1[64+1];
+ /** $Num - unknown function. */
+ uint8_t bDollaryNumber;
+ /** MSSD - shutdown reason. */
+ uint8_t bShutdownReason;
+ /** NATJ - Ninja action timer job. */
+ uint8_t bNinjaActionTimerJob;
+ /** @} */
+
+ /** The I/O port registration handle. */
+ IOMIOPORTHANDLE hIoPorts;
+} DEVSMC;
+#ifndef _MSC_VER
+AssertCompileMembersAtSameOffset(DEVSMC, u.abRegsRW[SMC_REG_CMD], DEVSMC, u.s.bState);
+AssertCompileMembersAtSameOffset(DEVSMC, u.abRegsRW[SMC_REG_STATUS_CODE], DEVSMC, u.s.bStatusCode);
+#endif
+
+/** Pointer to the SMC state. */
+typedef DEVSMC *PDEVSMC;
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+
+/**
+ * Method for retriving the key value and/or optionally also attributes.
+ *
+ * @returns Apple SMC Status Code.
+ * @param pThis The SMC instance data.
+ * @param pCurKey The current key structure (input / output).
+ * @param bCmd The current command (mostly for getters that also
+ * provides attributes or type info).
+ * @param pKeyDesc Pointer to the key descriptor so that the getter can
+ * service more than once key.
+ */
+typedef DECLCALLBACKTYPE(uint8_t, FNDEVSMCKEYGETTER,(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd,
+ struct DEVSMCKEYDESC const *pKeyDesc));
+
+/**
+ * Method for setting the key value.
+ *
+ * @returns Apple SMC Status Code.
+ * @param pThis The SMC instance data.
+ * @param pCurKey The current key structure (input / output).
+ * @param bCmd The current command (currently not relevant).
+ * @param pKeyDesc Pointer to the key descriptor so that the getter can
+ * service more than once key.
+ */
+typedef DECLCALLBACKTYPE(uint8_t, FNDEVSMCKEYPUTTER,(PDEVSMC pThis, PCDEVSMCCURKEY pCurKey, uint8_t bCmd,
+ struct DEVSMCKEYDESC const *pKeyDesc));
+
+/**
+ * Key descriptor.
+ */
+typedef struct DEVSMCKEYDESC
+{
+ /** The key 4 character identifier. */
+ DEVSMC4CHID Key;
+ /** Type 4 character identifier. 0 means the getter will set it dynamically. */
+ DEVSMC4CHID Type;
+ /** Getter method, see FNDEVSMCKEYGETTER. */
+ FNDEVSMCKEYGETTER *pfnGet;
+ /** Putter method, see FNDEVSMCKEYPUTTER. */
+ FNDEVSMCKEYPUTTER *pfnPut;
+ /** The keyvalue size. If 0 the pfnGet/pfnPut will define/check the size. */
+ uint8_t cbValue;
+ /** Attributes. 0 means the getter will set it dynamically. */
+ uint8_t fAttr;
+} DEVSMCKEYDESC;
+/** Pointer to a constant SMC key descriptor. */
+typedef DEVSMCKEYDESC const *PCDEVSMCKEYDESC;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static FNDEVSMCKEYGETTER scmKeyGetOSKs;
+static FNDEVSMCKEYGETTER scmKeyGetKeyCount;
+static FNDEVSMCKEYGETTER scmKeyGetRevision;
+#ifdef VBOX_WITH_SMC_2_x
+static FNDEVSMCKEYGETTER scmKeyGetDollarAddress;
+static FNDEVSMCKEYGETTER scmKeyGetDollarNumber;
+static FNDEVSMCKEYPUTTER scmKeyPutDollarNumber;
+#endif
+static FNDEVSMCKEYGETTER scmKeyGetShutdownReason;
+static FNDEVSMCKEYPUTTER scmKeyPutShutdownReason;
+static FNDEVSMCKEYGETTER scmKeyGetNinjaTimerAction;
+static FNDEVSMCKEYPUTTER scmKeyPutNinjaTimerAction;
+static FNDEVSMCKEYGETTER scmKeyGetOne;
+static FNDEVSMCKEYGETTER scmKeyGetZero;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Apple SMC key descriptor table.
+ */
+static const DEVSMCKEYDESC g_aSmcKeys[] =
+{
+ /* Non-enum keys first. */
+ { SMC4CH('O','S','K','0'), SMC4CH('c','h','8','*'), scmKeyGetOSKs, NULL, 32, SMC_KEY_ATTR_READ | SMC_KEY_ATTR_FUNCTION },
+ { SMC4CH('O','S','K','1'), SMC4CH('c','h','8','*'), scmKeyGetOSKs, NULL, 32, SMC_KEY_ATTR_READ | SMC_KEY_ATTR_FUNCTION },
+
+ /* The first enum key is the #KEY value. */
+ { SMC4CH('#','K','E','Y'), SMC4CH('u','i','3','2'), scmKeyGetKeyCount, NULL, 4, SMC_KEY_ATTR_READ },
+# ifdef VBOX_WITH_SMC_2_x
+ { SMC4CH('$','A','d','r'), SMC4CH('u','i','3','2'), scmKeyGetDollarAddress, NULL, 4, SMC_KEY_ATTR_READ },
+ { SMC4CH('$','N','u','m'), SMC4CH('u','i','8',' '), scmKeyGetDollarNumber, scmKeyPutDollarNumber, 1, SMC_KEY_ATTR_READ | SMC_KEY_ATTR_WRITE | SMC_KEY_ATTR_PRIVATE },
+ { SMC4CH('B','E','M','B'), SMC4CH('f','l','a','g'), scmKeyGetOne, NULL, 1, SMC_KEY_ATTR_READ },
+# else
+ { SMC4CH('L','S','O','F'), SMC4CH('f','l','a','g'), scmKeyGetZero, NULL, 1, SMC_KEY_ATTR_READ },
+# endif
+ { SMC4CH('M','S','S','D'), SMC4CH('s','i','8',' '), scmKeyGetShutdownReason, scmKeyPutShutdownReason, 1, SMC_KEY_ATTR_READ | SMC_KEY_ATTR_WRITE | SMC_KEY_ATTR_PRIVATE },
+ /* MSDS is not present on MacPro3,1 nor MacBookPro10,1, so returning not found is fine. */
+# ifdef VBOX_WITH_SMC_2_x
+ { SMC4CH('M','S','T','f'), SMC4CH('u','i','8',' '), scmKeyGetZero, NULL, 1, SMC_KEY_ATTR_READ },
+# endif
+ { SMC4CH('N','A','T','J'), SMC4CH('u','i','8',' '), scmKeyGetNinjaTimerAction, scmKeyPutNinjaTimerAction, 1, SMC_KEY_ATTR_READ | SMC_KEY_ATTR_WRITE | SMC_KEY_ATTR_PRIVATE },
+ { SMC4CH('R','E','V',' '), SMC4CH('{','r','e','v'), scmKeyGetRevision, NULL, 6, SMC_KEY_ATTR_READ },
+/** @todo MSSP, NTOK and more. */
+};
+
+#if defined(IN_RING0) && (defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86))
+
+/** Do once for the SMC ring-0 static data (g_abOsk0And1, g_fHaveOsk). */
+static RTONCE g_SmcR0Once = RTONCE_INITIALIZER;
+/** Indicates whether we've successfully queried the OSK* keys. */
+static bool g_fHaveOsk = false;
+/** The OSK0 and OSK1 values. */
+static uint8_t g_abOsk0And1[32+32];
+
+
+/**
+ * Waits for the specified state on the host SMC.
+ *
+ * @returns success indicator.
+ * @param bState The desired state.
+ * @param pszWhat What we're currently doing. For the log.
+ */
+static bool devR0SmcWaitHostState(uint8_t bState, const char *pszWhat)
+{
+ uint8_t bCurState = 0; /* (MSC is potentially uninitialized) */
+ for (uint32_t cMsSleep = 1; cMsSleep <= 64; cMsSleep <<= 1)
+ {
+ RTThreadSleep(cMsSleep);
+ bCurState = ASMInU16(SMC_PORT_CMD);
+ if ((bCurState & 0xf) == bState)
+ return true;
+ }
+
+ LogRel(("devR0Smc: %s: bCurState=%#x, wanted %#x.\n", pszWhat, bCurState, bState));
+#if 0
+ uint8_t bCurStatus2 = ASMInU8(SMC_PORT_STATUS_CODE);
+ uint8_t bCurStatus3 = ASMInU8(SMC_PORT_STATUS_CODE);
+ uint16_t wCurStatus3 = ASMInU16(SMC_PORT_STATUS_CODE);
+ uint32_t dwCurStatus3 = ASMInU32(SMC_PORT_STATUS_CODE);
+ LogRel(("SMC: status2=%#x status3=%#x w=%#x dw=%#x\n", bCurStatus2, bCurStatus3, wCurStatus3, dwCurStatus3));
+#endif
+ 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 (;;)
+ {
+ ASMOutU32(SMC_PORT_CMD, SMC_CMD_GET_KEY_VALUE);
+ RTThreadSleep(cMsSleep);
+ uint8_t bCurState = ASMInU8(SMC_PORT_CMD);
+ if ((bCurState & 0xf) == 0xc)
+ break;
+ cMsSleep <<= 1;
+ if (cMsSleep > 64)
+ {
+ LogRel(("devR0Smc: %s: bCurState=%#x, wanted %#x.\n", "cmd", bCurState, 0xc));
+ return false;
+ }
+ }
+
+ /*
+ * Send it the key.
+ */
+ for (unsigned off = 0; off < 4; off++)
+ {
+ ASMOutU8(SMC_PORT_DATA, pszName[off]);
+ if (!devR0SmcWaitHostState(4, "key"))
+ return false;
+ }
+
+ /*
+ * The desired amount of output.
+ */
+ ASMOutU8(SMC_PORT_DATA, (uint8_t)cbBuf);
+
+ /*
+ * Read the output.
+ */
+ for (size_t off = 0; off < cbBuf; off++)
+ {
+ if (!devR0SmcWaitHostState(5, off ? "data" : "len"))
+ return false;
+ pbBuf[off] = ASMInU8(SMC_PORT_DATA);
+ }
+
+ LogRel(("SMC: pbBuf=%.*s\n", cbBuf, pbBuf));
+ return true;
+}
+
+
+/**
+ * RTOnce callback that initializes g_fHaveOsk and g_abOsk0And1.
+ *
+ * @returns VINF_SUCCESS.
+ * @param pvUserIgnored Ignored.
+ */
+static DECLCALLBACK(int) devR0SmcInitOnce(void *pvUserIgnored)
+{
+ g_fHaveOsk = devR0SmcQueryHostKey("OSK0", &g_abOsk0And1[0], 32)
+ && devR0SmcQueryHostKey("OSK1", &g_abOsk0And1[32], 32);
+
+#if 0
+ /*
+ * Dump the device registers.
+ */
+ for (uint16_t uPort = 0x300; uPort < 0x320; uPort ++)
+ LogRel(("SMC: %#06x=%#010x w={%#06x, %#06x}, b={%#04x %#04x %#04x %#04x}\n", uPort,
+ ASMInU32(uPort), ASMInU16(uPort), ASMInU16(uPort + 2),
+ ASMInU8(uPort), ASMInU8(uPort + 1), ASMInU8(uPort +2), ASMInU8(uPort + 3) ));
+#endif
+
+ NOREF(pvUserIgnored);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREGR0,pfnRequest}
+ */
+static DECLCALLBACK(int) devR0SmcReqHandler(PPDMDEVINS pDevIns, uint32_t uReq, uint64_t uArg)
+{
+ PDEVSMC pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSMC);
+ int rc = VERR_INVALID_FUNCTION;
+ RT_NOREF_PV(uArg);
+
+ if (uReq == SMC_CALLR0_READ_OSK)
+ {
+ rc = RTOnce(&g_SmcR0Once, devR0SmcInitOnce, NULL);
+ if ( RT_SUCCESS(rc)
+ && g_fHaveOsk)
+ {
+ AssertCompile(sizeof(g_abOsk0And1) + 1 == sizeof(pThis->szOsk0And1));
+ memcpy(pThis->szOsk0And1, g_abOsk0And1, sizeof(pThis->szOsk0And1) - 1);
+ pThis->szOsk0And1[sizeof(pThis->szOsk0And1) - 1] = '\0';
+ }
+ }
+ return rc;
+}
+
+#endif /* IN_RING0 && (AMD64 || X86) */
+
+#if defined(IN_RING3) && defined(RT_OS_DARWIN)
+
+/**
+ * Preferred method to retrieve the SMC key.
+ *
+ * @param pabKey where to store the key.
+ * @param cbKey size of the buffer.
+ */
+static int getSmcKeyOs(char *pabKey, uint32_t cbKey)
+{
+ /*
+ * Method as described in Amit Singh's article:
+ * http://osxbook.com/book/bonus/chapter7/tpmdrmmyth/
+ */
+ typedef struct
+ {
+ uint32_t key;
+ uint8_t pad0[22];
+ uint32_t datasize;
+ uint8_t pad1[10];
+ uint8_t cmd;
+ uint32_t pad2;
+ uint8_t data[32];
+ } AppleSMCBuffer;
+
+ AssertReturn(cbKey >= 65, VERR_INTERNAL_ERROR);
+
+ io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault,
+ IOServiceMatching("AppleSMC"));
+ if (!service)
+ return VERR_NOT_FOUND;
+
+ io_connect_t port = (io_connect_t)0;
+ kern_return_t kr = IOServiceOpen(service, mach_task_self(), 0, &port);
+ IOObjectRelease(service);
+
+ if (kr != kIOReturnSuccess)
+ return RTErrConvertFromDarwin(kr);
+
+ AppleSMCBuffer inputStruct = { 0, {0}, 32, {0}, 5, };
+ AppleSMCBuffer outputStruct;
+ size_t cbOutputStruct = sizeof(outputStruct);
+
+ for (int i = 0; i < 2; i++)
+ {
+ inputStruct.key = (uint32_t)(i == 0 ? 'OSK0' : 'OSK1');
+ kr = IOConnectCallStructMethod((mach_port_t)port,
+ (uint32_t)2,
+ (const void *)&inputStruct,
+ sizeof(inputStruct),
+ (void *)&outputStruct,
+ &cbOutputStruct);
+ if (kr != kIOReturnSuccess)
+ {
+ IOServiceClose(port);
+ return RTErrConvertFromDarwin(kr);
+ }
+
+ for (int j = 0; j < 32; j++)
+ pabKey[j + i*32] = outputStruct.data[j];
+ }
+
+ IOServiceClose(port);
+
+ pabKey[64] = 0;
+
+ return VINF_SUCCESS;
+}
+
+#endif /* IN_RING3 && RT_OS_DARWIN */
+
+
+/** @callback_method_impl{FNDEVSMCKEYGETTER, OSK0 and OSK1} */
+static DECLCALLBACK(uint8_t) scmKeyGetOSKs(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
+{
+ RT_NOREF1(bCmd);
+ Assert(SMC4CH_EQ(&pKeyDesc->Key, "OSK0") || SMC4CH_EQ(&pKeyDesc->Key, "OSK1"));
+ const char *pszSrc = pThis->szOsk0And1;
+ if (SMC4CH_EQ(&pKeyDesc->Key, "OSK1"))
+ pszSrc += 32;
+ memcpy(pCurKey->Value.ab, pszSrc, 32);
+ return SMC_STATUS_CD_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNDEVSMCKEYGETTER, \#KEY} */
+static DECLCALLBACK(uint8_t) scmKeyGetKeyCount(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
+{
+ RT_NOREF3(pThis, bCmd, pKeyDesc);
+ Assert(pKeyDesc == &g_aSmcKeys[SMC_KEYIDX_FIRST_ENUM]);
+ uint32_t cKeys = RT_ELEMENTS(g_aSmcKeys) - SMC_KEYIDX_FIRST_ENUM;
+ pCurKey->Value.u32 = RT_H2BE_U32(cKeys);
+ return SMC_STATUS_CD_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNDEVSMCKEYGETTER, REV - Source revision.} */
+static DECLCALLBACK(uint8_t) scmKeyGetRevision(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
+{
+ RT_NOREF3(pThis, bCmd, pKeyDesc);
+#ifdef VBOX_WITH_SMC_2_x
+ pCurKey->Value.ab[0] = 0x02;
+ pCurKey->Value.ab[1] = 0x03;
+ pCurKey->Value.ab[2] = 0x0f;
+ pCurKey->Value.ab[3] = 0x00;
+ pCurKey->Value.ab[4] = 0x00;
+ pCurKey->Value.ab[5] = 0x35;
+#else
+ pCurKey->Value.ab[0] = 0x01;
+ pCurKey->Value.ab[1] = 0x25;
+ pCurKey->Value.ab[2] = 0x0f;
+ pCurKey->Value.ab[3] = 0x00;
+ pCurKey->Value.ab[4] = 0x00;
+ pCurKey->Value.ab[5] = 0x04;
+#endif
+ return SMC_STATUS_CD_SUCCESS;
+}
+
+#ifdef VBOX_WITH_SMC_2_x
+
+/** @callback_method_impl{FNDEVSMCKEYGETTER, $Adr - SMC address.} */
+static DECLCALLBACK(uint8_t) scmKeyGetDollarAddress(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
+{
+ RT_NOREF3(pThis, bCmd, pKeyDesc);
+ pCurKey->Value.u32 = RT_H2BE_U32(SMC_PORT_FIRST);
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNDEVSMCKEYGETTER, $Num - Some kind of number.} */
+static DECLCALLBACK(uint8_t) scmKeyGetDollarNumber(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
+{
+ RT_NOREF2(bCmd, pKeyDesc);
+ pCurKey->Value.ab[0] = pThis->bDollaryNumber;
+ return VINF_SUCCESS;
+}
+
+/** @callback_method_impl{FNDEVSMCKEYPUTTER, $Num - Some kind of number.} */
+static DECLCALLBACK(uint8_t) scmKeyPutDollarNumber(PDEVSMC pThis, PCDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
+{
+ RT_NOREF2(bCmd, pKeyDesc);
+ Log(("scmKeyPutDollarNumber: %#x -> %#x\n", pThis->bDollaryNumber, pCurKey->Value.ab[0]));
+ pThis->bDollaryNumber = pCurKey->Value.ab[0];
+ return VINF_SUCCESS;
+}
+
+#endif /* VBOX_WITH_SMC_2_x */
+
+/** @callback_method_impl{FNDEVSMCKEYGETTER, MSSD - Machine Shutdown reason.} */
+static DECLCALLBACK(uint8_t) scmKeyGetShutdownReason(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
+{
+ RT_NOREF2(bCmd, pKeyDesc);
+ pCurKey->Value.ab[0] = pThis->bShutdownReason;
+ return SMC_STATUS_CD_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNDEVSMCKEYPUTTER, MSSD - Machine Shutdown reason.} */
+static DECLCALLBACK(uint8_t) scmKeyPutShutdownReason(PDEVSMC pThis, PCDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
+{
+ RT_NOREF2(bCmd, pKeyDesc);
+ Log(("scmKeyPutShutdownReason: %#x -> %#x\n", pThis->bShutdownReason, pCurKey->Value.ab[0]));
+ pThis->bShutdownReason = pCurKey->Value.ab[0];
+ return SMC_STATUS_CD_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNDEVSMCKEYGETTER, MSSD - Ninja timer action job.} */
+static DECLCALLBACK(uint8_t)
+scmKeyGetNinjaTimerAction(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
+{
+ RT_NOREF2(bCmd, pKeyDesc);
+ pCurKey->Value.ab[0] = pThis->bNinjaActionTimerJob;
+ return SMC_STATUS_CD_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNDEVSMCKEYPUTTER, NATJ - Ninja timer action job.} */
+static DECLCALLBACK(uint8_t)
+scmKeyPutNinjaTimerAction(PDEVSMC pThis, PCDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
+{
+ RT_NOREF2(bCmd, pKeyDesc);
+ Log(("scmKeyPutNinjaTimerAction: %#x -> %#x\n", pThis->bNinjaActionTimerJob, pCurKey->Value.ab[0]));
+ pThis->bNinjaActionTimerJob = pCurKey->Value.ab[0];
+ return SMC_STATUS_CD_SUCCESS;
+}
+
+#ifdef VBOX_WITH_SMC_2_x
+
+/** @callback_method_impl{FNDEVSMCKEYGETTER, Generic one getter.} */
+static DECLCALLBACK(uint8_t) scmKeyGetOne(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
+{
+ RT_NOREF2(pThis, bCmd);
+ memset(&pCurKey->Value.ab[0], 0, pKeyDesc->cbValue);
+ pCurKey->Value.ab[pKeyDesc->cbValue - 1] = 1;
+ return SMC_STATUS_CD_SUCCESS;
+}
+
+#endif /* VBOX_WITH_SMC_2_x */
+
+/** @callback_method_impl{FNDEVSMCKEYGETTER, Generic zero getter.} */
+static DECLCALLBACK(uint8_t) scmKeyGetZero(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
+{
+ RT_NOREF2(pThis, bCmd);
+ memset(&pCurKey->Value.ab[0], 0, pKeyDesc->cbValue);
+ return SMC_STATUS_CD_SUCCESS;
+}
+
+
+/**
+ * Looks up a key and copies its value and attributes into the CurKey.
+ *
+ * @returns Key index on success, UINT32_MAX on failure.
+ * @param uKeyValue The key value (DEVSMC4CHID.u32).
+ */
+static uint32_t smcKeyLookup(uint32_t uKeyValue)
+{
+ uint32_t iKey = RT_ELEMENTS(g_aSmcKeys);
+ while (iKey-- > 0)
+ if (g_aSmcKeys[iKey].Key.u32 == uKeyValue)
+ return iKey;
+ return UINT32_MAX;
+}
+
+
+/**
+ * Looks up a key and copies its value and attributes into the CurKey.
+ *
+ * @returns Apple SMC Status Code.
+ * @param pThis The SMC instance data.
+ */
+static uint8_t smcKeyGetByName(PDEVSMC pThis)
+{
+ uint8_t bRc;
+#ifdef LOG_ENABLED
+ uint32_t const uKeyValueLog = RT_H2LE_U32(pThis->CurKey.Key.u32);
+#endif
+ uint32_t iKey = smcKeyLookup(pThis->CurKey.Key.u32);
+ if (iKey != UINT32_MAX)
+ {
+ if ( g_aSmcKeys[iKey].cbValue == pThis->CurKey.cbValue
+ || !g_aSmcKeys[iKey].cbValue)
+ {
+ pThis->CurKey.Type = g_aSmcKeys[iKey].Type;
+ pThis->CurKey.fAttr = g_aSmcKeys[iKey].fAttr;
+ RT_ZERO(pThis->CurKey.Value);
+ if (g_aSmcKeys[iKey].pfnGet)
+ {
+ bRc = g_aSmcKeys[iKey].pfnGet(pThis, &pThis->CurKey, pThis->bCmd, &g_aSmcKeys[iKey]);
+ if (bRc == SMC_STATUS_CD_SUCCESS)
+ {
+ LogFlow(("smcKeyGetByName: key=%4.4s value=%.*Rhxs\n",
+ &uKeyValueLog, pThis->CurKey.cbValue, &pThis->CurKey.Value));
+ return SMC_STATUS_CD_SUCCESS;
+ }
+
+ Log(("smcKeyGetByName: key=%4.4s getter failed! bRc=%#x\n", &uKeyValueLog, bRc));
+ }
+ else
+ {
+ Log(("smcKeyGetByName: key=%4.4s is not readable!\n", &uKeyValueLog));
+ bRc = SMC_STATUS_CD_KEY_NOT_READABLE;
+ }
+ }
+ else
+ {
+ Log(("smcKeyGetByName: Wrong value size; user=%#x smc=%#x key=%4.4s !\n",
+ pThis->CurKey.cbValue, g_aSmcKeys[iKey].cbValue, &uKeyValueLog));
+ bRc = SMC_STATUS_CD_KEY_SIZE_MISMATCH;
+ }
+ }
+ else
+ {
+ Log(("smcKeyGetByName: Key not found! key=%4.4s size=%#x\n", &uKeyValueLog, pThis->CurKey.cbValue));
+ bRc = SMC_STATUS_CD_KEY_NOT_FOUND;
+ }
+
+ RT_ZERO(pThis->CurKey);
+ return bRc;
+}
+
+
+/**
+ * Looks up a key by index and copies its name (and attributes) into the CurKey.
+ *
+ * @returns Apple SMC Status Code.
+ * @param pThis The SMC instance data.
+ */
+static uint8_t smcKeyGetByIndex(PDEVSMC pThis)
+{
+ uint8_t bRc;
+ uint32_t iKey = RT_BE2H_U32(pThis->CurKey.Key.u32);
+ if (iKey < RT_ELEMENTS(g_aSmcKeys) - SMC_KEYIDX_FIRST_ENUM)
+ {
+ pThis->CurKey.Key = g_aSmcKeys[iKey].Key;
+ pThis->CurKey.Type = g_aSmcKeys[iKey].Type;
+ pThis->CurKey.fAttr = g_aSmcKeys[iKey].fAttr;
+ pThis->CurKey.cbValue = g_aSmcKeys[iKey].cbValue;
+ RT_ZERO(pThis->CurKey.Value);
+ Log(("smcKeyGetByIndex: %#x -> %c%c%c%c\n", iKey,
+ pThis->CurKey.Key.ab[3], pThis->CurKey.Key.ab[2], pThis->CurKey.Key.ab[1], pThis->CurKey.Key.ab[0]));
+ bRc = SMC_STATUS_CD_SUCCESS;
+ }
+ else
+ {
+ Log(("smcKeyGetByIndex: Key out or range: %#x, max %#x\n", iKey, RT_ELEMENTS(g_aSmcKeys) - SMC_KEYIDX_FIRST_ENUM));
+ bRc = SMC_STATUS_CD_KEY_NOT_FOUND;
+ }
+ return bRc;
+}
+
+
+/**
+ * Looks up a key by index and copies its attributes into the CurKey.
+ *
+ * @returns Apple SMC Status Code.
+ * @param pThis The SMC instance data.
+ */
+static uint8_t smcKeyGetAttrByName(PDEVSMC pThis)
+{
+ uint8_t bRc;
+#ifdef LOG_ENABLED
+ uint32_t const uKeyValueLog = RT_H2LE_U32(pThis->CurKey.Key.u32);
+#endif
+ uint32_t iKey = smcKeyLookup(pThis->CurKey.Key.u32);
+ if (iKey != UINT32_MAX)
+ {
+ pThis->CurKey.Type = g_aSmcKeys[iKey].Type;
+ pThis->CurKey.fAttr = g_aSmcKeys[iKey].fAttr;
+ pThis->CurKey.cbValue = g_aSmcKeys[iKey].cbValue;
+ RT_ZERO(pThis->CurKey.Value);
+ if (g_aSmcKeys[iKey].cbValue)
+ bRc = SMC_STATUS_CD_SUCCESS;
+ else
+ bRc = g_aSmcKeys[iKey].pfnGet(pThis, &pThis->CurKey, pThis->bCmd, &g_aSmcKeys[iKey]);
+ if (bRc == SMC_STATUS_CD_SUCCESS)
+ {
+ LogFlow(("smcKeyGetAttrByName: key=%4.4s value=%.*Rhxs\n",
+ &uKeyValueLog, pThis->CurKey.cbValue, &pThis->CurKey.Value));
+ return SMC_STATUS_CD_SUCCESS;
+ }
+
+ Log(("smcKeyGetAttrByName: key=%4.4s getter failed! bRc=%#x\n", &uKeyValueLog, bRc));
+ }
+ else
+ {
+ Log(("smcKeyGetAttrByName: Key not found! key=%4.4s size=%#x\n", &uKeyValueLog, pThis->CurKey.cbValue));
+ bRc = SMC_STATUS_CD_KEY_NOT_FOUND;
+ }
+
+ RT_ZERO(pThis->CurKey);
+ return bRc;
+}
+
+
+static uint8_t smcKeyPutPrepare(PDEVSMC pThis)
+{
+ RT_NOREF1(pThis);
+ return 0;
+}
+
+static uint8_t smcKeyPutValue(PDEVSMC pThis)
+{
+ RT_NOREF1(pThis);
+ return 0;
+}
+
+
+/**
+ * Data register read.
+ *
+ * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
+ * @param uReg The register number.
+ * @param pbValue Where to return the value.
+ */
+static VBOXSTRICTRC smcRegData_r(PDEVSMC pThis, uint8_t uReg, uint8_t *pbValue)
+{
+ RT_NOREF1(uReg);
+ switch (pThis->bCmd)
+ {
+ case SMC_CMD_GET_KEY_VALUE:
+ if ( pThis->u.s.bState == 0x05
+ && pThis->offValue < pThis->CurKey.cbValue)
+ {
+ *pbValue = pThis->CurKey.Value.ab[pThis->offValue];
+ if (++pThis->offValue >= pThis->CurKey.cbValue)
+ pThis->u.s.bState = 0x00;
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
+ }
+ else
+ {
+ Log(("smcRegData_r: Reading too much or at wrong time during SMC_CMD_GET_KEY_INFO! bState=%#x offValue=%#x\n",
+ pThis->u.s.bState, pThis->offValue));
+ pThis->u.s.bState = 0x00;
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA; /** @todo check status code */
+ }
+ break;
+
+ case SMC_CMD_GET_KEY_INFO:
+ if ( pThis->u.s.bState == 0x05
+ && pThis->offValue < 6)
+ {
+ if (pThis->offValue == 0)
+ *pbValue = pThis->CurKey.cbValue;
+ else if (pThis->offValue < 1 + 4)
+ *pbValue = pThis->CurKey.Type.ab[pThis->offValue - 1];
+ else
+ *pbValue = pThis->CurKey.fAttr;
+ if (++pThis->offValue >= 6)
+ pThis->u.s.bState = 0x00;
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
+ }
+ else
+ {
+ Log(("smcRegData_r: Reading too much or at wrong time during SMC_CMD_GET_KEY_INFO! bState=%#x offValue=%#x\n",
+ pThis->u.s.bState, pThis->offValue));
+ pThis->u.s.bState = 0x00;
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA; /** @todo check status code */
+ }
+ break;
+
+ case SMC_CMD_GET_KEY_BY_INDEX:
+ if ( pThis->u.s.bState == 0x05
+ && pThis->offValue < sizeof(pThis->CurKey.Key))
+ {
+ *pbValue = pThis->CurKey.Key.ab[pThis->offValue];
+ if (++pThis->offValue >= sizeof(pThis->CurKey.Key))
+ pThis->u.s.bState = 0x00;
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
+ }
+ else
+ {
+ Log(("smcRegData_r: Reading too much or at wrong time during GET_KEY_BY_INDEX! bState=%#x offValue=%#x\n",
+ pThis->u.s.bState, pThis->offValue));
+ pThis->u.s.bState = 0x00;
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA; /** @todo check status code */
+ }
+ break;
+
+ case SMC_CMD_PUT_KEY:
+ Log(("smcRegData_r: Attempting to read data during PUT_KEY!\n"));
+ *pbValue = 0xff;
+ pThis->u.s.bState = 0;
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA;
+ break;
+
+ default:
+ Log(("smcRegData_r: Unknown command attempts reading data\n"));
+ *pbValue = 0xff;
+ pThis->u.s.bState = 0;
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA;
+ break;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Data register write.
+ *
+ * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
+ * @param uReg The register number.
+ * @param bValue The value being written.
+ */
+static VBOXSTRICTRC smcRegData_w(PDEVSMC pThis, uint8_t uReg, uint8_t bValue)
+{
+ RT_NOREF1(uReg);
+ switch (pThis->bCmd)
+ {
+ /*
+ * Get or put key value.
+ *
+ * 5 bytes written, first 4 is the key the 5th is the value size. In
+ * the case of a put the value bytes are then written, while a get will
+ * read the value bytes.
+ */
+ case SMC_CMD_GET_KEY_VALUE:
+ case SMC_CMD_PUT_KEY:
+ if (pThis->offKey < 4)
+ {
+ /* Key byte. */
+ pThis->CurKey.Key.ab[pThis->offKey++] = bValue;
+ pThis->u.s.bState = 0x04;
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
+ }
+ else if (pThis->offKey == 4)
+ {
+ /* Data length. */
+ pThis->u.s.bState = 0;
+ if (bValue <= sizeof(pThis->CurKey.Value))
+ {
+ pThis->CurKey.cbValue = bValue;
+ pThis->offKey = 5;
+ Assert(pThis->offValue == 0);
+
+ if (pThis->bCmd == SMC_CMD_GET_KEY_VALUE)
+ pThis->u.s.bStatusCode = smcKeyGetByName(pThis);
+ else
+ pThis->u.s.bStatusCode = smcKeyPutPrepare(pThis);
+ if (pThis->u.s.bStatusCode == SMC_STATUS_CD_SUCCESS)
+ pThis->u.s.bState = 0x05;
+ }
+ else
+ {
+ Log(("smcRegData_w: Guest attempts to get/put too many value bytes: %#x (max %#x)!\n",
+ bValue, sizeof(pThis->CurKey.Value)));
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_KEY_SIZE_MISMATCH; /** @todo check this case! */
+ }
+ }
+ else if ( pThis->bCmd == SMC_CMD_PUT_KEY
+ && pThis->offValue < pThis->CurKey.cbValue)
+ {
+ /* More value bytes for put key action. */
+ pThis->CurKey.Value.ab[pThis->offValue++] = bValue;
+ if (pThis->offValue != pThis->CurKey.cbValue)
+ pThis->u.s.bState = 0x05;
+ else
+ {
+ pThis->u.s.bState = 0x00;
+ pThis->u.s.bStatusCode = smcKeyPutValue(pThis);
+ }
+ }
+ else
+ {
+ Log(("smcRegData_w: Writing too much data on %s command!\n", pThis->bCmd == SMC_CMD_PUT_KEY ? "put" : "get"));
+ pThis->u.s.bState = 0x00;
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA;
+ }
+ break;
+
+ /*
+ * Get key info and key by index seems to take action after the last
+ * key char is written. They then both go into a data reading phase.
+ */
+ case SMC_CMD_GET_KEY_INFO:
+ case SMC_CMD_GET_KEY_BY_INDEX:
+ if (pThis->offKey < 4)
+ {
+ pThis->CurKey.Key.ab[pThis->offKey] = bValue;
+ if (++pThis->offKey == 4)
+ {
+ if (pThis->bCmd == SMC_CMD_GET_KEY_BY_INDEX)
+ pThis->u.s.bStatusCode = smcKeyGetByIndex(pThis);
+ else
+ pThis->u.s.bStatusCode = smcKeyGetAttrByName(pThis);
+ pThis->u.s.bState = pThis->u.s.bStatusCode == SMC_STATUS_CD_SUCCESS ? 0x05 : 0x00;
+ }
+ else
+ {
+ pThis->u.s.bState = 0x04;
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
+ }
+ }
+ else
+ {
+ Log(("smcRegData_w: Writing data beyond 5th byte on get %s command!\n",
+ pThis->bCmd == SMC_CMD_GET_KEY_INFO ? "info" : "by index"));
+ pThis->u.s.bState = 0x00;
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA;
+ }
+ break;
+
+ default:
+ Log(("smcRegData_w: Unknown command %#x!\n", bValue));
+ pThis->u.s.bState = 0x00; /** @todo Check statuses with real HW. */
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_BAD_COMMAND;
+ break;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Command register write.
+ *
+ * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
+ * @param uReg The register number.
+ * @param bValue The value being written.
+ */
+static VBOXSTRICTRC smcRegCmd_w(PDEVSMC pThis, uint8_t uReg, uint8_t bValue)
+{
+ LogFlow(("smcRegCmd_w: New command: %#x (old=%#x)\n", bValue, pThis->bCmd)); NOREF(uReg);
+
+ pThis->bCmd = bValue;
+
+ /* Validate the command. */
+ switch (bValue)
+ {
+ case SMC_CMD_GET_KEY_VALUE:
+ case SMC_CMD_PUT_KEY:
+ case SMC_CMD_GET_KEY_BY_INDEX:
+ case SMC_CMD_GET_KEY_INFO:
+ pThis->u.s.bState = 0x0c;
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
+ break;
+
+ default:
+ Log(("SMC: Unknown command %#x!\n", bValue));
+ pThis->u.s.bState = 0x00; /** @todo Check state with real HW. */
+ pThis->u.s.bStatusCode = SMC_STATUS_CD_BAD_COMMAND;
+ break;
+ }
+
+ /* Reset the value/key related state. */
+ pThis->offKey = 0;
+ pThis->offValue = 0;
+ pThis->CurKey.Key.u32 = 0;
+ pThis->CurKey.cbValue = 0;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Generic register write.
+ *
+ * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
+ * @param uReg The register number.
+ * @param bValue The value being written.
+ */
+static VBOXSTRICTRC smcRegGen_w(PDEVSMC pThis, uint8_t uReg, uint8_t bValue)
+{
+ Log(("smcRegGen_w: %#04x: %#x -> %#x (write)\n", uReg, pThis->u.abRegsRW[uReg], bValue));
+ pThis->u.abRegsRW[uReg] = bValue;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Read from register that isn't writable and reads as 0xFF.
+ *
+ * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
+ * @param uReg The register number.
+ * @param pbValue Where to return the value.
+ */
+static VBOXSTRICTRC smcRegGen_r(PDEVSMC pThis, uint8_t uReg, uint8_t *pbValue)
+{
+ Log(("smcRegGen_r: %#04x: %#x (read)\n", uReg, pThis->u.abRegsRW[uReg]));
+ *pbValue = pThis->u.abRegsRW[uReg];
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Write to register that isn't writable and reads as 0xFF.
+ *
+ * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
+ * @param uReg The register number.
+ * @param bValue The value being written.
+ */
+static VBOXSTRICTRC smcRegFF_w(PDEVSMC pThis, uint8_t uReg, uint8_t bValue)
+{
+ RT_NOREF3(pThis, uReg, bValue);
+ Log(("SMC: %#04x: Writing %#x to unknown register!\n", uReg, bValue));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Read from register that isn't writable and reads as 0xFF.
+ *
+ * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
+ * @param uReg The register number.
+ * @param pbValue Where to return the value.
+ */
+static VBOXSTRICTRC smcRegFF_r(PDEVSMC pThis, uint8_t uReg, uint8_t *pbValue)
+{
+ RT_NOREF2(pThis, uReg);
+ Log(("SMC: %#04x: Reading from unknown register!\n", uReg));
+ *pbValue = 0xff;
+ return VINF_SUCCESS;
+}
+
+
+
+/**
+ * SMC register handlers (indexed by relative I/O port).
+ *
+ * The device seems to be all byte registers and will split wider
+ * accesses between registers like if it was MMIO. To better illustrate it
+ * here is the output of the code in devR0SmcInitOnce on a MacPro3,1:
+ * @verbatim
+ * SMC: 0x0300=0xffffff63 w={0xff63, 0xffff}, b={0x63 0xff 0xff 0xff}
+ * SMC: 0x0301=0x0cffffff w={0xffff, 0x0cff}, b={0xff 0xff 0xff 0x0c}
+ * SMC: 0x0302=0xff0cffff w={0xffff, 0xff0c}, b={0xff 0xff 0x0c 0xff}
+ * SMC: 0x0303=0xffff0cff w={0x0cff, 0xffff}, b={0xff 0x0c 0xff 0xff}
+ * SMC: 0x0304=0xffffff0c w={0xff0c, 0xffff}, b={0x0c 0xff 0xff 0xff}
+ * SMC: 0x0305=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
+ * SMC: 0x0306=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
+ * SMC: 0x0307=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
+ * SMC: 0x0308=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
+ * SMC: 0x0309=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
+ * SMC: 0x030a=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
+ * SMC: 0x030b=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
+ * SMC: 0x030c=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
+ * SMC: 0x030d=0x00ffffff w={0xffff, 0x00ff}, b={0xff 0xff 0xff 0x00}
+ * SMC: 0x030e=0x0000ffff w={0xffff, 0x0000}, b={0xff 0xff 0x00 0x00}
+ * SMC: 0x030f=0x000000ff w={0x00ff, 0x0000}, b={0xff 0x00 0x00 0x00}
+ * SMC: 0x0310=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
+ * SMC: 0x0311=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
+ * SMC: 0x0312=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
+ * SMC: 0x0313=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
+ * SMC: 0x0314=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
+ * SMC: 0x0315=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
+ * SMC: 0x0316=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
+ * SMC: 0x0317=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
+ * SMC: 0x0318=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
+ * SMC: 0x0319=0xbe000000 w={0x0000, 0xbe00}, b={0x00 0x00 0x00 0xbe}
+ * SMC: 0x031a=0xbabe0000 w={0x0000, 0xbabe}, b={0x00 0x00 0xbe 0xba}
+ * SMC: 0x031b=0x00babe00 w={0xbe00, 0x00ba}, b={0x00 0xbe 0xba 0x00}
+ * SMC: 0x031c=0xbe00babe w={0xbabe, 0xbe00}, b={0xbe 0xba 0x00 0xbe}
+ * SMC: 0x031d=0xffbe00ba w={0x00ba, 0xffbe}, b={0xba 0x00 0xbe 0xff}
+ * SMC: 0x031e=0xffffbe00 w={0xbe00, 0xffff}, b={0x00 0xbe 0xff 0xff}
+ * SMC: 0x031f=0xffffffbe w={0xffbe, 0xffff}, b={0xbe 0xff 0xff 0xff}
+ * @endverbatim
+ *
+ * The last dword is writable (0xbeXXbabe) where in the register at 0x1e is some
+ * kind of status register for qualifying search failures and the like and will
+ * be cleared under certain conditions. The whole dword can be written and read
+ * back unchanged, according to my experiments. The 0x00 and 0x04 registers
+ * does not read back what is written.
+ *
+ * My guess is that the 0xff values indicates ports that are not writable and
+ * hardwired to 0xff, while the other values indicates ports that can be written
+ * to and normally read back as written. I'm not going to push my luck too far
+ * wrt to exact behavior until I see the guest using the registers.
+ */
+static const struct
+{
+ VBOXSTRICTRC (*pfnWrite)(PDEVSMC pThis, uint8_t uReg, uint8_t bValue);
+ VBOXSTRICTRC (*pfnRead)(PDEVSMC pThis, uint8_t uReg, uint8_t *pbValue);
+} g_aSmcRegs[SMC_REG_COUNT] =
+{
+ /* [0x00] = */ { smcRegData_w, smcRegData_r },
+ /* [0x01] = */ { smcRegFF_w, smcRegFF_r },
+ /* [0x02] = */ { smcRegFF_w, smcRegFF_r },
+ /* [0x03] = */ { smcRegFF_w, smcRegFF_r },
+ /* [0x04] = */ { smcRegCmd_w, smcRegGen_r },
+ /* [0x05] = */ { smcRegFF_w, smcRegFF_r },
+ /* [0x06] = */ { smcRegFF_w, smcRegFF_r },
+ /* [0x07] = */ { smcRegFF_w, smcRegFF_r },
+ /* [0x08] = */ { smcRegFF_w, smcRegFF_r },
+ /* [0x09] = */ { smcRegFF_w, smcRegFF_r },
+ /* [0x0a] = */ { smcRegFF_w, smcRegFF_r },
+ /* [0x0b] = */ { smcRegFF_w, smcRegFF_r },
+ /* [0x0c] = */ { smcRegFF_w, smcRegFF_r },
+ /* [0x0d] = */ { smcRegFF_w, smcRegFF_r },
+ /* [0x0e] = */ { smcRegFF_w, smcRegFF_r },
+ /* [0x0f] = */ { smcRegFF_w, smcRegFF_r },
+ /* [0x10] = */ { smcRegGen_w, smcRegGen_r },
+ /* [0x11] = */ { smcRegGen_w, smcRegGen_r },
+ /* [0x12] = */ { smcRegGen_w, smcRegGen_r },
+ /* [0x13] = */ { smcRegGen_w, smcRegGen_r },
+ /* [0x14] = */ { smcRegGen_w, smcRegGen_r },
+ /* [0x15] = */ { smcRegGen_w, smcRegGen_r },
+ /* [0x16] = */ { smcRegGen_w, smcRegGen_r },
+ /* [0x17] = */ { smcRegGen_w, smcRegGen_r },
+ /* [0x18] = */ { smcRegGen_w, smcRegGen_r },
+ /* [0x19] = */ { smcRegGen_w, smcRegGen_r },
+ /* [0x1a] = */ { smcRegGen_w, smcRegGen_r },
+ /* [0x1b] = */ { smcRegGen_w, smcRegGen_r },
+ /* [0x1c] = */ { smcRegGen_w, smcRegGen_r },
+ /* [0x1d] = */ { smcRegGen_w, smcRegGen_r },
+ /* [0x1e] = */ { smcRegGen_w, smcRegGen_r },
+ /* [0x1f] = */ { smcRegGen_w, smcRegGen_r },
+};
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTNEWOUT}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) smcIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
+{
+ RT_NOREF1(pvUser);
+#ifndef IN_RING3
+ if (cb > 1)
+ {
+ Log3(("smcIoPortWrite: %#04x write access: %#x (LB %u) -> ring-3\n", offPort, u32, cb));
+ return VINF_IOM_R3_IOPORT_WRITE;
+ }
+#endif
+#ifdef LOG_ENABLED
+ RTIOPORT const offPortLog = offPort;
+ unsigned const cbLog = cb;
+#endif
+
+ /*
+ * The first register, usually only one is accessed.
+ */
+ PDEVSMC pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSMC);
+ AssertReturn(offPort < RT_ELEMENTS(g_aSmcRegs), VERR_INTERNAL_ERROR_3); /* impossible*/
+ VBOXSTRICTRC rc = g_aSmcRegs[offPort].pfnWrite(pThis, offPort, u32);
+
+ /*
+ * On the off chance that multiple registers are being read.
+ */
+ if (cb > 1)
+ {
+ while (cb > 1 && offPort < SMC_REG_COUNT - 1)
+ {
+ cb--;
+ offPort++;
+ u32 >>= 8;
+ VBOXSTRICTRC rc2 = g_aSmcRegs[offPort].pfnWrite(pThis, offPort, u32);
+ if (rc2 != VINF_SUCCESS)
+ {
+ if ( rc == VINF_SUCCESS
+ || (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ || (rc2 < rc && RT_SUCCESS(rc2) && RT_SUCCESS(rc)))
+ rc = rc2;
+ }
+ }
+ }
+
+ LogFlow(("smcIoPortWrite: %#04x write access: %#x (LB %u) rc=%Rrc\n", offPortLog, u32, cbLog, VBOXSTRICTRC_VAL(rc) ));
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTNEWIN}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) smcIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
+{
+ RT_NOREF1(pvUser);
+#ifndef IN_RING3
+ if (cb > 1)
+ return VINF_IOM_R3_IOPORT_READ;
+#endif
+
+ PDEVSMC pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSMC);
+#ifdef LOG_ENABLED
+ RTIOPORT const offPortLog = offPort;
+ unsigned const cbLog = cb;
+#endif
+
+ /*
+ * The first register, usually only one is accessed.
+ */
+ AssertReturn(offPort < RT_ELEMENTS(g_aSmcRegs), VERR_INTERNAL_ERROR_3); /* impossible*/
+ Log2(("smcIoPortRead: %#04x read access: LB %u\n", offPort, cb));
+ uint8_t bValue = 0xff;
+ VBOXSTRICTRC rc = g_aSmcRegs[offPort].pfnRead(pThis, offPort, &bValue);
+ *pu32 = bValue;
+
+ /*
+ * On the off chance that multiple registers are being read.
+ */
+ if (cb > 1)
+ {
+ do
+ {
+ cb--;
+ offPort++;
+ bValue = 0xff;
+ if (offPort < SMC_REG_COUNT)
+ {
+ VBOXSTRICTRC rc2 = g_aSmcRegs[offPort].pfnRead(pThis, offPort, &bValue);
+ if (rc2 != VINF_SUCCESS)
+ {
+ if ( rc == VINF_SUCCESS
+ || (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ || (rc2 < rc && RT_SUCCESS(rc2) && RT_SUCCESS(rc)))
+ rc = rc2;
+ }
+ }
+ *pu32 |= (uint32_t)bValue << ((4 - cb) * 8);
+ } while (cb > 1);
+ }
+ LogFlow(("smcIoPortRead: %#04x read access: %#x (LB %u) rc=%Rrc\n", offPortLog, *pu32, cbLog, VBOXSTRICTRC_VAL(rc)));
+ return rc;
+}
+
+#ifdef IN_RING3
+
+/** @callback_method_impl{FNSSMDEVSAVEEXEC} */
+static DECLCALLBACK(int) smcR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PDEVSMC pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSMC);
+ RT_NOREF2(pSSM, pThis);
+
+ /** @todo */
+
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNSSMDEVLOADEXEC} */
+static DECLCALLBACK(int) smcR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PDEVSMC pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSMC);
+ Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
+ RT_NOREF2(pSSM, pThis);
+
+ /* Fend off unsupported versions. */
+ if ( uVersion != SMC_SAVED_STATE_VERSION
+#if SMC_SAVED_STATE_VERSION != SMC_SAVED_STATE_VERSION_BAKA
+ && uVersion != SMC_SAVED_STATE_VERSION_BAKA
+#endif
+ && uVersion != SMC_SAVED_STATE_VERSION_BAKA + 1)
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+
+ /*
+ * Do the actual restoring.
+ */
+ if (uVersion == SMC_SAVED_STATE_VERSION)
+ {
+ /** @todo */
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) smcR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PDEVSMC pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSMC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ Assert(iInstance == 0); RT_NOREF1(iInstance);
+
+ /*
+ * Init the data.
+ */
+ pThis->bDollaryNumber = 1;
+ pThis->bShutdownReason = 3; /* STOP_CAUSE_POWERKEY_GOOD_CODE */
+
+ /*
+ * Validate configuration.
+ */
+ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DeviceKey|GetKeyFromRealSMC", "");
+
+ /*
+ * Read configuration.
+ */
+
+ /* The DeviceKey sets OSK0 and OSK1. */
+ int rc = pHlp->pfnCFGMQueryStringDef(pCfg, "DeviceKey", pThis->szOsk0And1, sizeof(pThis->szOsk0And1), "");
+ if (RT_FAILURE(rc))
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("Configuration error: Querying \"DeviceKey\" as a string failed"));
+
+ /* Query the key from the OS / real hardware if asked to do so. */
+ bool fGetKeyFromRealSMC;
+ rc = pHlp->pfnCFGMQueryBoolDef(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)
+ {
+# ifdef RT_OS_DARWIN
+ rc = getSmcKeyOs(pThis->szOsk0And1, sizeof(pThis->szOsk0And1));
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("SMC: Retrieving the SMC key from the OS failed (%Rrc), trying to read it from hardware\n", rc));
+# endif
+ rc = PDMDevHlpCallR0(pDevIns, SMC_CALLR0_READ_OSK, 0 /*uArg*/);
+ if (RT_SUCCESS(rc))
+ LogRel(("SMC: Successfully retrieved the SMC key from hardware\n"));
+ else
+ LogRel(("SMC: Retrieving the SMC key from hardware failed(%Rrc)\n", rc));
+# ifdef RT_OS_DARWIN
+ }
+ else
+ LogRel(("SMC: Successfully retrieved the SMC key from the OS\n"));
+# endif
+ if (RT_FAILURE(rc))
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("Failed to query SMC value from the host"));
+ }
+
+ /*
+ * Register I/O Ports
+ */
+ rc = PDMDevHlpIoPortCreateAndMap(pDevIns, SMC_PORT_FIRST, SMC_REG_COUNT, smcIoPortWrite, smcIoPortRead,
+ "SMC data port", NULL, &pThis->hIoPorts);
+ AssertRCReturn(rc, rc);
+
+ /** @todo Newer versions (2.03) have an MMIO mapping as well (ACPI). */
+
+
+ /*
+ * Saved state.
+ */
+ rc = PDMDevHlpSSMRegister(pDevIns, SMC_SAVED_STATE_VERSION, sizeof(*pThis), smcR3SaveExec, smcR3LoadExec);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ return VINF_SUCCESS;
+}
+
+#else /* !IN_RING3 */
+
+/**
+ * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
+ */
+static DECLCALLBACK(int) smcRZConstruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PDEVSMC pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSMC);
+
+ int rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPorts, smcIoPortWrite, smcIoPortRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+ RT_NOREF(pThis);
+
+ return VINF_SUCCESS;
+}
+
+#endif /* !IN_RING3 */
+
+
+/**
+ * The device registration structure.
+ */
+const PDMDEVREG g_DeviceSmc =
+{
+ /* .u32Version = */ PDM_DEVREG_VERSION,
+ /* .uReserved0 = */ 0,
+ /* .szName = */ "smc",
+ /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
+ /* .fClass = */ PDM_DEVREG_CLASS_ARCH,
+ /* .cMaxInstances = */ 1,
+ /* .uSharedVersion = */ 42,
+ /* .cbInstanceShared = */ sizeof(DEVSMC),
+ /* .cbInstanceCC = */ 0,
+ /* .cbInstanceRC = */ 0,
+ /* .cMaxPciDevices = */ 0,
+ /* .cMaxMsixVectors = */ 0,
+ /* .pszDescription = */ "Apple System Management Controller",
+#if defined(IN_RING3)
+ /* .pszRCMod = */ "VBoxDDRC.rc",
+ /* .pszR0Mod = */ "VBoxDDR0.r0",
+ /* .pfnConstruct = */ smcR3Construct,
+ /* .pfnDestruct = */ NULL,
+ /* .pfnRelocate = */ NULL,
+ /* .pfnMemSetup = */ NULL,
+ /* .pfnPowerOn = */ NULL,
+ /* .pfnReset = */ NULL,
+ /* .pfnSuspend = */ NULL,
+ /* .pfnResume = */ NULL,
+ /* .pfnAttach = */ NULL,
+ /* .pfnDetach = */ NULL,
+ /* .pfnQueryInterface = */ NULL,
+ /* .pfnInitComplete = */ NULL,
+ /* .pfnPowerOff = */ NULL,
+ /* .pfnSoftReset = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RING0)
+ /* .pfnEarlyConstruct = */ NULL,
+ /* .pfnConstruct = */ smcRZConstruct,
+ /* .pfnDestruct = */ NULL,
+ /* .pfnFinalDestruct = */ NULL,
+# if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ /* .pfnRequest = */ devR0SmcReqHandler,
+# else
+ /* .pfnRequest = */ NULL,
+# endif
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RC)
+ /* .pfnConstruct = */ smcRZConstruct,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#else
+# error "Not in IN_RING3, IN_RING0 or IN_RC!"
+#endif
+ /* .u32VersionEnd = */ PDM_DEVREG_VERSION
+};
+
+#endif /* VBOX_DEVICE_STRUCT_TESTCASE */