VirtualBox

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

Last change on this file since 51287 was 48760, checked in by vboxsync, 11 years ago

build fix

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