VirtualBox

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

Last change on this file since 52934 was 52508, checked in by vboxsync, 10 years ago

SMC structure tests.

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