VirtualBox

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

Last change on this file since 60404 was 57358, checked in by vboxsync, 9 years ago

*: scm cleanup run.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.2 KB
Line 
1/* $Id: DevSmc.cpp 57358 2015-08-14 15:16:38Z vboxsync $ */
2/** @file
3 * DevSmc - Apple System Manaagement 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-2015 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;
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
491 if (uOperation == SMC_CALLR0_READ_OSK)
492 {
493 rc = RTOnce(&g_SmcR0Once, devR0SmcInitOnce, NULL);
494 if ( RT_SUCCESS(rc)
495 && g_fHaveOsk)
496 {
497 AssertCompile(sizeof(g_abOsk0And1) + 1 == sizeof(pThis->szOsk0And1));
498 memcpy(pThis->szOsk0And1, g_abOsk0And1, sizeof(pThis->szOsk0And1) - 1);
499 pThis->szOsk0And1[sizeof(pThis->szOsk0And1) - 1] = '\0';
500 }
501 }
502 return rc;
503}
504
505#endif /* IN_RING0 */
506
507#if defined(IN_RING3) && defined(RT_OS_DARWIN)
508
509/**
510 * Preferred method to retrieve the SMC key.
511 *
512 * @param pabKey where to store the key.
513 * @param cbKey size of the buffer.
514 */
515static int getSmcKeyOs(char *pabKey, uint32_t cbKey)
516{
517 /*
518 * Method as described in Amit Singh's article:
519 * http://osxbook.com/book/bonus/chapter7/tpmdrmmyth/
520 */
521 typedef struct
522 {
523 uint32_t key;
524 uint8_t pad0[22];
525 uint32_t datasize;
526 uint8_t pad1[10];
527 uint8_t cmd;
528 uint32_t pad2;
529 uint8_t data[32];
530 } AppleSMCBuffer;
531
532 AssertReturn(cbKey >= 65, VERR_INTERNAL_ERROR);
533
534 io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault,
535 IOServiceMatching("AppleSMC"));
536 if (!service)
537 return VERR_NOT_FOUND;
538
539 io_connect_t port = (io_connect_t)0;
540 kern_return_t kr = IOServiceOpen(service, mach_task_self(), 0, &port);
541 IOObjectRelease(service);
542
543 if (kr != kIOReturnSuccess)
544 return RTErrConvertFromDarwin(kr);
545
546 AppleSMCBuffer inputStruct = { 0, {0}, 32, {0}, 5, };
547 AppleSMCBuffer outputStruct;
548 size_t cbOutputStruct = sizeof(outputStruct);
549
550 for (int i = 0; i < 2; i++)
551 {
552 inputStruct.key = (uint32_t)(i == 0 ? 'OSK0' : 'OSK1');
553 kr = IOConnectCallStructMethod((mach_port_t)port,
554 (uint32_t)2,
555 (const void *)&inputStruct,
556 sizeof(inputStruct),
557 (void *)&outputStruct,
558 &cbOutputStruct);
559 if (kr != kIOReturnSuccess)
560 {
561 IOServiceClose(port);
562 return RTErrConvertFromDarwin(kr);
563 }
564
565 for (int j = 0; j < 32; j++)
566 pabKey[j + i*32] = outputStruct.data[j];
567 }
568
569 IOServiceClose(port);
570
571 pabKey[64] = 0;
572
573 return VINF_SUCCESS;
574}
575
576#endif /* IN_RING3 && RT_OS_DARWIN */
577
578#ifdef IN_RING3 /* For now. */
579
580/** @callback_method_impl{DEVSMCKEYGETTER, OSK0 and OSK1} */
581static uint8_t scmKeyGetOSKs(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
582{
583 Assert(SMC4CH_EQ(&pKeyDesc->Key, "OSK0") || SMC4CH_EQ(&pKeyDesc->Key, "OSK1"));
584 const char *pszSrc = pThis->szOsk0And1;
585 if (SMC4CH_EQ(&pKeyDesc->Key, "OSK1"))
586 pszSrc += 32;
587 memcpy(pCurKey->Value.ab, pszSrc, 32);
588 return SMC_STATUS_CD_SUCCESS;
589}
590
591
592/** @callback_method_impl{DEVSMCKEYGETTER, \#KEY} */
593static uint8_t scmKeyGetKeyCount(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
594{
595 Assert(pKeyDesc == &g_aSmcKeys[SMC_KEYIDX_FIRST_ENUM]);
596 uint32_t cKeys = RT_ELEMENTS(g_aSmcKeys) - SMC_KEYIDX_FIRST_ENUM;
597 pCurKey->Value.u32 = RT_H2BE_U32(cKeys);
598 return SMC_STATUS_CD_SUCCESS;
599}
600
601
602/** @callback_method_impl{DEVSMCKEYGETTER, REV - Source revision.} */
603static uint8_t scmKeyGetRevision(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
604{
605#ifdef VBOX_WITH_SMC_2_x
606 pCurKey->Value.ab[0] = 0x02;
607 pCurKey->Value.ab[1] = 0x03;
608 pCurKey->Value.ab[2] = 0x0f;
609 pCurKey->Value.ab[3] = 0x00;
610 pCurKey->Value.ab[4] = 0x00;
611 pCurKey->Value.ab[5] = 0x35;
612#else
613 pCurKey->Value.ab[0] = 0x01;
614 pCurKey->Value.ab[1] = 0x25;
615 pCurKey->Value.ab[2] = 0x0f;
616 pCurKey->Value.ab[3] = 0x00;
617 pCurKey->Value.ab[4] = 0x00;
618 pCurKey->Value.ab[5] = 0x04;
619#endif
620 return SMC_STATUS_CD_SUCCESS;
621}
622
623#ifdef VBOX_WITH_SMC_2_x
624
625/** @callback_method_impl{DEVSMCKEYGETTER, $Adr - SMC address.} */
626static uint8_t scmKeyGetDollarAddress(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
627{
628 pCurKey->Value.u32 = RT_H2BE_U32(SMC_PORT_FIRST);
629 return VINF_SUCCESS;
630}
631
632
633/** @callback_method_impl{DEVSMCKEYGETTER, $Num - Some kind of number.} */
634static uint8_t scmKeyGetDollarNumber(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
635{
636 pCurKey->Value.ab[0] = pThis->bDollaryNumber;
637 return VINF_SUCCESS;
638}
639
640/** @callback_method_impl{DEVSMCKEYPUTTER, $Num - Some kind of number.} */
641static uint8_t scmKeyPutDollarNumber(PDEVSMC pThis, PCDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
642{
643 Log(("scmKeyPutDollarNumber: %#x -> %#x\n", pThis->bDollaryNumber, pCurKey->Value.ab[0]));
644 pThis->bDollaryNumber = pCurKey->Value.ab[0];
645 return VINF_SUCCESS;
646}
647
648#endif /* VBOX_WITH_SMC_2_x */
649
650/** @callback_method_impl{DEVSMCKEYGETTER, MSSD - Machine Shutdown reason.} */
651static uint8_t scmKeyGetShutdownReason(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
652{
653 pCurKey->Value.ab[0] = pThis->bShutdownReason;
654 return SMC_STATUS_CD_SUCCESS;
655}
656
657
658/** @callback_method_impl{DEVSMCKEYPUTTER, MSSD - Machine Shutdown reason.} */
659static uint8_t scmKeyPutShutdownReason(PDEVSMC pThis, PCDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
660{
661 Log(("scmKeyPutShutdownReason: %#x -> %#x\n", pThis->bShutdownReason, pCurKey->Value.ab[0]));
662 pThis->bShutdownReason = pCurKey->Value.ab[0];
663 return SMC_STATUS_CD_SUCCESS;
664}
665
666
667/** @callback_method_impl{DEVSMCKEYGETTER, MSSD - Ninja timer action job.} */
668static uint8_t scmKeyGetNinjaTimerAction(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
669{
670 pCurKey->Value.ab[0] = pThis->bNinjaActionTimerJob;
671 return SMC_STATUS_CD_SUCCESS;
672}
673
674
675/** @callback_method_impl{DEVSMCKEYPUTTER, NATJ - Ninja timer action job.} */
676static uint8_t scmKeyPutNinjaTimerAction(PDEVSMC pThis, PCDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
677{
678 Log(("scmKeyPutNinjaTimerAction: %#x -> %#x\n", pThis->bNinjaActionTimerJob, pCurKey->Value.ab[0]));
679 pThis->bNinjaActionTimerJob = pCurKey->Value.ab[0];
680 return SMC_STATUS_CD_SUCCESS;
681}
682
683#ifdef VBOX_WITH_SMC_2_x
684
685/** @callback_method_impl{DEVSMCKEYGETTER, Generic one getter.} */
686static uint8_t scmKeyGetOne(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
687{
688 memset(&pCurKey->Value.ab[0], 0, pKeyDesc->cbValue);
689 pCurKey->Value.ab[pKeyDesc->cbValue - 1] = 1;
690 return SMC_STATUS_CD_SUCCESS;
691}
692
693#endif /* VBOX_WITH_SMC_2_x */
694
695/** @callback_method_impl{DEVSMCKEYGETTER, Generic zero getter.} */
696static uint8_t scmKeyGetZero(PDEVSMC pThis, PDEVSMCCURKEY pCurKey, uint8_t bCmd, PCDEVSMCKEYDESC pKeyDesc)
697{
698 memset(&pCurKey->Value.ab[0], 0, pKeyDesc->cbValue);
699 return SMC_STATUS_CD_SUCCESS;
700}
701
702
703/**
704 * Looks up a key and copies its value and attributes into the CurKey.
705 *
706 * @returns Key index on success, UINT32_MAX on failure.
707 * @param pThis The SMC instance data.
708 * @param uKeyValue The key value (DEVSMC4CHID.u32).
709 */
710static uint32_t smcKeyLookup(PDEVSMC pThis, uint32_t uKeyValue)
711{
712 uint32_t iKey = RT_ELEMENTS(g_aSmcKeys);
713 while (iKey-- > 0)
714 if (g_aSmcKeys[iKey].Key.u32 == uKeyValue)
715 return iKey;
716 return UINT32_MAX;
717}
718
719
720/**
721 * Looks up a key and copies its value and attributes into the CurKey.
722 *
723 * @returns Apple SMC Status Code.
724 * @param pThis The SMC instance data.
725 */
726static uint8_t smcKeyGetByName(PDEVSMC pThis)
727{
728 uint8_t bRc;
729#ifdef LOG_ENABLED
730 uint32_t const uKeyValueLog = RT_H2LE_U32(pThis->CurKey.Key.u32);
731#endif
732 uint32_t iKey = smcKeyLookup(pThis, pThis->CurKey.Key.u32);
733 if (iKey != UINT32_MAX)
734 {
735 if ( g_aSmcKeys[iKey].cbValue == pThis->CurKey.cbValue
736 || !g_aSmcKeys[iKey].cbValue)
737 {
738 pThis->CurKey.Type = g_aSmcKeys[iKey].Type;
739 pThis->CurKey.fAttr = g_aSmcKeys[iKey].fAttr;
740 RT_ZERO(pThis->CurKey.Value);
741 if (g_aSmcKeys[iKey].pfnGet)
742 {
743 bRc = g_aSmcKeys[iKey].pfnGet(pThis, &pThis->CurKey, pThis->bCmd, &g_aSmcKeys[iKey]);
744 if (bRc == SMC_STATUS_CD_SUCCESS)
745 {
746 LogFlow(("smcKeyGetByName: key=%4.4s value=%.*Rhxs\n",
747 &uKeyValueLog, pThis->CurKey.cbValue, &pThis->CurKey.Value));
748 return SMC_STATUS_CD_SUCCESS;
749 }
750
751 Log(("smcKeyGetByName: key=%4.4s getter failed! bRc=%#x\n", &uKeyValueLog, bRc));
752 }
753 else
754 {
755 Log(("smcKeyGetByName: key=%4.4s is not readable!\n", &uKeyValueLog));
756 bRc = SMC_STATUS_CD_KEY_NOT_READABLE;
757 }
758 }
759 else
760 {
761 Log(("smcKeyGetByName: Wrong value size; user=%#x smc=%#x key=%4.4s !\n",
762 pThis->CurKey.cbValue, g_aSmcKeys[iKey].cbValue, &uKeyValueLog));
763 bRc = SMC_STATUS_CD_KEY_SIZE_MISMATCH;
764 }
765 }
766 else
767 {
768 Log(("smcKeyGetByName: Key not found! key=%4.4s size=%#x\n", &uKeyValueLog, pThis->CurKey.cbValue));
769 bRc = SMC_STATUS_CD_KEY_NOT_FOUND;
770 }
771
772 RT_ZERO(pThis->CurKey);
773 return bRc;
774}
775
776
777/**
778 * Looks up a key by index and copies its name (and attributes) into the CurKey.
779 *
780 * @returns Apple SMC Status Code.
781 * @param pThis The SMC instance data.
782 */
783static uint8_t smcKeyGetByIndex(PDEVSMC pThis)
784{
785 uint8_t bRc;
786 uint32_t iKey = RT_BE2H_U32(pThis->CurKey.Key.u32);
787 if (iKey < RT_ELEMENTS(g_aSmcKeys) - SMC_KEYIDX_FIRST_ENUM)
788 {
789 pThis->CurKey.Key = g_aSmcKeys[iKey].Key;
790 pThis->CurKey.Type = g_aSmcKeys[iKey].Type;
791 pThis->CurKey.fAttr = g_aSmcKeys[iKey].fAttr;
792 pThis->CurKey.cbValue = g_aSmcKeys[iKey].cbValue;
793 RT_ZERO(pThis->CurKey.Value);
794 Log(("smcKeyGetByIndex: %#x -> %c%c%c%c\n", iKey,
795 pThis->CurKey.Key.ab[3], pThis->CurKey.Key.ab[2], pThis->CurKey.Key.ab[1], pThis->CurKey.Key.ab[0]));
796 bRc = SMC_STATUS_CD_SUCCESS;
797 }
798 else
799 {
800 Log(("smcKeyGetByIndex: Key out or range: %#x, max %#x\n", iKey, RT_ELEMENTS(g_aSmcKeys) - SMC_KEYIDX_FIRST_ENUM));
801 bRc = SMC_STATUS_CD_KEY_NOT_FOUND;
802 }
803 return bRc;
804}
805
806
807/**
808 * Looks up a key by index and copies its attributes into the CurKey.
809 *
810 * @returns Apple SMC Status Code.
811 * @param pThis The SMC instance data.
812 */
813static uint8_t smcKeyGetAttrByName(PDEVSMC pThis)
814{
815 uint8_t bRc;
816#ifdef LOG_ENABLED
817 uint32_t const uKeyValueLog = RT_H2LE_U32(pThis->CurKey.Key.u32);
818#endif
819 uint32_t iKey = smcKeyLookup(pThis, pThis->CurKey.Key.u32);
820 if (iKey != UINT32_MAX)
821 {
822 pThis->CurKey.Type = g_aSmcKeys[iKey].Type;
823 pThis->CurKey.fAttr = g_aSmcKeys[iKey].fAttr;
824 pThis->CurKey.cbValue = g_aSmcKeys[iKey].cbValue;
825 RT_ZERO(pThis->CurKey.Value);
826 if (g_aSmcKeys[iKey].cbValue)
827 bRc = SMC_STATUS_CD_SUCCESS;
828 else
829 bRc = g_aSmcKeys[iKey].pfnGet(pThis, &pThis->CurKey, pThis->bCmd, &g_aSmcKeys[iKey]);
830 if (bRc == SMC_STATUS_CD_SUCCESS)
831 {
832 LogFlow(("smcKeyGetAttrByName: key=%4.4s value=%.*Rhxs\n",
833 &uKeyValueLog, pThis->CurKey.cbValue, &pThis->CurKey.Value));
834 return SMC_STATUS_CD_SUCCESS;
835 }
836
837 Log(("smcKeyGetAttrByName: key=%4.4s getter failed! bRc=%#x\n", &uKeyValueLog, bRc));
838 }
839 else
840 {
841 Log(("smcKeyGetAttrByName: Key not found! key=%4.4s size=%#x\n", &uKeyValueLog, pThis->CurKey.cbValue));
842 bRc = SMC_STATUS_CD_KEY_NOT_FOUND;
843 }
844
845 RT_ZERO(pThis->CurKey);
846 return bRc;
847}
848
849
850static uint8_t smcKeyPutPrepare(PDEVSMC pThis)
851{
852 return 0;
853}
854
855static uint8_t smcKeyPutValue(PDEVSMC pThis)
856{
857 return 0;
858}
859
860
861/**
862 * Data register read.
863 *
864 * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
865 * @param uReg The register number.
866 * @param pbValue Where to return the value.
867 */
868static int smcRegData_r(PDEVSMC pThis, uint8_t uReg, uint8_t *pbValue)
869{
870 switch (pThis->bCmd)
871 {
872 case SMC_CMD_GET_KEY_VALUE:
873 if ( pThis->u.s.bState == 0x05
874 && pThis->offValue < pThis->CurKey.cbValue)
875 {
876 *pbValue = pThis->CurKey.Value.ab[pThis->offValue];
877 if (++pThis->offValue >= pThis->CurKey.cbValue)
878 pThis->u.s.bState = 0x00;
879 pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
880 }
881 else
882 {
883 Log(("smcRegData_r: Reading too much or at wrong time during SMC_CMD_GET_KEY_INFO! bState=%#x offValue=%#x\n",
884 pThis->u.s.bState, pThis->offValue));
885 pThis->u.s.bState = 0x00;
886 pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA; /** @todo check status code */
887 }
888 break;
889
890 case SMC_CMD_GET_KEY_INFO:
891 if ( pThis->u.s.bState == 0x05
892 && pThis->offValue < 6)
893 {
894 if (pThis->offValue == 0)
895 *pbValue = pThis->CurKey.cbValue;
896 else if (pThis->offValue < 1 + 4)
897 *pbValue = pThis->CurKey.Type.ab[pThis->offValue - 1];
898 else
899 *pbValue = pThis->CurKey.fAttr;
900 if (++pThis->offValue >= 6)
901 pThis->u.s.bState = 0x00;
902 pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
903 }
904 else
905 {
906 Log(("smcRegData_r: Reading too much or at wrong time during SMC_CMD_GET_KEY_INFO! bState=%#x offValue=%#x\n",
907 pThis->u.s.bState, pThis->offValue));
908 pThis->u.s.bState = 0x00;
909 pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA; /** @todo check status code */
910 }
911 break;
912
913 case SMC_CMD_GET_KEY_BY_INDEX:
914 if ( pThis->u.s.bState == 0x05
915 && pThis->offValue < sizeof(pThis->CurKey.Key))
916 {
917 *pbValue = pThis->CurKey.Key.ab[pThis->offValue];
918 if (++pThis->offValue >= sizeof(pThis->CurKey.Key))
919 pThis->u.s.bState = 0x00;
920 pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
921 }
922 else
923 {
924 Log(("smcRegData_r: Reading too much or at wrong time during GET_KEY_BY_INDEX! bState=%#x offValue=%#x\n",
925 pThis->u.s.bState, pThis->offValue));
926 pThis->u.s.bState = 0x00;
927 pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA; /** @todo check status code */
928 }
929 break;
930
931 case SMC_CMD_PUT_KEY:
932 Log(("smcRegData_r: Attempting to read data during PUT_KEY!\n"));
933 *pbValue = 0xff;
934 pThis->u.s.bState = 0;
935 pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA;
936 break;
937
938 default:
939 Log(("smcRegData_r: Unknown command attempts reading data\n"));
940 *pbValue = 0xff;
941 pThis->u.s.bState = 0;
942 pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA;
943 break;
944 }
945
946 return VINF_SUCCESS;
947}
948
949
950/**
951 * Data register write.
952 *
953 * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
954 * @param uReg The register number.
955 * @param bValue The value being written.
956 */
957static int smcRegData_w(PDEVSMC pThis, uint8_t uReg, uint8_t bValue)
958{
959 switch (pThis->bCmd)
960 {
961 /*
962 * Get or put key value.
963 *
964 * 5 bytes written, first 4 is the key the 5th is the value size. In
965 * the case of a put the value bytes are then written, while a get will
966 * read the value bytes.
967 */
968 case SMC_CMD_GET_KEY_VALUE:
969 case SMC_CMD_PUT_KEY:
970 if (pThis->offKey < 4)
971 {
972 /* Key byte. */
973 pThis->CurKey.Key.ab[pThis->offKey++] = bValue;
974 pThis->u.s.bState = 0x04;
975 pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
976 }
977 else if (pThis->offKey == 4)
978 {
979 /* Data length. */
980 pThis->u.s.bState = 0;
981 if (bValue <= sizeof(pThis->CurKey.Value))
982 {
983 pThis->CurKey.cbValue = bValue;
984 pThis->offKey = 5;
985 Assert(pThis->offValue == 0);
986
987 if (pThis->bCmd == SMC_CMD_GET_KEY_VALUE)
988 pThis->u.s.bStatusCode = smcKeyGetByName(pThis);
989 else
990 pThis->u.s.bStatusCode = smcKeyPutPrepare(pThis);
991 if (pThis->u.s.bStatusCode == SMC_STATUS_CD_SUCCESS)
992 pThis->u.s.bState = 0x05;
993 }
994 else
995 {
996 Log(("smcRegData_w: Guest attempts to get/put too many value bytes: %#x (max %#x)!\n",
997 bValue, sizeof(pThis->CurKey.Value)));
998 pThis->u.s.bStatusCode = SMC_STATUS_CD_KEY_SIZE_MISMATCH; /** @todo check this case! */
999 }
1000 }
1001 else if ( pThis->bCmd == SMC_CMD_PUT_KEY
1002 && pThis->offValue < pThis->CurKey.cbValue)
1003 {
1004 /* More value bytes for put key action. */
1005 pThis->CurKey.Value.ab[pThis->offValue++] = bValue;
1006 if (pThis->offValue != pThis->CurKey.cbValue)
1007 pThis->u.s.bState = 0x05;
1008 else
1009 {
1010 pThis->u.s.bState = 0x00;
1011 pThis->u.s.bStatusCode = smcKeyPutValue(pThis);
1012 }
1013 }
1014 else
1015 {
1016 Log(("smcRegData_w: Writing too much data on %s command!\n", pThis->bCmd == SMC_CMD_PUT_KEY ? "put" : "get"));
1017 pThis->u.s.bState = 0x00;
1018 pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA;
1019 }
1020 break;
1021
1022 /*
1023 * Get key info and key by index seems to take action after the last
1024 * key char is written. They then both go into a data reading phase.
1025 */
1026 case SMC_CMD_GET_KEY_INFO:
1027 case SMC_CMD_GET_KEY_BY_INDEX:
1028 if (pThis->offKey < 4)
1029 {
1030 pThis->CurKey.Key.ab[pThis->offKey] = bValue;
1031 if (++pThis->offKey == 4)
1032 {
1033 if (pThis->bCmd == SMC_CMD_GET_KEY_BY_INDEX)
1034 pThis->u.s.bStatusCode = smcKeyGetByIndex(pThis);
1035 else
1036 pThis->u.s.bStatusCode = smcKeyGetAttrByName(pThis);
1037 pThis->u.s.bState = pThis->u.s.bStatusCode == SMC_STATUS_CD_SUCCESS ? 0x05 : 0x00;
1038 }
1039 else
1040 {
1041 pThis->u.s.bState = 0x04;
1042 pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
1043 }
1044 }
1045 else
1046 {
1047 Log(("smcRegData_w: Writing data beyond 5th byte on get %s command!\n",
1048 pThis->bCmd == SMC_CMD_GET_KEY_INFO ? "info" : "by index"));
1049 pThis->u.s.bState = 0x00;
1050 pThis->u.s.bStatusCode = SMC_STATUS_CD_SPURIOUS_DATA;
1051 }
1052 break;
1053
1054 default:
1055 Log(("smcRegData_w: Unknown command %#x!\n", bValue));
1056 pThis->u.s.bState = 0x00; /** @todo Check statuses with real HW. */
1057 pThis->u.s.bStatusCode = SMC_STATUS_CD_BAD_COMMAND;
1058 break;
1059 }
1060 return VINF_SUCCESS;
1061}
1062
1063
1064/**
1065 * Command register write.
1066 *
1067 * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
1068 * @param uReg The register number.
1069 * @param bValue The value being written.
1070 */
1071static int smcRegCmd_w(PDEVSMC pThis, uint8_t uReg, uint8_t bValue)
1072{
1073 LogFlow(("smcRegCmd_w: New command: %#x (old=%#x)\n", bValue, pThis->bCmd)); NOREF(uReg);
1074
1075 pThis->bCmd = bValue;
1076
1077 /* Validate the command. */
1078 switch (bValue)
1079 {
1080 case SMC_CMD_GET_KEY_VALUE:
1081 case SMC_CMD_PUT_KEY:
1082 case SMC_CMD_GET_KEY_BY_INDEX:
1083 case SMC_CMD_GET_KEY_INFO:
1084 pThis->u.s.bState = 0x0c;
1085 pThis->u.s.bStatusCode = SMC_STATUS_CD_SUCCESS;
1086 break;
1087
1088 default:
1089 Log(("SMC: Unknown command %#x!\n", bValue));
1090 pThis->u.s.bState = 0x00; /** @todo Check state with real HW. */
1091 pThis->u.s.bStatusCode = SMC_STATUS_CD_BAD_COMMAND;
1092 break;
1093 }
1094
1095 /* Reset the value/key related state. */
1096 pThis->offKey = 0;
1097 pThis->offValue = 0;
1098 pThis->CurKey.Key.u32 = 0;
1099 pThis->CurKey.cbValue = 0;
1100
1101 return VINF_SUCCESS;
1102}
1103
1104
1105/**
1106 * Generic register write.
1107 *
1108 * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
1109 * @param uReg The register number.
1110 * @param bValue The value being written.
1111 */
1112static int smcRegGen_w(PDEVSMC pThis, uint8_t uReg, uint8_t bValue)
1113{
1114 Log(("smcRegGen_w: %#04x: %#x -> %#x (write)\n", uReg, pThis->u.abRegsRW[uReg], bValue));
1115 pThis->u.abRegsRW[uReg] = bValue;
1116 return VINF_SUCCESS;
1117}
1118
1119
1120/**
1121 * Read from register that isn't writable and reads as 0xFF.
1122 *
1123 * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
1124 * @param uReg The register number.
1125 * @param pbValue Where to return the value.
1126 */
1127static int smcRegGen_r(PDEVSMC pThis, uint8_t uReg, uint8_t *pbValue)
1128{
1129 Log(("smcRegGen_r: %#04x: %#x (read)\n", uReg, pThis->u.abRegsRW[uReg]));
1130 *pbValue = pThis->u.abRegsRW[uReg];
1131 return VINF_SUCCESS;
1132}
1133
1134
1135/**
1136 * Write to register that isn't writable and reads as 0xFF.
1137 *
1138 * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
1139 * @param uReg The register number.
1140 * @param bValue The value being written.
1141 */
1142static int smcRegFF_w(PDEVSMC pThis, uint8_t uReg, uint8_t bValue)
1143{
1144 Log(("SMC: %#04x: Writing %#x to unknown register!\n", uReg, bValue));
1145 return VINF_SUCCESS;
1146}
1147
1148
1149/**
1150 * Read from register that isn't writable and reads as 0xFF.
1151 *
1152 * @returns VINF_SUCCESS or VINF_IOM_R3_IOPORT_WRITE.
1153 * @param uReg The register number.
1154 * @param pbValue Where to return the value.
1155 */
1156static int smcRegFF_r(PDEVSMC pThis, uint8_t uReg, uint8_t *pbValue)
1157{
1158 Log(("SMC: %#04x: Reading from unknown register!\n", uReg));
1159 *pbValue = 0xff;
1160 return VINF_SUCCESS;
1161}
1162
1163
1164
1165/**
1166 * SMC register handlers (indexed by relative I/O port).
1167 *
1168 * The device seems to be all byte registers and will split wider
1169 * accesses between registers like if it was MMIO. To better illustrate it
1170 * here is the output of the code in devR0SmcInitOnce on a MacPro3,1:
1171 * @verbatim
1172 * SMC: 0x0300=0xffffff63 w={0xff63, 0xffff}, b={0x63 0xff 0xff 0xff}
1173 * SMC: 0x0301=0x0cffffff w={0xffff, 0x0cff}, b={0xff 0xff 0xff 0x0c}
1174 * SMC: 0x0302=0xff0cffff w={0xffff, 0xff0c}, b={0xff 0xff 0x0c 0xff}
1175 * SMC: 0x0303=0xffff0cff w={0x0cff, 0xffff}, b={0xff 0x0c 0xff 0xff}
1176 * SMC: 0x0304=0xffffff0c w={0xff0c, 0xffff}, b={0x0c 0xff 0xff 0xff}
1177 * SMC: 0x0305=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1178 * SMC: 0x0306=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1179 * SMC: 0x0307=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1180 * SMC: 0x0308=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1181 * SMC: 0x0309=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1182 * SMC: 0x030a=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1183 * SMC: 0x030b=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1184 * SMC: 0x030c=0xffffffff w={0xffff, 0xffff}, b={0xff 0xff 0xff 0xff}
1185 * SMC: 0x030d=0x00ffffff w={0xffff, 0x00ff}, b={0xff 0xff 0xff 0x00}
1186 * SMC: 0x030e=0x0000ffff w={0xffff, 0x0000}, b={0xff 0xff 0x00 0x00}
1187 * SMC: 0x030f=0x000000ff w={0x00ff, 0x0000}, b={0xff 0x00 0x00 0x00}
1188 * SMC: 0x0310=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1189 * SMC: 0x0311=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1190 * SMC: 0x0312=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1191 * SMC: 0x0313=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1192 * SMC: 0x0314=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1193 * SMC: 0x0315=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1194 * SMC: 0x0316=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1195 * SMC: 0x0317=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1196 * SMC: 0x0318=0x00000000 w={0x0000, 0x0000}, b={0x00 0x00 0x00 0x00}
1197 * SMC: 0x0319=0xbe000000 w={0x0000, 0xbe00}, b={0x00 0x00 0x00 0xbe}
1198 * SMC: 0x031a=0xbabe0000 w={0x0000, 0xbabe}, b={0x00 0x00 0xbe 0xba}
1199 * SMC: 0x031b=0x00babe00 w={0xbe00, 0x00ba}, b={0x00 0xbe 0xba 0x00}
1200 * SMC: 0x031c=0xbe00babe w={0xbabe, 0xbe00}, b={0xbe 0xba 0x00 0xbe}
1201 * SMC: 0x031d=0xffbe00ba w={0x00ba, 0xffbe}, b={0xba 0x00 0xbe 0xff}
1202 * SMC: 0x031e=0xffffbe00 w={0xbe00, 0xffff}, b={0x00 0xbe 0xff 0xff}
1203 * SMC: 0x031f=0xffffffbe w={0xffbe, 0xffff}, b={0xbe 0xff 0xff 0xff}
1204 * @endverbatim
1205 *
1206 * The last dword is writable (0xbeXXbabe) where in the register at 0x1e is some
1207 * kind of status register for qualifying search failures and the like and will
1208 * be cleared under certain conditions. The whole dword can be written and read
1209 * back unchanged, according to my experiments. The 0x00 and 0x04 registers
1210 * does not read back what is written.
1211 *
1212 * My guess is that the 0xff values indicates ports that are not writable and
1213 * hardwired to 0xff, while the other values indicates ports that can be written
1214 * to and normally read back as written. I'm not going to push my luck too far
1215 * wrt to exact behavior until I see the guest using the registers.
1216 */
1217static const struct
1218{
1219 int (*pfnWrite)(PDEVSMC pThis, uint8_t uReg, uint8_t bValue);
1220 int (*pfnRead)(PDEVSMC pThis, uint8_t uReg, uint8_t *pbValue);
1221} g_aSmcRegs[SMC_REG_COUNT] =
1222{
1223 /* [0x00] = */ { smcRegData_w, smcRegData_r },
1224 /* [0x01] = */ { smcRegFF_w, smcRegFF_r },
1225 /* [0x02] = */ { smcRegFF_w, smcRegFF_r },
1226 /* [0x03] = */ { smcRegFF_w, smcRegFF_r },
1227 /* [0x04] = */ { smcRegCmd_w, smcRegGen_r },
1228 /* [0x05] = */ { smcRegFF_w, smcRegFF_r },
1229 /* [0x06] = */ { smcRegFF_w, smcRegFF_r },
1230 /* [0x07] = */ { smcRegFF_w, smcRegFF_r },
1231 /* [0x08] = */ { smcRegFF_w, smcRegFF_r },
1232 /* [0x09] = */ { smcRegFF_w, smcRegFF_r },
1233 /* [0x0a] = */ { smcRegFF_w, smcRegFF_r },
1234 /* [0x0b] = */ { smcRegFF_w, smcRegFF_r },
1235 /* [0x0c] = */ { smcRegFF_w, smcRegFF_r },
1236 /* [0x0d] = */ { smcRegFF_w, smcRegFF_r },
1237 /* [0x0e] = */ { smcRegFF_w, smcRegFF_r },
1238 /* [0x0f] = */ { smcRegFF_w, smcRegFF_r },
1239 /* [0x10] = */ { smcRegGen_w, smcRegGen_r },
1240 /* [0x11] = */ { smcRegGen_w, smcRegGen_r },
1241 /* [0x12] = */ { smcRegGen_w, smcRegGen_r },
1242 /* [0x13] = */ { smcRegGen_w, smcRegGen_r },
1243 /* [0x14] = */ { smcRegGen_w, smcRegGen_r },
1244 /* [0x15] = */ { smcRegGen_w, smcRegGen_r },
1245 /* [0x16] = */ { smcRegGen_w, smcRegGen_r },
1246 /* [0x17] = */ { smcRegGen_w, smcRegGen_r },
1247 /* [0x18] = */ { smcRegGen_w, smcRegGen_r },
1248 /* [0x19] = */ { smcRegGen_w, smcRegGen_r },
1249 /* [0x1a] = */ { smcRegGen_w, smcRegGen_r },
1250 /* [0x1b] = */ { smcRegGen_w, smcRegGen_r },
1251 /* [0x1c] = */ { smcRegGen_w, smcRegGen_r },
1252 /* [0x1d] = */ { smcRegGen_w, smcRegGen_r },
1253 /* [0x1e] = */ { smcRegGen_w, smcRegGen_r },
1254 /* [0x1f] = */ { smcRegGen_w, smcRegGen_r },
1255};
1256
1257
1258/** @callback_method_impl{FNIOMIOPORTOUT} */
1259PDMBOTHCBDECL(int) smcIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
1260{
1261#ifndef IN_RING3
1262 if (cb > 1)
1263 return VINF_IOM_R3_IOPORT_WRITE;
1264#endif
1265
1266 /*
1267 * The first register, usually only one is accessed.
1268 */
1269 PDEVSMC pThis = PDMINS_2_DATA(pDevIns, PDEVSMC);
1270 uint32_t uReg = Port - SMC_PORT_FIRST;
1271 int rc = g_aSmcRegs[uReg].pfnWrite(pThis, uReg, u32);
1272
1273 /*
1274 * On the off chance that multiple registers are being read.
1275 */
1276 if (cb > 1)
1277 {
1278 while (cb > 1 && uReg < SMC_REG_COUNT - 1)
1279 {
1280 cb--;
1281 uReg++;
1282 u32 >>= 8;
1283 int rc2 = g_aSmcRegs[uReg].pfnWrite(pThis, uReg, u32);
1284 if (rc2 != VINF_SUCCESS)
1285 {
1286 if ( rc == VINF_SUCCESS
1287 || (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1288 || (rc2 < rc && RT_SUCCESS(rc2) && RT_SUCCESS(rc)))
1289 rc = rc2;
1290 }
1291 }
1292 }
1293
1294 LogFlow(("smcIoPortWrite: %#04x write access: %#x (LB %u) rc=%Rrc\n", uReg, u32, cb, rc));
1295 return rc;
1296}
1297
1298
1299/** @callback_method_impl{FNIOMIOPORTIN} */
1300PDMBOTHCBDECL(int) smcIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
1301{
1302#ifndef IN_RING3
1303 if (cb > 1)
1304 return VINF_IOM_R3_IOPORT_READ;
1305#endif
1306
1307 PDEVSMC pThis = PDMINS_2_DATA(pDevIns, PDEVSMC);
1308
1309 /*
1310 * The first register, usually only one is accessed.
1311 */
1312 uint32_t uReg = Port - SMC_PORT_FIRST;
1313 Log2(("smcIoPortRead: %#04x read access: LB %u\n", uReg, cb));
1314 uint8_t bValue = 0xff;
1315 int rc = g_aSmcRegs[uReg].pfnRead(pThis, uReg, &bValue);
1316 *pu32 = bValue;
1317
1318 /*
1319 * On the off chance that multiple registers are being read.
1320 */
1321 if (cb > 1)
1322 {
1323 do
1324 {
1325 cb--;
1326 uReg++;
1327 bValue = 0xff;
1328 if (uReg < SMC_REG_COUNT)
1329 {
1330 int rc2 = g_aSmcRegs[uReg].pfnRead(pThis, uReg, &bValue);
1331 if (rc2 != VINF_SUCCESS)
1332 {
1333 if ( rc == VINF_SUCCESS
1334 || (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1335 || (rc2 < rc && RT_SUCCESS(rc2) && RT_SUCCESS(rc)))
1336 rc = rc2;
1337 }
1338 }
1339 *pu32 |= (uint32_t)bValue << ((4 - cb) * 8);
1340 } while (cb > 1);
1341 }
1342 LogFlow(("smcIoPortRead: %#04x read access: %#x (LB %u) rc=%Rrc\n", uReg, *pu32, cb, rc));
1343 return rc;
1344}
1345
1346#endif /* IN_RING3 for now */
1347#ifdef IN_RING3
1348
1349/** @callback_method_impl{FNSSMDEVSAVEEXEC} */
1350static DECLCALLBACK(int) smcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1351{
1352 PDEVSMC pThis = PDMINS_2_DATA(pDevIns, PDEVSMC);
1353
1354 /** @todo */
1355
1356 return VINF_SUCCESS;
1357}
1358
1359
1360/** @callback_method_impl{FNSSMDEVLOADEXEC} */
1361static DECLCALLBACK(int) smcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1362{
1363 PDEVSMC pThis = PDMINS_2_DATA(pDevIns, PDEVSMC);
1364 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
1365
1366 /* Fend off unsupported versions. */
1367 if ( uVersion != SMC_SAVED_STATE_VERSION
1368 && uVersion != SMC_SAVED_STATE_VERSION_BAKA
1369 && uVersion != SMC_SAVED_STATE_VERSION_BAKA + 1)
1370 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1371
1372 /*
1373 * Do the actual restoring.
1374 */
1375 if (uVersion == SMC_SAVED_STATE_VERSION)
1376 {
1377 /** @todo */
1378 }
1379
1380 return VINF_SUCCESS;
1381}
1382
1383
1384/**
1385 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1386 */
1387static DECLCALLBACK(int) smcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1388{
1389 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1390 PDEVSMC pThis = PDMINS_2_DATA(pDevIns, PDEVSMC);
1391 Assert(iInstance == 0);
1392
1393 /*
1394 * Init the data.
1395 */
1396 pThis->bDollaryNumber = 1;
1397 pThis->bShutdownReason = 3; /* STOP_CAUSE_POWERKEY_GOOD_CODE */
1398
1399 /*
1400 * Validate configuration.
1401 */
1402 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DeviceKey|GetKeyFromRealSMC", "");
1403
1404 /*
1405 * Read configuration.
1406 */
1407
1408 /* The DeviceKey sets OSK0 and OSK1. */
1409 int rc = CFGMR3QueryStringDef(pCfg, "DeviceKey", pThis->szOsk0And1, sizeof(pThis->szOsk0And1), "");
1410 if (RT_FAILURE(rc))
1411 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1412 N_("Configuration error: Querying \"DeviceKey\" as a string failed"));
1413
1414 /* Query the key from the OS / real hardware if asked to do so. */
1415 bool fGetKeyFromRealSMC;
1416 rc = CFGMR3QueryBoolDef(pCfg, "GetKeyFromRealSMC", &fGetKeyFromRealSMC, false);
1417 if (RT_FAILURE(rc))
1418 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1419 N_("Configuration error: Querying \"GetKeyFromRealSMC\" as a boolean failed"));
1420 if (fGetKeyFromRealSMC)
1421 {
1422#ifdef RT_OS_DARWIN
1423 rc = getSmcKeyOs(pThis->szOsk0And1, sizeof(pThis->szOsk0And1));
1424 if (RT_FAILURE(rc))
1425 {
1426 LogRel(("SMC: Retrieving the SMC key from the OS failed (%Rrc), trying to read it from hardware\n", rc));
1427#endif
1428 rc = PDMDevHlpCallR0(pDevIns, SMC_CALLR0_READ_OSK, 0 /*u64Arg*/);
1429 if (RT_SUCCESS(rc))
1430 LogRel(("SMC: Successfully retrieved the SMC key from hardware\n"));
1431 else
1432 LogRel(("SMC: Retrieving the SMC key from hardware failed(%Rrc)\n", rc));
1433#ifdef RT_OS_DARWIN
1434 }
1435 else
1436 LogRel(("SMC: Successfully retrieved the SMC key from the OS\n"));
1437#endif
1438 if (RT_FAILURE(rc))
1439 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1440 N_("Failed to query SMC value from the host"));
1441 }
1442
1443 /*
1444 * Register I/O Ports
1445 */
1446 rc = PDMDevHlpIOPortRegister(pDevIns, SMC_PORT_FIRST, SMC_REG_COUNT, NULL,
1447 smcIoPortWrite, smcIoPortRead,
1448 NULL, NULL, "SMC data port");
1449 AssertRCReturn(rc, rc);
1450
1451 /** @todo Newer versions (2.03) have an MMIO mapping as well (ACPI). */
1452
1453
1454 /*
1455 * Saved state.
1456 */
1457 rc = PDMDevHlpSSMRegister(pDevIns, SMC_SAVED_STATE_VERSION, sizeof(*pThis), smcSaveExec, smcLoadExec);
1458 if (RT_FAILURE(rc))
1459 return rc;
1460
1461 return VINF_SUCCESS;
1462}
1463
1464
1465/**
1466 * The device registration structure.
1467 */
1468const PDMDEVREG g_DeviceSmc =
1469{
1470 /* u32Version */
1471 PDM_DEVREG_VERSION,
1472 /* szName */
1473 "smc",
1474 /* szRCMod */
1475 "VBoxDDRC.rc",
1476 /* szR0Mod */
1477 "VBoxDDR0.r0",
1478 /* pszDescription */
1479 "Apple System Management Controller",
1480 /* fFlags */
1481 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT | PDM_DEVREG_FLAGS_R0 | PDM_DEVREG_FLAGS_RC,
1482 /* fClass */
1483 PDM_DEVREG_CLASS_ARCH,
1484 /* cMaxInstances */
1485 1,
1486 /* cbInstance */
1487 sizeof(DEVSMC),
1488 /* pfnConstruct */
1489 smcConstruct,
1490 /* pfnDestruct */
1491 NULL,
1492 /* pfnRelocate */
1493 NULL,
1494 /* pfnMemSetup */
1495 NULL,
1496 /* pfnPowerOn */
1497 NULL,
1498 /* pfnReset */
1499 NULL,
1500 /* pfnSuspend */
1501 NULL,
1502 /* pfnResume */
1503 NULL,
1504 /* pfnAttach */
1505 NULL,
1506 /* pfnDetach */
1507 NULL,
1508 /* pfnQueryInterface. */
1509 NULL,
1510 /* pfnInitComplete. */
1511 NULL,
1512 /* pfnPowerOff */
1513 NULL,
1514 /* pfnSoftReset */
1515 NULL,
1516 /* u32VersionEnd */
1517 PDM_DEVREG_VERSION
1518};
1519
1520#endif /* IN_RING3 */
1521#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use