VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/DevSmc.cpp@ 69304

Last change on this file since 69304 was 64805, checked in by vboxsync, 8 years ago

Comment typo.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.9 KB
Line 
1/* $Id: DevSmc.cpp 64805 2016-12-08 08:59:14Z vboxsync $ */
2/** @file
3 * DevSmc - Apple System Management Controller.
4 *
5 * The SMC is controlling power, fans, take measurements (voltage, temperature,
6 * fan speed, ++), and lock Mac OS X to Apple hardware. For more details see:
7 * - http://en.wikipedia.org/wiki/System_Management_Controller
8 * - http://www.parhelia.ch/blog/statics/k3_keys.html
9 * - http://www.nosuchcon.org/talks/D1_02_Alex_Ninjas_and_Harry_Potter.pdf
10 */
11
12/*
13 * Copyright (C) 2013-2016 Oracle Corporation
14 *
15 * This file is part of VirtualBox Open Source Edition (OSE), as
16 * available from http://www.virtualbox.org. This file is free software;
17 * you can redistribute it and/or modify it under the terms of the GNU
18 * General Public License (GPL) as published by the Free Software
19 * Foundation, in version 2 as it comes in the "COPYING" file of the
20 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
21 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
22 */
23
24
25/*********************************************************************************************************************************
26* Header Files *
27*********************************************************************************************************************************/
28#define LOG_GROUP LOG_GROUP_DEV_SMC
29#include <VBox/vmm/pdmdev.h>
30#include <VBox/log.h>
31#include <VBox/err.h>
32#include <iprt/assert.h>
33#include <iprt/string.h>
34#ifdef IN_RING0
35# include <iprt/asm-amd64-x86.h>
36# include <iprt/once.h>
37#endif
38#if defined(RT_OS_DARWIN) && defined(IN_RING3)
39# include "IOKit/IOKitLib.h"
40#endif
41
42#include "VBoxDD.h"
43
44
45/*********************************************************************************************************************************
46* Defined Constants And Macros *
47*********************************************************************************************************************************/
48/** The current version of the saved state. */
49#define SMC_SAVED_STATE_VERSION 1 /** @todo later 2 */
50/** Empty saved state version. */
51#define SMC_SAVED_STATE_VERSION_BAKA 1
52
53/** The ring-0 operation number that attempts to get OSK0 and OSK1 from the real
54 * SMC. */
55#define SMC_CALLR0_READ_OSK 1
56
57
58/** @name Apple SMC port and register definitions.
59 * @{ */
60
61/** The first Apple SMC port. */
62#define SMC_PORT_FIRST 0x0300
63/** The number of registers (also ports). */
64#define SMC_REG_COUNT 0x0020
65
66/** The data register. */
67#define SMC_REG_DATA 0x00
68#define SMC_PORT_DATA (SMC_PORT_FIRST + SMC_REG_DATA)
69
70/** The command register. */
71#define SMC_REG_CMD 0x04
72#define SMC_PORT_CMD (SMC_PORT_FIRST + SMC_REG_CMD)
73
74/** Status code register. */
75#define SMC_REG_STATUS_CODE 0x1e
76#define SMC_PORT_STATUS_CODE (SMC_PORT_FIRST + SMC_REG_STATUS_CODE)
77/** @} */
78
79/** @name Apple SMC Commands.
80 * @{ */
81#define SMC_CMD_GET_KEY_VALUE 0x10
82#define SMC_CMD_PUT_KEY 0x11
83#define SMC_CMD_GET_KEY_BY_INDEX 0x12
84#define SMC_CMD_GET_KEY_INFO 0x13
85/** @} */
86
87/** @name Apple SMC Status Codes.
88 * @{ */
89#define SMC_STATUS_CD_SUCCESS UINT8_C(0x00)
90#define SMC_STATUS_CD_COMM_COLLISION UINT8_C(0x80)
91#define SMC_STATUS_CD_SPURIOUS_DATA UINT8_C(0x81)
92#define SMC_STATUS_CD_BAD_COMMAND UINT8_C(0x82)
93#define SMC_STATUS_CD_BAD_PARAMETER UINT8_C(0x83)
94#define SMC_STATUS_CD_KEY_NOT_FOUND UINT8_C(0x84)
95#define SMC_STATUS_CD_KEY_NOT_READABLE UINT8_C(0x85)
96#define SMC_STATUS_CD_KEY_NOT_WRITABLE UINT8_C(0x86)
97#define SMC_STATUS_CD_KEY_SIZE_MISMATCH UINT8_C(0x87)
98#define SMC_STATUS_CD_FRAMING_ERROR UINT8_C(0x88)
99#define SMC_STATUS_CD_BAD_ARGUMENT_ERROR UINT8_C(0x89)
100#define SMC_STATUS_CD_TIMEOUT_ERROR UINT8_C(0xb7)
101#define SMC_STATUS_CD_KEY_INDEX_RANGE_ERROR UINT8_C(0xb8)
102#define SMC_STATUS_CD_BAD_FUNC_PARAMETER UINT8_C(0xc0)
103#define SMC_STATUS_CD_EVENT_BUFF_WRONG_ORDER UINT8_C(0x??)
104#define SMC_STATUS_CD_EVENT_BUFF_READ_ERROR UINT8_C(0x??)
105#define SMC_STATUS_CD_DEVICE_ACCESS_ERROR UINT8_C(0xc7)
106#define SMC_STATUS_CD_UNSUPPORTED_FEATURE UINT8_C(0xcb)
107#define SMC_STATUS_CD_SMB_ACCESS_ERROR UINT8_C(0xcc)
108/** @} */
109
110/** @name Apple SMC Key Attributes.
111 * @{ */
112#define SMC_KEY_ATTR_PRIVATE UINT8_C(0x01)
113#define SMC_KEY_ATTR_UKN_0x02 UINT8_C(0x02)
114#define SMC_KEY_ATTR_UKN_0x04 UINT8_C(0x04)
115#define SMC_KEY_ATTR_CONST UINT8_C(0x08)
116#define SMC_KEY_ATTR_FUNCTION UINT8_C(0x10)
117#define SMC_KEY_ATTR_UKN_0x20 UINT8_C(0x20)
118#define SMC_KEY_ATTR_WRITE UINT8_C(0x40)
119#define SMC_KEY_ATTR_READ UINT8_C(0x80)
120/** @} */
121
122
123/** The index of the first enumerable key in g_aSmcKeys. */
124#define SMC_KEYIDX_FIRST_ENUM 2
125
126/** Macro for emitting a static DEVSMC4CHID initializer. */
127#define SMC4CH(a_sz4) { { a_sz4[0], a_sz4[1], a_sz4[2], a_sz4[3] } }
128
129/**
130 * Macro for comparing DEVSMC4CHID with a string value.
131 * @returns true if equal, false if not.
132 */
133#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]) )
134
135/** Indicates the we want a 2.x SMC. */
136#define VBOX_WITH_SMC_2_x
137
138
139/*********************************************************************************************************************************
140* Structures and Typedefs *
141*********************************************************************************************************************************/
142
143/**
144 * 4 char identifier
145 */
146typedef union DEVSMC4CHID
147{
148 /** Byte view. */
149 uint8_t ab[4];
150 /** 32-bit unsigned integer view. */
151 uint32_t u32;
152} DEVSMC4CHID;
153
154
155/**
156 * Current key data area for communicating with the guest.
157 */
158typedef struct DEVSMCCURKEY
159{
160 /** The key. */
161 DEVSMC4CHID Key;
162 /** The data type. */
163 DEVSMC4CHID Type;
164 /** Key attributes. */
165 uint8_t fAttr;
166 /** The value length. */
167 uint8_t cbValue;
168 uint8_t abAlignment[2];
169 /**
170 * The value union. 32 bytes is probably sufficient here, but we provide a
171 * little more room since it doesn't cost us anything. */
172 union
173 {
174 /** Byte view. */
175 uint8_t ab[128];
176 /** 16-bit view. */
177 uint16_t u16;
178 /** 32-bit view. */
179 uint32_t u32;
180 } Value;
181} DEVSMCCURKEY;
182AssertCompileSize(DEVSMCCURKEY, 128+12);
183/** Pointer to the current key buffer. */
184typedef DEVSMCCURKEY *PDEVSMCCURKEY;
185/** Const pointer to the current key buffer. */
186typedef DEVSMCCURKEY const *PCDEVSMCCURKEY;
187
188
189/**
190 * The device
191 */
192typedef struct DEVSMC
193{
194 /** The current command (SMC_PORT_CMD write). */
195 uint8_t bCmd;
196 /** Current key offset. */
197 uint8_t offKey;
198 /** Current value offset. */
199 uint8_t offValue;
200 /** Number of keys in the aKeys array. */
201 uint8_t cKeys;
202
203 /** The current key data the user is accessing. */
204 DEVSMCCURKEY CurKey;
205
206 /**
207 * Generic read/write register values.
208 *
209 * The DATA register entry is not used at all. The CMD register entry contains
210 * the state value.
211 */
212 union
213 {
214 /** Index register view. */
215 uint8_t abRegsRW[SMC_REG_COUNT];
216 /** Named register view. */
217 struct
218 {
219 uint8_t abUnknown0[0x04];
220 /** The current state (SMC_PORT_CMD read). */
221 uint8_t bState;
222 uint8_t abUnknown1[0x1e - 0x05];
223 /** The current status code (SMC_PORT_STATUS_CODE). */
224 uint8_t bStatusCode;
225 uint8_t abUnknown2[1];
226 } s;
227 } u;
228
229 /** @name Key data.
230 * @{ */
231 /** OSK0 and OSK1. */
232 char szOsk0And1[64+1];
233 /** $Num - unknown function. */
234 uint8_t bDollaryNumber;
235 /** MSSD - shutdown reason. */
236 uint8_t bShutdownReason;
237 /** NATJ - Ninja action timer job. */
238 uint8_t bNinjaActionTimerJob;
239 /** @} */
240} DEVSMC;
241#ifndef _MSC_VER
242AssertCompileMembersAtSameOffset(DEVSMC, u.abRegsRW[SMC_REG_CMD], DEVSMC, u.s.bState);
243AssertCompileMembersAtSameOffset(DEVSMC, u.abRegsRW[SMC_REG_STATUS_CODE], DEVSMC, u.s.bStatusCode);
244#endif
245
246/** Pointer to the SMC state. */
247typedef DEVSMC *PDEVSMC;
248
249#ifndef VBOX_DEVICE_STRUCT_TESTCASE
250
251
252/**
253 * Method for retriving the key value and/or optionally also attributes.
254 *
255 * @returns Apple SMC Status Code.
256 * @param pThis The SMC instance data.
257 * @param pCurKey The current key structure (input / output).
258 * @param bCmd The current command (mostly for getters that also
259 * provides attributes or type info).
260 * @param pKeyDesc Pointer to the key descriptor so that the getter can
261 * service more than once key.
262 */
263typedef DECLCALLBACK(uint8_t) DEVSMCKEYGETTER(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd,
264 struct DEVSMCKEYDESC const *pKeyDesc);
265
266/**
267 * Method for setting the key value.
268 *
269 * @returns Apple SMC Status Code.
270 * @param pThis The SMC instance data.
271 * @param pCurKey The current key structure (input / output).
272 * @param bCmd The current command (currently not relevant).
273 * @param pKeyDesc Pointer to the key descriptor so that the getter can
274 * service more than once key.
275 */
276typedef DECLCALLBACK(uint8_t) DEVSMCKEYPUTTER(PDEVSMC pThis, PCDEVSMCCURKEY pCurKey, uint8_t bCmd,
277 struct DEVSMCKEYDESC const *pKeyDesc);
278
279/**
280 * Key descriptor.
281 */
282typedef struct DEVSMCKEYDESC
283{
284 /** The key 4 character identifier. */
285 DEVSMC4CHID Key;
286 /** Type 4 character identifier. 0 means the getter will set it dynamically. */
287 DEVSMC4CHID Type;
288 /** Getter method, see DEVSMCKEYPUTTER. */
289 DEVSMCKEYGETTER *pfnGet;
290 /** Putter method, see DEVSMCKEYPUTTER. */
291 DEVSMCKEYPUTTER *pfnPut;
292 /** The keyvalue size. If 0 the pfnGet/pfnPut will define/check the size. */
293 uint8_t cbValue;
294 /** Attributes. 0 means the getter will set it dynamically. */
295 uint8_t fAttr;
296} DEVSMCKEYDESC;
297/** Pointer to a constant SMC key descriptor. */
298typedef DEVSMCKEYDESC const *PCDEVSMCKEYDESC;
299
300
301/*********************************************************************************************************************************
302* Internal Functions *
303*********************************************************************************************************************************/
304#ifdef IN_RING3
305static DEVSMCKEYGETTER scmKeyGetOSKs;
306static DEVSMCKEYGETTER scmKeyGetKeyCount;
307static DEVSMCKEYGETTER scmKeyGetRevision;
308# ifdef VBOX_WITH_SMC_2_x
309static DEVSMCKEYGETTER scmKeyGetDollarAddress;
310static DEVSMCKEYGETTER scmKeyGetDollarNumber;
311static DEVSMCKEYPUTTER scmKeyPutDollarNumber;
312# endif
313static DEVSMCKEYGETTER scmKeyGetShutdownReason;
314static DEVSMCKEYPUTTER scmKeyPutShutdownReason;
315static DEVSMCKEYGETTER scmKeyGetNinjaTimerAction;
316static DEVSMCKEYPUTTER scmKeyPutNinjaTimerAction;
317static DEVSMCKEYGETTER scmKeyGetOne;
318static DEVSMCKEYGETTER scmKeyGetZero;
319#endif
320
321
322/*********************************************************************************************************************************
323* Global Variables *
324*********************************************************************************************************************************/
325#ifdef IN_RING3
326/**
327 * Apple SMC key descriptor table.
328 */
329static const DEVSMCKEYDESC g_aSmcKeys[] =
330{
331 /* Non-enum keys first. */
332 { SMC4CH("OSK0"), SMC4CH("ch8*"), scmKeyGetOSKs, NULL, 32, SMC_KEY_ATTR_READ | SMC_KEY_ATTR_FUNCTION },
333 { SMC4CH("OSK1"), SMC4CH("ch8*"), scmKeyGetOSKs, NULL, 32, SMC_KEY_ATTR_READ | SMC_KEY_ATTR_FUNCTION },
334
335 /* The first enum key is the #KEY value. */
336 { SMC4CH("#KEY"), SMC4CH("ui32"), scmKeyGetKeyCount, NULL, 4, SMC_KEY_ATTR_READ },
337# ifdef VBOX_WITH_SMC_2_x
338 { SMC4CH("$Adr"), SMC4CH("ui32"), scmKeyGetDollarAddress, NULL, 4, SMC_KEY_ATTR_READ },
339 { SMC4CH("$Num"), SMC4CH("ui8 "), scmKeyGetDollarNumber, scmKeyPutDollarNumber, 1, SMC_KEY_ATTR_READ | SMC_KEY_ATTR_WRITE | SMC_KEY_ATTR_PRIVATE },
340 { SMC4CH("BEMB"), SMC4CH("flag"), scmKeyGetOne, NULL, 1, SMC_KEY_ATTR_READ },
341# else
342 { SMC4CH("LSOF"), SMC4CH("flag"), scmKeyGetZero, NULL, 1, SMC_KEY_ATTR_READ },
343# endif
344 { SMC4CH("MSSD"), SMC4CH("si8 "), scmKeyGetShutdownReason, scmKeyPutShutdownReason, 1, SMC_KEY_ATTR_READ | SMC_KEY_ATTR_WRITE | SMC_KEY_ATTR_PRIVATE },
345 /* MSDS is not present on MacPro3,1 nor MacBookPro10,1, so returning not found is fine. */
346# ifdef VBOX_WITH_SMC_2_x
347 { SMC4CH("MSTf"), SMC4CH("ui8 "), scmKeyGetZero, NULL, 1, SMC_KEY_ATTR_READ },
348# endif
349 { SMC4CH("NATJ"), SMC4CH("ui8 "), scmKeyGetNinjaTimerAction, scmKeyPutNinjaTimerAction, 1, SMC_KEY_ATTR_READ | SMC_KEY_ATTR_WRITE | SMC_KEY_ATTR_PRIVATE },
350 { SMC4CH("REV "), SMC4CH("{rev"), scmKeyGetRevision, NULL, 6, SMC_KEY_ATTR_READ },
351/** @todo MSSP, NTOK and more. */
352};
353#endif
354
355#ifdef IN_RING0
356/** Do once for the SMC ring-0 static data (g_abOsk0And1, g_fHaveOsk). */
357static RTONCE g_SmcR0Once = RTONCE_INITIALIZER;
358/** Indicates whether we've successfully queried the OSK* keys. */
359static bool g_fHaveOsk = false;
360/** The OSK0 and OSK1 values. */
361static uint8_t g_abOsk0And1[32+32];
362
363
364/**
365 * Waits for the specified state on the host SMC.
366 *
367 * @returns success indicator.
368 * @param bState The desired state.
369 * @param pszWhat What we're currently doing. For the log.
370 */
371static bool devR0SmcWaitHostState(uint8_t bState, const char *pszWhat)
372{
373 uint8_t bCurState = 0; /* (MSC is potentially uninitialized) */
374 for (uint32_t cMsSleep = 1; cMsSleep <= 64; cMsSleep <<= 1)
375 {
376 RTThreadSleep(cMsSleep);
377 bCurState = ASMInU16(SMC_PORT_CMD);
378 if ((bCurState & 0xf) == bState)
379 return true;
380 }
381
382 LogRel(("devR0Smc: %s: bCurState=%#x, wanted %#x.\n", pszWhat, bCurState, bState));
383#if 0
384 uint8_t bCurStatus2 = ASMInU8(SMC_PORT_STATUS_CODE);
385 uint8_t bCurStatus3 = ASMInU8(SMC_PORT_STATUS_CODE);
386 uint16_t wCurStatus3 = ASMInU16(SMC_PORT_STATUS_CODE);
387 uint32_t dwCurStatus3 = ASMInU32(SMC_PORT_STATUS_CODE);
388 LogRel(("SMC: status2=%#x status3=%#x w=%#x dw=%#x\n", bCurStatus2, bCurStatus3, wCurStatus3, dwCurStatus3));
389#endif
390 return false;
391}
392
393
394/**
395 * Reads a key by name from the host SMC.
396 *
397 * @returns success indicator.
398 * @param pszName The key name, must be exactly 4 chars long.
399 * @param pbBuf The output buffer.
400 * @param cbBuf The buffer size. Max 32 bytes.
401 */
402static bool devR0SmcQueryHostKey(const char *pszName, uint8_t *pbBuf, size_t cbBuf)
403{
404 Assert(strlen(pszName) == 4);
405 Assert(cbBuf <= 32);
406 Assert(cbBuf > 0);
407
408 /*
409 * Issue the READ command.
410 */
411 uint32_t cMsSleep = 1;
412 for (;;)
413 {
414 ASMOutU32(SMC_PORT_CMD, SMC_CMD_GET_KEY_VALUE);
415 RTThreadSleep(cMsSleep);
416 uint8_t bCurState = ASMInU8(SMC_PORT_CMD);
417 if ((bCurState & 0xf) == 0xc)
418 break;
419 cMsSleep <<= 1;
420 if (cMsSleep > 64)
421 {
422 LogRel(("devR0Smc: %s: bCurState=%#x, wanted %#x.\n", "cmd", bCurState, 0xc));
423 return false;
424 }
425 }
426
427 /*
428 * Send it the key.
429 */
430 for (unsigned off = 0; off < 4; off++)
431 {
432 ASMOutU8(SMC_PORT_DATA, pszName[off]);
433 if (!devR0SmcWaitHostState(4, "key"))
434 return false;
435 }
436
437 /*
438 * The desired amount of output.
439 */
440 ASMOutU8(SMC_PORT_DATA, (uint8_t)cbBuf);
441
442 /*
443 * Read the output.
444 */
445 for (size_t off = 0; off < cbBuf; off++)
446 {
447 if (!devR0SmcWaitHostState(5, off ? "data" : "len"))
448 return false;
449 pbBuf[off] = ASMInU8(SMC_PORT_DATA);
450 }
451
452 LogRel(("SMC: pbBuf=%.*s\n", cbBuf, pbBuf));
453 return true;
454}
455
456
457/**
458 * RTOnce callback that initializes g_fHaveOsk and g_abOsk0And1.
459 *
460 * @returns VINF_SUCCESS.
461 * @param pvUserIgnored Ignored.
462 */
463static DECLCALLBACK(int) devR0SmcInitOnce(void *pvUserIgnored)
464{
465 g_fHaveOsk = devR0SmcQueryHostKey("OSK0", &g_abOsk0And1[0], 32)
466 && devR0SmcQueryHostKey("OSK1", &g_abOsk0And1[32], 32);
467
468#if 0
469 /*
470 * Dump the device registers.
471 */
472 for (uint16_t uPort = 0x300; uPort < 0x320; uPort ++)
473 LogRel(("SMC: %#06x=%#010x w={%#06x, %#06x}, b={%#04x %#04x %#04x %#04x}\n", uPort,
474 ASMInU32(uPort), ASMInU16(uPort), ASMInU16(uPort + 2),
475 ASMInU8(uPort), ASMInU8(uPort + 1), ASMInU8(uPort +2), ASMInU8(uPort + 3) ));
476#endif
477
478 NOREF(pvUserIgnored);
479 return VINF_SUCCESS;
480}
481
482
483/**
484 * @callback_method_impl{FNPDMDEVREQHANDLERR0}
485 */
486PDMBOTHCBDECL(int) devR0SmcReqHandler(PPDMDEVINS pDevIns, uint32_t uOperation, uint64_t u64Arg)
487{
488 PDEVSMC pThis = PDMINS_2_DATA(pDevIns, PDEVSMC);
489 int rc = VERR_INVALID_FUNCTION;
490 RT_NOREF_PV(u64Arg);
491
492 if (uOperation == SMC_CALLR0_READ_OSK)
493 {
494 rc = RTOnce(&g_SmcR0Once, devR0SmcInitOnce, NULL);
495 if ( RT_SUCCESS(rc)
496 && g_fHaveOsk)
497 {
498 AssertCompile(sizeof(g_abOsk0And1) + 1 == sizeof(pThis->szOsk0And1));
499 memcpy(pThis->szOsk0And1, g_abOsk0And1, sizeof(pThis->szOsk0And1) - 1);
500 pThis->szOsk0And1[sizeof(pThis->szOsk0And1) - 1] = '\0';
501 }
502 }
503 return rc;
504}
505
506#endif /* IN_RING0 */
507
508#if defined(IN_RING3) && defined(RT_OS_DARWIN)
509
510/**
511 * Preferred method to retrieve the SMC key.
512 *
513 * @param pabKey where to store the key.
514 * @param cbKey size of the buffer.
515 */
516static int getSmcKeyOs(char *pabKey, uint32_t cbKey)
517{
518 /*
519 * Method as described in Amit Singh's article:
520 * http://osxbook.com/book/bonus/chapter7/tpmdrmmyth/
521 */
522 typedef struct
523 {
524 uint32_t key;
525 uint8_t pad0[22];
526 uint32_t datasize;
527 uint8_t pad1[10];
528 uint8_t cmd;
529 uint32_t pad2;
530 uint8_t data[32];
531 } AppleSMCBuffer;
532
533 AssertReturn(cbKey >= 65, VERR_INTERNAL_ERROR);
534
535 io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault,
536 IOServiceMatching("AppleSMC"));
537 if (!service)
538 return VERR_NOT_FOUND;
539
540 io_connect_t port = (io_connect_t)0;
541 kern_return_t kr = IOServiceOpen(service, mach_task_self(), 0, &port);
542 IOObjectRelease(service);
543
544 if (kr != kIOReturnSuccess)
545 return RTErrConvertFromDarwin(kr);
546
547 AppleSMCBuffer inputStruct = { 0, {0}, 32, {0}, 5, };
548 AppleSMCBuffer outputStruct;
549 size_t cbOutputStruct = sizeof(outputStruct);
550
551 for (int i = 0; i < 2; i++)
552 {
553 inputStruct.key = (uint32_t)(i == 0 ? 'OSK0' : 'OSK1');
554 kr = IOConnectCallStructMethod((mach_port_t)port,
555 (uint32_t)2,
556 (const void *)&inputStruct,
557 sizeof(inputStruct),
558 (void *)&outputStruct,
559 &cbOutputStruct);
560 if (kr != kIOReturnSuccess)
561 {
562 IOServiceClose(port);
563 return RTErrConvertFromDarwin(kr);
564 }
565
566 for (int j = 0; j < 32; j++)
567 pabKey[j + i*32] = outputStruct.data[j];
568 }
569
570 IOServiceClose(port);
571
572 pabKey[64] = 0;
573
574 return VINF_SUCCESS;
575}
576
577#endif /* IN_RING3 && RT_OS_DARWIN */
578
579#ifdef IN_RING3 /* For now. */
580
581/** @callback_method_impl{DEVSMCKEYGETTER, OSK0 and OSK1} */
582static uint8_t scmKeyGetOSKs(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
583{
584 RT_NOREF1(bCmd);
585 Assert(SMC4CH_EQ(&pKeyDesc->Key, "OSK0") || SMC4CH_EQ(&pKeyDesc->Key, "OSK1"));
586 const char *pszSrc = pThis->szOsk0And1;
587 if (SMC4CH_EQ(&pKeyDesc->Key, "OSK1"))
588 pszSrc += 32;
589 memcpy(pCurKey->Value.ab, pszSrc, 32);
590 return SMC_STATUS_CD_SUCCESS;
591}
592
593
594/** @callback_method_impl{DEVSMCKEYGETTER, \#KEY} */
595static uint8_t scmKeyGetKeyCount(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
596{
597 RT_NOREF3(pThis, bCmd, pKeyDesc);
598 Assert(pKeyDesc == &g_aSmcKeys[SMC_KEYIDX_FIRST_ENUM]);
599 uint32_t cKeys = RT_ELEMENTS(g_aSmcKeys) - SMC_KEYIDX_FIRST_ENUM;
600 pCurKey->Value.u32 = RT_H2BE_U32(cKeys);
601 return SMC_STATUS_CD_SUCCESS;
602}
603
604
605/** @callback_method_impl{DEVSMCKEYGETTER, REV - Source revision.} */
606static uint8_t scmKeyGetRevision(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
607{
608 RT_NOREF3(pThis, bCmd, pKeyDesc);
609#ifdef VBOX_WITH_SMC_2_x
610 pCurKey->Value.ab[0] = 0x02;
611 pCurKey->Value.ab[1] = 0x03;
612 pCurKey->Value.ab[2] = 0x0f;
613 pCurKey->Value.ab[3] = 0x00;
614 pCurKey->Value.ab[4] = 0x00;
615 pCurKey->Value.ab[5] = 0x35;
616#else
617 pCurKey->Value.ab[0] = 0x01;
618 pCurKey->Value.ab[1] = 0x25;
619 pCurKey->Value.ab[2] = 0x0f;
620 pCurKey->Value.ab[3] = 0x00;
621 pCurKey->Value.ab[4] = 0x00;
622 pCurKey->Value.ab[5] = 0x04;
623#endif
624 return SMC_STATUS_CD_SUCCESS;
625}
626
627#ifdef VBOX_WITH_SMC_2_x
628
629/** @callback_method_impl{DEVSMCKEYGETTER, $Adr - SMC address.} */
630static uint8_t scmKeyGetDollarAddress(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
631{
632 RT_NOREF3(pThis, bCmd, pKeyDesc);
633 pCurKey->Value.u32 = RT_H2BE_U32(SMC_PORT_FIRST);
634 return VINF_SUCCESS;
635}
636
637
638/** @callback_method_impl{DEVSMCKEYGETTER, $Num - Some kind of number.} */
639static uint8_t scmKeyGetDollarNumber(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
640{
641 RT_NOREF2(bCmd, pKeyDesc);
642 pCurKey->Value.ab[0] = pThis->bDollaryNumber;
643 return VINF_SUCCESS;
644}
645
646/** @callback_method_impl{DEVSMCKEYPUTTER, $Num - Some kind of number.} */
647static uint8_t scmKeyPutDollarNumber(PDEVSMC pThis, PCDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
648{
649 RT_NOREF2(bCmd, pKeyDesc);
650 Log(("scmKeyPutDollarNumber: %#x -> %#x\n", pThis->bDollaryNumber, pCurKey->Value.ab[0]));
651 pThis->bDollaryNumber = pCurKey->Value.ab[0];
652 return VINF_SUCCESS;
653}
654
655#endif /* VBOX_WITH_SMC_2_x */
656
657/** @callback_method_impl{DEVSMCKEYGETTER, MSSD - Machine Shutdown reason.} */
658static uint8_t scmKeyGetShutdownReason(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
659{
660 RT_NOREF2(bCmd, pKeyDesc);
661 pCurKey->Value.ab[0] = pThis->bShutdownReason;
662 return SMC_STATUS_CD_SUCCESS;
663}
664
665
666/** @callback_method_impl{DEVSMCKEYPUTTER, MSSD - Machine Shutdown reason.} */
667static uint8_t scmKeyPutShutdownReason(PDEVSMC pThis, PCDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
668{
669 RT_NOREF2(bCmd, pKeyDesc);
670 Log(("scmKeyPutShutdownReason: %#x -> %#x\n", pThis->bShutdownReason, pCurKey->Value.ab[0]));
671 pThis->bShutdownReason = pCurKey->Value.ab[0];
672 return SMC_STATUS_CD_SUCCESS;
673}
674
675
676/** @callback_method_impl{DEVSMCKEYGETTER, MSSD - Ninja timer action job.} */
677static uint8_t scmKeyGetNinjaTimerAction(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
678{
679 RT_NOREF2(bCmd, pKeyDesc);
680 pCurKey->Value.ab[0] = pThis->bNinjaActionTimerJob;
681 return SMC_STATUS_CD_SUCCESS;
682}
683
684
685/** @callback_method_impl{DEVSMCKEYPUTTER, NATJ - Ninja timer action job.} */
686static uint8_t scmKeyPutNinjaTimerAction(PDEVSMC pThis, PCDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
687{
688 RT_NOREF2(bCmd, pKeyDesc);
689 Log(("scmKeyPutNinjaTimerAction: %#x -> %#x\n", pThis->bNinjaActionTimerJob, pCurKey->Value.ab[0]));
690 pThis->bNinjaActionTimerJob = pCurKey->Value.ab[0];
691 return SMC_STATUS_CD_SUCCESS;
692}
693
694#ifdef VBOX_WITH_SMC_2_x
695
696/** @callback_method_impl{DEVSMCKEYGETTER, Generic one getter.} */
697static uint8_t scmKeyGetOne(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
698{
699 RT_NOREF2(pThis, bCmd);
700 memset(&pCurKey->Value.ab[0], 0, pKeyDesc->cbValue);
701 pCurKey->Value.ab[pKeyDesc->cbValue - 1] = 1;
702 return SMC_STATUS_CD_SUCCESS;
703}
704
705#endif /* VBOX_WITH_SMC_2_x */
706
707/** @callback_method_impl{DEVSMCKEYGETTER, Generic zero getter.} */
708static uint8_t scmKeyGetZero(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
709{
710 RT_NOREF2(pThis, bCmd);
711 memset(&pCurKey->Value.ab[0], 0, pKeyDesc->cbValue);
712 return SMC_STATUS_CD_SUCCESS;
713}
714
715
716/**
717 * Looks up a key and copies its value and attributes into the CurKey.
718 *
719 * @returns Key index on success, UINT32_MAX on failure.
720 * @param uKeyValue The key value (DEVSMC4CHID.u32).
721 */
722static uint32_t smcKeyLookup(uint32_t uKeyValue)
723{
724 uint32_t iKey = RT_ELEMENTS(g_aSmcKeys);
725 while (iKey-- > 0)
726 if (g_aSmcKeys[iKey].Key.u32 == uKeyValue)
727 return iKey;
728 return UINT32_MAX;
729}
730
731
732/**
733 * Looks up a key and copies its value and attributes into the CurKey.
734 *
735 * @returns Apple SMC Status Code.
736 * @param pThis The SMC instance data.
737 */
738static uint8_t smcKeyGetByName(PDEVSMC pThis)
739{
740 uint8_t bRc;
741#ifdef LOG_ENABLED
742 uint32_t const uKeyValueLog = RT_H2LE_U32(pThis->CurKey.Key.u32);
743#endif
744 uint32_t iKey = smcKeyLookup(pThis->CurKey.Key.u32);
745 if (iKey != UINT32_MAX)
746 {
747 if ( g_aSmcKeys[iKey].cbValue == pThis->CurKey.cbValue
748 || !g_aSmcKeys[iKey].cbValue)
749 {
750 pThis->CurKey.Type = g_aSmcKeys[iKey].Type;
751 pThis->CurKey.fAttr = g_aSmcKeys[iKey].fAttr;
752 RT_ZERO(pThis->CurKey.Value);
753 if (g_aSmcKeys[iKey].pfnGet)
754 {
755 bRc = g_aSmcKeys[iKey].pfnGet(pThis, &pThis->CurKey, pThis->bCmd, &g_aSmcKeys[iKey]);
756 if (bRc == SMC_STATUS_CD_SUCCESS)
757 {
758 LogFlow(("smcKeyGetByName: key=%4.4s value=%.*Rhxs\n",
759 &uKeyValueLog, pThis->CurKey.cbValue, &pThis->CurKey.Value));
760 return SMC_STATUS_CD_SUCCESS;
761 }
762
763 Log(("smcKeyGetByName: key=%4.4s getter failed! bRc=%#x\n", &uKeyValueLog, bRc));
764 }
765 else
766 {
767 Log(("smcKeyGetByName: key=%4.4s is not readable!\n", &uKeyValueLog));
768 bRc = SMC_STATUS_CD_KEY_NOT_READABLE;
769 }
770 }
771 else
772 {
773 Log(("smcKeyGetByName: Wrong value size; user=%#x smc=%#x key=%4.4s !\n",
774 pThis->CurKey.cbValue, g_aSmcKeys[iKey].cbValue, &uKeyValueLog));
775 bRc = SMC_STATUS_CD_KEY_SIZE_MISMATCH;
776 }
777 }
778 else
779 {
780 Log(("smcKeyGetByName: Key not found! key=%4.4s size=%#x\n", &uKeyValueLog, pThis->CurKey.cbValue));
781 bRc = SMC_STATUS_CD_KEY_NOT_FOUND;
782 }
783
784 RT_ZERO(pThis->CurKey);
785 return bRc;
786}
787
788
789/**
790 * Looks up a key by index and copies its name (and attributes) into the CurKey.
791 *
792 * @returns Apple SMC Status Code.
793 * @param pThis The SMC instance data.
794 */
795static uint8_t smcKeyGetByIndex(PDEVSMC pThis)
796{
797 uint8_t bRc;
798 uint32_t iKey = RT_BE2H_U32(pThis->CurKey.Key.u32);
799 if (iKey < RT_ELEMENTS(g_aSmcKeys) - SMC_KEYIDX_FIRST_ENUM)
800 {
801 pThis->CurKey.Key = g_aSmcKeys[iKey].Key;
802 pThis->CurKey.Type = g_aSmcKeys[iKey].Type;
803 pThis->CurKey.fAttr = g_aSmcKeys[iKey].fAttr;
804 pThis->CurKey.cbValue = g_aSmcKeys[iKey].cbValue;
805 RT_ZERO(pThis->CurKey.Value);
806 Log(("smcKeyGetByIndex: %#x -> %c%c%c%c\n", iKey,
807 pThis->CurKey.Key.ab[3], pThis->CurKey.Key.ab[2], pThis->CurKey.Key.ab[1], pThis->CurKey.Key.ab[0]));
808 bRc = SMC_STATUS_CD_SUCCESS;
809 }
810 else
811 {
812 Log(("smcKeyGetByIndex: Key out or range: %#x, max %#x\n", iKey, RT_ELEMENTS(g_aSmcKeys) - SMC_KEYIDX_FIRST_ENUM));
813 bRc = SMC_STATUS_CD_KEY_NOT_FOUND;
814 }
815 return bRc;
816}
817
818
819/**
820 * Looks up a key by index and copies its attributes into the CurKey.
821 *
822 * @returns Apple SMC Status Code.
823 * @param pThis The SMC instance data.
824 */
825static uint8_t smcKeyGetAttrByName(PDEVSMC pThis)
826{
827 uint8_t bRc;
828#ifdef LOG_ENABLED
829 uint32_t const uKeyValueLog = RT_H2LE_U32(pThis->CurKey.Key.u32);
830#endif
831 uint32_t iKey = smcKeyLookup(pThis->CurKey.Key.u32);
832 if (iKey != UINT32_MAX)
833 {
834 pThis->CurKey.Type = g_aSmcKeys[iKey].Type;
835 pThis->CurKey.fAttr = g_aSmcKeys[iKey].fAttr;
836 pThis->CurKey.cbValue = g_aSmcKeys[iKey].cbValue;
837 RT_ZERO(pThis->CurKey.Value);
838 if (g_aSmcKeys[iKey].cbValue)
839 bRc = SMC_STATUS_CD_SUCCESS;
840 else
841 bRc = g_aSmcKeys[iKey].pfnGet(pThis, &pThis->CurKey, pThis->bCmd, &g_aSmcKeys[iKey]);
842 if (bRc == SMC_STATUS_CD_SUCCESS)
843 {
844 LogFlow(("smcKeyGetAttrByName: key=%4.4s value=%.*Rhxs\n",
845 &uKeyValueLog, pThis->CurKey.cbValue, &pThis->CurKey.Value));
846 return SMC_STATUS_CD_SUCCESS;
847 }
848
849 Log(("smcKeyGetAttrByName: key=%4.4s getter failed! bRc=%#x\n", &uKeyValueLog, bRc));
850 }
851 else
852 {
853 Log(("smcKeyGetAttrByName: Key not found! key=%4.4s size=%#x\n", &uKeyValueLog, pThis->CurKey.cbValue));
854 bRc = SMC_STATUS_CD_KEY_NOT_FOUND;
855 }
856
857 RT_ZERO(pThis->CurKey);
858 return bRc;
859}
860
861
862static uint8_t smcKeyPutPrepare(PDEVSMC pThis)
863{
864 RT_NOREF1(pThis);
865 return 0;
866}
867
868static uint8_t smcKeyPutValue(PDEVSMC pThis)
869{
870 RT_NOREF1(pThis);
871 return 0;
872}
873
874
875/**
876 * Data register read.
877 *
878 * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
879 * @param uReg The register number.
880 * @param pbValue Where to return the value.
881 */
882static int smcRegData_r(PDEVSMC pThis, uint8_t uReg, uint8_t *pbValue)
883{
884 RT_NOREF1(uReg);
885 switch (pThis->bCmd)
886 {
887 case SMC_CMD_GET_KEY_VALUE:
888 if ( pThis->u.s.bState == 0x05
889 && pThis->offValue < pThis->CurKey.cbValue)
890 {
891 *pbValue = pThis->CurKey.Value.ab[pThis->offValue];
892 if (++pThis->offValue >= pThis->CurKey.cbValue)
893 pThis->u.s.bState = 0x00;
894 pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
895 }
896 else
897 {
898 Log(("smcRegData_r: Reading too much or at wrong time during SMC_CMD_GET_KEY_INFO! bState=%#x offValue=%#x\n",
899 pThis->u.s.bState, pThis->offValue));
900 pThis->u.s.bState = 0x00;
901 pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA; /** @todo check status code */
902 }
903 break;
904
905 case SMC_CMD_GET_KEY_INFO:
906 if ( pThis->u.s.bState == 0x05
907 && pThis->offValue < 6)
908 {
909 if (pThis->offValue == 0)
910 *pbValue = pThis->CurKey.cbValue;
911 else if (pThis->offValue < 1 + 4)
912 *pbValue = pThis->CurKey.Type.ab[pThis->offValue - 1];
913 else
914 *pbValue = pThis->CurKey.fAttr;
915 if (++pThis->offValue >= 6)
916 pThis->u.s.bState = 0x00;
917 pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
918 }
919 else
920 {
921 Log(("smcRegData_r: Reading too much or at wrong time during SMC_CMD_GET_KEY_INFO! bState=%#x offValue=%#x\n",
922 pThis->u.s.bState, pThis->offValue));
923 pThis->u.s.bState = 0x00;
924 pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA; /** @todo check status code */
925 }
926 break;
927
928 case SMC_CMD_GET_KEY_BY_INDEX:
929 if ( pThis->u.s.bState == 0x05
930 && pThis->offValue < sizeof(pThis->CurKey.Key))
931 {
932 *pbValue = pThis->CurKey.Key.ab[pThis->offValue];
933 if (++pThis->offValue >= sizeof(pThis->CurKey.Key))
934 pThis->u.s.bState = 0x00;
935 pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
936 }
937 else
938 {
939 Log(("smcRegData_r: Reading too much or at wrong time during GET_KEY_BY_INDEX! bState=%#x offValue=%#x\n",
940 pThis->u.s.bState, pThis->offValue));
941 pThis->u.s.bState = 0x00;
942 pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA; /** @todo check status code */
943 }
944 break;
945
946 case SMC_CMD_PUT_KEY:
947 Log(("smcRegData_r: Attempting to read data during PUT_KEY!\n"));
948 *pbValue = 0xff;
949 pThis->u.s.bState = 0;
950 pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA;
951 break;
952
953 default:
954 Log(("smcRegData_r: Unknown command attempts reading data\n"));
955 *pbValue = 0xff;
956 pThis->u.s.bState = 0;
957 pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA;
958 break;
959 }
960
961 return VINF_SUCCESS;
962}
963
964
965/**
966 * Data register write.
967 *
968 * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
969 * @param uReg The register number.
970 * @param bValue The value being written.
971 */
972static int smcRegData_w(PDEVSMC pThis, uint8_t uReg, uint8_t bValue)
973{
974 RT_NOREF1(uReg);
975 switch (pThis->bCmd)
976 {
977 /*
978 * Get or put key value.
979 *
980 * 5 bytes written, first 4 is the key the 5th is the value size. In
981 * the case of a put the value bytes are then written, while a get will
982 * read the value bytes.
983 */
984 case SMC_CMD_GET_KEY_VALUE:
985 case SMC_CMD_PUT_KEY:
986 if (pThis->offKey < 4)
987 {
988 /* Key byte. */
989 pThis->CurKey.Key.ab[pThis->offKey++] = bValue;
990 pThis->u.s.bState = 0x04;
991 pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
992 }
993 else if (pThis->offKey == 4)
994 {
995 /* Data length. */
996 pThis->u.s.bState = 0;
997 if (bValue <= sizeof(pThis->CurKey.Value))
998 {
999 pThis->CurKey.cbValue = bValue;
1000 pThis->offKey = 5;
1001 Assert(pThis->offValue == 0);
1002
1003 if (pThis->bCmd == SMC_CMD_GET_KEY_VALUE)
1004 pThis->u.s.bStatusCode = smcKeyGetByName(pThis);
1005 else
1006 pThis->u.s.bStatusCode = smcKeyPutPrepare(pThis);
1007 if (pThis->u.s.bStatusCode == SMC_STATUS_CD_SUCCESS)
1008 pThis->u.s.bState = 0x05;
1009 }
1010 else
1011 {
1012 Log(("smcRegData_w: Guest attempts to get/put too many value bytes: %#x (max %#x)!\n",
1013 bValue, sizeof(pThis->CurKey.Value)));
1014 pThis->u.s.bStatusCode = SMC_STATUS_CD_KEY_SIZE_MISMATCH; /** @todo check this case! */
1015 }
1016 }
1017 else if ( pThis->bCmd == SMC_CMD_PUT_KEY
1018 && pThis->offValue < pThis->CurKey.cbValue)
1019 {
1020 /* More value bytes for put key action. */
1021 pThis->CurKey.Value.ab[pThis->offValue++] = bValue;
1022 if (pThis->offValue != pThis->CurKey.cbValue)
1023 pThis->u.s.bState = 0x05;
1024 else
1025 {
1026 pThis->u.s.bState = 0x00;
1027 pThis->u.s.bStatusCode = smcKeyPutValue(pThis);
1028 }
1029 }
1030 else
1031 {
1032 Log(("smcRegData_w: Writing too much data on %s command!\n", pThis->bCmd == SMC_CMD_PUT_KEY ? "put" : "get"));
1033 pThis->u.s.bState = 0x00;
1034 pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA;
1035 }
1036 break;
1037
1038 /*
1039 * Get key info and key by index seems to take action after the last
1040 * key char is written. They then both go into a data reading phase.
1041 */
1042 case SMC_CMD_GET_KEY_INFO:
1043 case SMC_CMD_GET_KEY_BY_INDEX:
1044 if (pThis->offKey < 4)
1045 {
1046 pThis->CurKey.Key.ab[pThis->offKey] = bValue;
1047 if (++pThis->offKey == 4)
1048 {
1049 if (pThis->bCmd == SMC_CMD_GET_KEY_BY_INDEX)
1050 pThis->u.s.bStatusCode = smcKeyGetByIndex(pThis);
1051 else
1052 pThis->u.s.bStatusCode = smcKeyGetAttrByName(pThis);
1053 pThis->u.s.bState = pThis->u.s.bStatusCode == SMC_STATUS_CD_SUCCESS ? 0x05 : 0x00;
1054 }
1055 else
1056 {
1057 pThis->u.s.bState = 0x04;
1058 pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
1059 }
1060 }
1061 else
1062 {
1063 Log(("smcRegData_w: Writing data beyond 5th byte on get %s command!\n",
1064 pThis->bCmd == SMC_CMD_GET_KEY_INFO ? "info" : "by index"));
1065 pThis->u.s.bState = 0x00;
1066 pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA;
1067 }
1068 break;
1069
1070 default:
1071 Log(("smcRegData_w: Unknown command %#x!\n", bValue));
1072 pThis->u.s.bState = 0x00; /** @todo Check statuses with real HW. */
1073 pThis->u.s.bStatusCode = SMC_STATUS_CD_BAD_COMMAND;
1074 break;
1075 }
1076 return VINF_SUCCESS;
1077}
1078
1079
1080/**
1081 * Command register write.
1082 *
1083 * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
1084 * @param uReg The register number.
1085 * @param bValue The value being written.
1086 */
1087static int smcRegCmd_w(PDEVSMC pThis, uint8_t uReg, uint8_t bValue)
1088{
1089 LogFlow(("smcRegCmd_w: New command: %#x (old=%#x)\n", bValue, pThis->bCmd)); NOREF(uReg);
1090
1091 pThis->bCmd = bValue;
1092
1093 /* Validate the command. */
1094 switch (bValue)
1095 {
1096 case SMC_CMD_GET_KEY_VALUE:
1097 case SMC_CMD_PUT_KEY:
1098 case SMC_CMD_GET_KEY_BY_INDEX:
1099 case SMC_CMD_GET_KEY_INFO:
1100 pThis->u.s.bState = 0x0c;
1101 pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
1102 break;
1103
1104 default:
1105 Log(("SMC: Unknown command %#x!\n", bValue));
1106 pThis->u.s.bState = 0x00; /** @todo Check state with real HW. */
1107 pThis->u.s.bStatusCode = SMC_STATUS_CD_BAD_COMMAND;
1108 break;
1109 }
1110
1111 /* Reset the value/key related state. */
1112 pThis->offKey = 0;
1113 pThis->offValue = 0;
1114 pThis->CurKey.Key.u32 = 0;
1115 pThis->CurKey.cbValue = 0;
1116
1117 return VINF_SUCCESS;
1118}
1119
1120
1121/**
1122 * Generic register write.
1123 *
1124 * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
1125 * @param uReg The register number.
1126 * @param bValue The value being written.
1127 */
1128static int smcRegGen_w(PDEVSMC pThis, uint8_t uReg, uint8_t bValue)
1129{
1130 Log(("smcRegGen_w: %#04x: %#x -> %#x (write)\n", uReg, pThis->u.abRegsRW[uReg], bValue));
1131 pThis->u.abRegsRW[uReg] = bValue;
1132 return VINF_SUCCESS;
1133}
1134
1135
1136/**
1137 * Read from register that isn't writable and reads as 0xFF.
1138 *
1139 * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
1140 * @param uReg The register number.
1141 * @param pbValue Where to return the value.
1142 */
1143static int smcRegGen_r(PDEVSMC pThis, uint8_t uReg, uint8_t *pbValue)
1144{
1145 Log(("smcRegGen_r: %#04x: %#x (read)\n", uReg, pThis->u.abRegsRW[uReg]));
1146 *pbValue = pThis->u.abRegsRW[uReg];
1147 return VINF_SUCCESS;
1148}
1149
1150
1151/**
1152 * Write to register that isn't writable and reads as 0xFF.
1153 *
1154 * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
1155 * @param uReg The register number.
1156 * @param bValue The value being written.
1157 */
1158static int smcRegFF_w(PDEVSMC pThis, uint8_t uReg, uint8_t bValue)
1159{
1160 RT_NOREF3(pThis, uReg, bValue);
1161 Log(("SMC: %#04x: Writing %#x to unknown register!\n", uReg, bValue));
1162 return VINF_SUCCESS;
1163}
1164
1165
1166/**
1167 * Read from register that isn't writable and reads as 0xFF.
1168 *
1169 * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
1170 * @param uReg The register number.
1171 * @param pbValue Where to return the value.
1172 */
1173static int smcRegFF_r(PDEVSMC pThis, uint8_t uReg, uint8_t *pbValue)
1174{
1175 RT_NOREF2(pThis, uReg);
1176 Log(("SMC: %#04x: Reading from unknown register!\n", uReg));
1177 *pbValue = 0xff;
1178 return VINF_SUCCESS;
1179}
1180
1181
1182
1183/**
1184 * SMC register handlers (indexed by relative I/O port).
1185 *
1186 * The device seems to be all byte registers and will split wider
1187 * accesses between registers like if it was MMIO. To better illustrate it
1188 * here is the output of the code in devR0SmcInitOnce on a MacPro3,1:
1189 * @verbatim
1190 * SMC: 0x0300=0xffffff63 w={0xff63, 0xffff}, b={0x63 0xff 0xff 0xff}
1191 * SMC: 0x0301=0x0cffffff w={0xffff, 0x0cff}, b={0xff 0xff 0xff 0x0c}
1192 * SMC: 0x0302=0xff0cffff w={0xffff, 0xff0c}, b={0xff 0xff 0x0c 0xff}
1193 * SMC: 0x0303=0xffff0cff w={0x0cff, 0xffff}, b={0xff 0x0c 0xff 0xff}
1194 * SMC: 0x0304=0xffffff0c w={0xff0c, 0xffff}, b={0x0c 0xff 0xff 0xff}
1195 * SMC: 0x0305=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1196 * SMC: 0x0306=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1197 * SMC: 0x0307=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1198 * SMC: 0x0308=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1199 * SMC: 0x0309=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1200 * SMC: 0x030a=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1201 * SMC: 0x030b=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1202 * SMC: 0x030c=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1203 * SMC: 0x030d=0x00ffffff w={0xffff, 0x00ff}, b={0xff 0xff 0xff 0x00}
1204 * SMC: 0x030e=0x0000ffff w={0xffff, 0x0000}, b={0xff 0xff 0x00 0x00}
1205 * SMC: 0x030f=0x000000ff w={0x00ff, 0x0000}, b={0xff 0x00 0x00 0x00}
1206 * SMC: 0x0310=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1207 * SMC: 0x0311=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1208 * SMC: 0x0312=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1209 * SMC: 0x0313=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1210 * SMC: 0x0314=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1211 * SMC: 0x0315=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1212 * SMC: 0x0316=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1213 * SMC: 0x0317=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1214 * SMC: 0x0318=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1215 * SMC: 0x0319=0xbe000000 w={0x0000, 0xbe00}, b={0x00 0x00 0x00 0xbe}
1216 * SMC: 0x031a=0xbabe0000 w={0x0000, 0xbabe}, b={0x00 0x00 0xbe 0xba}
1217 * SMC: 0x031b=0x00babe00 w={0xbe00, 0x00ba}, b={0x00 0xbe 0xba 0x00}
1218 * SMC: 0x031c=0xbe00babe w={0xbabe, 0xbe00}, b={0xbe 0xba 0x00 0xbe}
1219 * SMC: 0x031d=0xffbe00ba w={0x00ba, 0xffbe}, b={0xba 0x00 0xbe 0xff}
1220 * SMC: 0x031e=0xffffbe00 w={0xbe00, 0xffff}, b={0x00 0xbe 0xff 0xff}
1221 * SMC: 0x031f=0xffffffbe w={0xffbe, 0xffff}, b={0xbe 0xff 0xff 0xff}
1222 * @endverbatim
1223 *
1224 * The last dword is writable (0xbeXXbabe) where in the register at 0x1e is some
1225 * kind of status register for qualifying search failures and the like and will
1226 * be cleared under certain conditions. The whole dword can be written and read
1227 * back unchanged, according to my experiments. The 0x00 and 0x04 registers
1228 * does not read back what is written.
1229 *
1230 * My guess is that the 0xff values indicates ports that are not writable and
1231 * hardwired to 0xff, while the other values indicates ports that can be written
1232 * to and normally read back as written. I'm not going to push my luck too far
1233 * wrt to exact behavior until I see the guest using the registers.
1234 */
1235static const struct
1236{
1237 int (*pfnWrite)(PDEVSMC pThis, uint8_t uReg, uint8_t bValue);
1238 int (*pfnRead)(PDEVSMC pThis, uint8_t uReg, uint8_t *pbValue);
1239} g_aSmcRegs[SMC_REG_COUNT] =
1240{
1241 /* [0x00] = */ { smcRegData_w, smcRegData_r },
1242 /* [0x01] = */ { smcRegFF_w, smcRegFF_r },
1243 /* [0x02] = */ { smcRegFF_w, smcRegFF_r },
1244 /* [0x03] = */ { smcRegFF_w, smcRegFF_r },
1245 /* [0x04] = */ { smcRegCmd_w, smcRegGen_r },
1246 /* [0x05] = */ { smcRegFF_w, smcRegFF_r },
1247 /* [0x06] = */ { smcRegFF_w, smcRegFF_r },
1248 /* [0x07] = */ { smcRegFF_w, smcRegFF_r },
1249 /* [0x08] = */ { smcRegFF_w, smcRegFF_r },
1250 /* [0x09] = */ { smcRegFF_w, smcRegFF_r },
1251 /* [0x0a] = */ { smcRegFF_w, smcRegFF_r },
1252 /* [0x0b] = */ { smcRegFF_w, smcRegFF_r },
1253 /* [0x0c] = */ { smcRegFF_w, smcRegFF_r },
1254 /* [0x0d] = */ { smcRegFF_w, smcRegFF_r },
1255 /* [0x0e] = */ { smcRegFF_w, smcRegFF_r },
1256 /* [0x0f] = */ { smcRegFF_w, smcRegFF_r },
1257 /* [0x10] = */ { smcRegGen_w, smcRegGen_r },
1258 /* [0x11] = */ { smcRegGen_w, smcRegGen_r },
1259 /* [0x12] = */ { smcRegGen_w, smcRegGen_r },
1260 /* [0x13] = */ { smcRegGen_w, smcRegGen_r },
1261 /* [0x14] = */ { smcRegGen_w, smcRegGen_r },
1262 /* [0x15] = */ { smcRegGen_w, smcRegGen_r },
1263 /* [0x16] = */ { smcRegGen_w, smcRegGen_r },
1264 /* [0x17] = */ { smcRegGen_w, smcRegGen_r },
1265 /* [0x18] = */ { smcRegGen_w, smcRegGen_r },
1266 /* [0x19] = */ { smcRegGen_w, smcRegGen_r },
1267 /* [0x1a] = */ { smcRegGen_w, smcRegGen_r },
1268 /* [0x1b] = */ { smcRegGen_w, smcRegGen_r },
1269 /* [0x1c] = */ { smcRegGen_w, smcRegGen_r },
1270 /* [0x1d] = */ { smcRegGen_w, smcRegGen_r },
1271 /* [0x1e] = */ { smcRegGen_w, smcRegGen_r },
1272 /* [0x1f] = */ { smcRegGen_w, smcRegGen_r },
1273};
1274
1275
1276/** @callback_method_impl{FNIOMIOPORTOUT} */
1277PDMBOTHCBDECL(int) smcIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
1278{
1279 RT_NOREF1(pvUser);
1280#ifndef IN_RING3
1281 if (cb > 1)
1282 return VINF_IOM_R3_IOPORT_WRITE;
1283#endif
1284
1285 /*
1286 * The first register, usually only one is accessed.
1287 */
1288 PDEVSMC pThis = PDMINS_2_DATA(pDevIns, PDEVSMC);
1289 uint32_t uReg = Port - SMC_PORT_FIRST;
1290 int rc = g_aSmcRegs[uReg].pfnWrite(pThis, uReg, u32);
1291
1292 /*
1293 * On the off chance that multiple registers are being read.
1294 */
1295 if (cb > 1)
1296 {
1297 while (cb > 1 && uReg < SMC_REG_COUNT - 1)
1298 {
1299 cb--;
1300 uReg++;
1301 u32 >>= 8;
1302 int rc2 = g_aSmcRegs[uReg].pfnWrite(pThis, uReg, u32);
1303 if (rc2 != VINF_SUCCESS)
1304 {
1305 if ( rc == VINF_SUCCESS
1306 || (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1307 || (rc2 < rc && RT_SUCCESS(rc2) && RT_SUCCESS(rc)))
1308 rc = rc2;
1309 }
1310 }
1311 }
1312
1313 LogFlow(("smcIoPortWrite: %#04x write access: %#x (LB %u) rc=%Rrc\n", uReg, u32, cb, rc));
1314 return rc;
1315}
1316
1317
1318/** @callback_method_impl{FNIOMIOPORTIN} */
1319PDMBOTHCBDECL(int) smcIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
1320{
1321 RT_NOREF1(pvUser);
1322#ifndef IN_RING3
1323 if (cb > 1)
1324 return VINF_IOM_R3_IOPORT_READ;
1325#endif
1326
1327 PDEVSMC pThis = PDMINS_2_DATA(pDevIns, PDEVSMC);
1328
1329 /*
1330 * The first register, usually only one is accessed.
1331 */
1332 uint32_t uReg = Port - SMC_PORT_FIRST;
1333 Log2(("smcIoPortRead: %#04x read access: LB %u\n", uReg, cb));
1334 uint8_t bValue = 0xff;
1335 int rc = g_aSmcRegs[uReg].pfnRead(pThis, uReg, &bValue);
1336 *pu32 = bValue;
1337
1338 /*
1339 * On the off chance that multiple registers are being read.
1340 */
1341 if (cb > 1)
1342 {
1343 do
1344 {
1345 cb--;
1346 uReg++;
1347 bValue = 0xff;
1348 if (uReg < SMC_REG_COUNT)
1349 {
1350 int rc2 = g_aSmcRegs[uReg].pfnRead(pThis, uReg, &bValue);
1351 if (rc2 != VINF_SUCCESS)
1352 {
1353 if ( rc == VINF_SUCCESS
1354 || (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1355 || (rc2 < rc && RT_SUCCESS(rc2) && RT_SUCCESS(rc)))
1356 rc = rc2;
1357 }
1358 }
1359 *pu32 |= (uint32_t)bValue << ((4 - cb) * 8);
1360 } while (cb > 1);
1361 }
1362 LogFlow(("smcIoPortRead: %#04x read access: %#x (LB %u) rc=%Rrc\n", uReg, *pu32, cb, rc));
1363 return rc;
1364}
1365
1366#endif /* IN_RING3 for now */
1367#ifdef IN_RING3
1368
1369/** @callback_method_impl{FNSSMDEVSAVEEXEC} */
1370static DECLCALLBACK(int) smcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1371{
1372 PDEVSMC pThis = PDMINS_2_DATA(pDevIns, PDEVSMC);
1373 RT_NOREF2(pSSM, pThis);
1374
1375 /** @todo */
1376
1377 return VINF_SUCCESS;
1378}
1379
1380
1381/** @callback_method_impl{FNSSMDEVLOADEXEC} */
1382static DECLCALLBACK(int) smcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1383{
1384 PDEVSMC pThis = PDMINS_2_DATA(pDevIns, PDEVSMC);
1385 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
1386 RT_NOREF2(pSSM, pThis);
1387
1388 /* Fend off unsupported versions. */
1389 if ( uVersion != SMC_SAVED_STATE_VERSION
1390#if SMC_SAVED_STATE_VERSION != SMC_SAVED_STATE_VERSION_BAKA
1391 && uVersion != SMC_SAVED_STATE_VERSION_BAKA
1392#endif
1393 && uVersion != SMC_SAVED_STATE_VERSION_BAKA + 1)
1394 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1395
1396 /*
1397 * Do the actual restoring.
1398 */
1399 if (uVersion == SMC_SAVED_STATE_VERSION)
1400 {
1401 /** @todo */
1402 }
1403
1404 return VINF_SUCCESS;
1405}
1406
1407
1408/**
1409 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1410 */
1411static DECLCALLBACK(int) smcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1412{
1413 RT_NOREF1(iInstance);
1414 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1415 PDEVSMC pThis = PDMINS_2_DATA(pDevIns, PDEVSMC);
1416 Assert(iInstance == 0);
1417
1418 /*
1419 * Init the data.
1420 */
1421 pThis->bDollaryNumber = 1;
1422 pThis->bShutdownReason = 3; /* STOP_CAUSE_POWERKEY_GOOD_CODE */
1423
1424 /*
1425 * Validate configuration.
1426 */
1427 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DeviceKey|GetKeyFromRealSMC", "");
1428
1429 /*
1430 * Read configuration.
1431 */
1432
1433 /* The DeviceKey sets OSK0 and OSK1. */
1434 int rc = CFGMR3QueryStringDef(pCfg, "DeviceKey", pThis->szOsk0And1, sizeof(pThis->szOsk0And1), "");
1435 if (RT_FAILURE(rc))
1436 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1437 N_("Configuration error: Querying \"DeviceKey\" as a string failed"));
1438
1439 /* Query the key from the OS / real hardware if asked to do so. */
1440 bool fGetKeyFromRealSMC;
1441 rc = CFGMR3QueryBoolDef(pCfg, "GetKeyFromRealSMC", &fGetKeyFromRealSMC, false);
1442 if (RT_FAILURE(rc))
1443 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1444 N_("Configuration error: Querying \"GetKeyFromRealSMC\" as a boolean failed"));
1445 if (fGetKeyFromRealSMC)
1446 {
1447#ifdef RT_OS_DARWIN
1448 rc = getSmcKeyOs(pThis->szOsk0And1, sizeof(pThis->szOsk0And1));
1449 if (RT_FAILURE(rc))
1450 {
1451 LogRel(("SMC: Retrieving the SMC key from the OS failed (%Rrc), trying to read it from hardware\n", rc));
1452#endif
1453 rc = PDMDevHlpCallR0(pDevIns, SMC_CALLR0_READ_OSK, 0 /*u64Arg*/);
1454 if (RT_SUCCESS(rc))
1455 LogRel(("SMC: Successfully retrieved the SMC key from hardware\n"));
1456 else
1457 LogRel(("SMC: Retrieving the SMC key from hardware failed(%Rrc)\n", rc));
1458#ifdef RT_OS_DARWIN
1459 }
1460 else
1461 LogRel(("SMC: Successfully retrieved the SMC key from the OS\n"));
1462#endif
1463 if (RT_FAILURE(rc))
1464 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1465 N_("Failed to query SMC value from the host"));
1466 }
1467
1468 /*
1469 * Register I/O Ports
1470 */
1471 rc = PDMDevHlpIOPortRegister(pDevIns, SMC_PORT_FIRST, SMC_REG_COUNT, NULL,
1472 smcIoPortWrite, smcIoPortRead,
1473 NULL, NULL, "SMC data port");
1474 AssertRCReturn(rc, rc);
1475
1476 /** @todo Newer versions (2.03) have an MMIO mapping as well (ACPI). */
1477
1478
1479 /*
1480 * Saved state.
1481 */
1482 rc = PDMDevHlpSSMRegister(pDevIns, SMC_SAVED_STATE_VERSION, sizeof(*pThis), smcSaveExec, smcLoadExec);
1483 if (RT_FAILURE(rc))
1484 return rc;
1485
1486 return VINF_SUCCESS;
1487}
1488
1489
1490/**
1491 * The device registration structure.
1492 */
1493const PDMDEVREG g_DeviceSmc =
1494{
1495 /* u32Version */
1496 PDM_DEVREG_VERSION,
1497 /* szName */
1498 "smc",
1499 /* szRCMod */
1500 "VBoxDDRC.rc",
1501 /* szR0Mod */
1502 "VBoxDDR0.r0",
1503 /* pszDescription */
1504 "Apple System Management Controller",
1505 /* fFlags */
1506 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT | PDM_DEVREG_FLAGS_R0 | PDM_DEVREG_FLAGS_RC,
1507 /* fClass */
1508 PDM_DEVREG_CLASS_ARCH,
1509 /* cMaxInstances */
1510 1,
1511 /* cbInstance */
1512 sizeof(DEVSMC),
1513 /* pfnConstruct */
1514 smcConstruct,
1515 /* pfnDestruct */
1516 NULL,
1517 /* pfnRelocate */
1518 NULL,
1519 /* pfnMemSetup */
1520 NULL,
1521 /* pfnPowerOn */
1522 NULL,
1523 /* pfnReset */
1524 NULL,
1525 /* pfnSuspend */
1526 NULL,
1527 /* pfnResume */
1528 NULL,
1529 /* pfnAttach */
1530 NULL,
1531 /* pfnDetach */
1532 NULL,
1533 /* pfnQueryInterface. */
1534 NULL,
1535 /* pfnInitComplete. */
1536 NULL,
1537 /* pfnPowerOff */
1538 NULL,
1539 /* pfnSoftReset */
1540 NULL,
1541 /* u32VersionEnd */
1542 PDM_DEVREG_VERSION
1543};
1544
1545#endif /* IN_RING3 */
1546#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette