VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/DevSMC.cpp@ 47631

Last change on this file since 47631 was 45025, checked in by vboxsync, 12 years ago

Update PDMDEVREG initialization comment so they refer to pfnMemSetup instead of pfnIOCtl.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.1 KB
Line 
1/* $Id: DevSMC.cpp 45025 2013-03-13 16:45:15Z vboxsync $ */
2/** @file
3 * DevSMC - SMC device emulation.
4 *
5 * @todo Rainy day: Rewrite from scratch!!
6 */
7
8/*
9 * Copyright (C) 2006-2012 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 * --------------------------------------------------------------------
19 *
20 * This code is based on:
21 *
22 * Apple SMC controller
23 *
24 * Copyright (c) 2007 Alexander Graf
25 *
26 * This library is free software; you can redistribute it and/or
27 * modify it under the terms of the GNU Lesser General Public
28 * License as published by the Free Software Foundation; either
29 * version 2 of the License, or (at your option) any later version.
30 *
31 * This library is distributed in the hope that it will be useful,
32 * but WITHOUT ANY WARRANTY; without even the implied warranty of
33 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
34 * Lesser General Public License for more details.
35 *
36 * You should have received a copy of the GNU Lesser General Public
37 * License along with this library; if not, write to the Free Software
38 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
39 *
40 * *****************************************************************
41 *
42 * In all Intel-based Apple hardware there is an SMC chip to control the
43 * backlight, fans and several other generic device parameters. It also
44 * contains the magic keys used to dongle Mac OS X to the device.
45 *
46 * This driver was mostly created by looking at the Linux AppleSMC driver
47 * implementation and does not support IRQ.
48 *
49 */
50
51/*******************************************************************************
52* Header Files *
53*******************************************************************************/
54#define LOG_GROUP LOG_GROUP_DEV_SMC
55#include <VBox/vmm/pdmdev.h>
56#include <VBox/log.h>
57#include <VBox/vmm/stam.h>
58#include <iprt/assert.h>
59#include <iprt/string.h>
60#ifdef IN_RING0
61# include <iprt/asm-amd64-x86.h>
62# include <iprt/once.h>
63# include <iprt/thread.h>
64#endif
65
66#include "VBoxDD2.h"
67
68
69/*******************************************************************************
70* Defined Constants And Macros *
71*******************************************************************************/
72/* data port used by Apple SMC */
73#define APPLESMC_DATA_PORT 0x300
74/* command/status port used by Apple SMC */
75#define APPLESMC_CMD_PORT 0x304
76#define APPLESMC_NR_PORTS 32 /* 0x300-0x31f */
77#define APPLESMC_MAX_DATA_LENGTH 32
78
79#define APPLESMC_READ_CMD 0x10
80#define APPLESMC_WRITE_CMD 0x11
81#define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12
82#define APPLESMC_GET_KEY_TYPE_CMD 0x13
83
84/** The version of the saved state. */
85#define SMC_SAVED_STATE_VERSION 1
86
87/** The ring-0 operation number that attempts to get OSK0 and OSK1 from the real
88 * SMC. */
89#define SMC_CALLR0_READ_OSK 1
90
91/*******************************************************************************
92* Structures and Typedefs *
93*******************************************************************************/
94typedef struct AppleSMCData
95{
96 uint8_t len;
97 const char *key;
98 const char *data;
99} AppleSMCData;
100
101
102typedef struct
103{
104 PPDMDEVINSR3 pDevIns;
105
106 uint8_t cmd;
107 uint8_t status;
108 uint8_t key[4];
109 uint8_t read_pos;
110 uint8_t data_len;
111 uint8_t data_pos;
112 uint8_t data[255];
113
114 /** The OSK0 value. This is currently only used in the constructor. */
115 uint8_t abOsk0[32];
116 /** The OSK1 value. This is currently only used in the constructor */
117 uint8_t abOsk1[32];
118} SMCState;
119
120/*******************************************************************************
121* Global Variables *
122*******************************************************************************/
123#ifdef IN_RING3
124static char osk[64];
125
126/* See http://www.mactel-linux.org/wiki/AppleSMC */
127static struct AppleSMCData data[] =
128{
129 {6, "REV ", "\0x01\0x13\0x0f\0x00\0x00\0x03"},
130 {32,"OSK0", osk },
131 {32,"OSK1", osk+32 },
132 {1, "NATJ", "\0" },
133 {1, "MSSP", "\0" },
134 {1, "MSSD", "\0x3" },
135 {1, "NTOK", "\0"},
136 {0, NULL, NULL }
137};
138#endif /* IN_RING3 */
139#ifdef IN_RING0
140/** Do once for the SMC ring-0 static data (g_abOsk0, g_abOsk1, g_fHaveOsk). */
141static RTONCE g_SmcR0Once = RTONCE_INITIALIZER;
142/** Indicates whether we've successfully queried the OSK* keys. */
143static bool g_fHaveOsk = false;
144/** The OSK0 value. */
145static uint8_t g_abOsk0[32];
146/** The OSK1 value. */
147static uint8_t g_abOsk1[32];
148#endif /* IN_RING0 */
149
150#ifndef VBOX_DEVICE_STRUCT_TESTCASE
151
152#ifdef IN_RING0
153
154/**
155 * Waits for the specified status on the host SMC.
156 *
157 * @returns success indicator.
158 * @param bStatus The desired status.
159 * @param pszWhat What we're currently doing. For the log.
160 */
161static bool devR0SmcWaitHostStatus(uint8_t bStatus, const char *pszWhat)
162{
163 uint8_t bCurStatus;
164 for (uint32_t cMsSleep = 1; cMsSleep <= 64; cMsSleep <<= 1)
165 {
166 RTThreadSleep(cMsSleep);
167 bCurStatus = ASMInU8(APPLESMC_CMD_PORT);
168 if ((bCurStatus & 0xf) == bStatus)
169 return true;
170 }
171
172 LogRel(("devR0Smc: %s: bCurStatus=%#x, wanted %#x.\n", pszWhat, bCurStatus, bStatus));
173 return false;
174}
175
176/**
177 * Reads a key by name from the host SMC.
178 *
179 * @returns success indicator.
180 * @param pszName The key name, must be exactly 4 chars long.
181 * @param pbBuf The output buffer.
182 * @param cbBuf The buffer size. Max 32 bytes.
183 */
184static bool devR0SmcQueryHostKey(const char *pszName, uint8_t *pbBuf, size_t cbBuf)
185{
186 Assert(strlen(pszName) == 4);
187 Assert(cbBuf <= 32);
188 Assert(cbBuf > 0);
189
190 /*
191 * Issue the READ command.
192 */
193 uint32_t cMsSleep = 1;
194 for (;;)
195 {
196 ASMOutU8(APPLESMC_CMD_PORT, APPLESMC_READ_CMD);
197 RTThreadSleep(cMsSleep);
198 uint8_t bCurStatus = ASMInU8(APPLESMC_CMD_PORT);
199 if ((bCurStatus & 0xf) == 0xc)
200 break;
201 cMsSleep <<= 1;
202 if (cMsSleep > 64)
203 {
204 LogRel(("devR0Smc: %s: bCurStatus=%#x, wanted %#x.\n", "cmd", bCurStatus, 0xc));
205 return false;
206 }
207 }
208
209 /*
210 * Send it the key.
211 */
212 for (unsigned off = 0; off < 4; off++)
213 {
214 ASMOutU8(APPLESMC_DATA_PORT, pszName[off]);
215 if (!devR0SmcWaitHostStatus(4, "key"))
216 return false;
217 }
218
219 /*
220 * The desired amount of output.
221 */
222 ASMOutU8(APPLESMC_DATA_PORT, (uint8_t)cbBuf);
223
224 /*
225 * Read the output.
226 */
227 for (size_t off = 0; off < cbBuf; off++)
228 {
229 if (!devR0SmcWaitHostStatus(5, off ? "data" : "len"))
230 return false;
231 pbBuf[off] = ASMInU8(APPLESMC_DATA_PORT);
232 }
233
234 return true;
235}
236
237/**
238 * RTOnce callback that initializes g_fHaveOsk, g_abOsk0 and g_abOsk1.
239 *
240 * @returns VINF_SUCCESS.
241 * @param pvUserIgnored Ignored.
242 */
243static DECLCALLBACK(int) devR0SmcInitOnce(void *pvUserIgnored)
244{
245 g_fHaveOsk = devR0SmcQueryHostKey("OSK0", &g_abOsk0[0], sizeof(g_abOsk0))
246 && devR0SmcQueryHostKey("OSK1", &g_abOsk1[0], sizeof(g_abOsk1));
247
248 NOREF(pvUserIgnored);
249 return VINF_SUCCESS;
250}
251
252/**
253 * @interface_method_impl{FNPDMDEVREQHANDLERR0}
254 */
255PDMBOTHCBDECL(int) devR0SmcReqHandler(PPDMDEVINS pDevIns, uint32_t uOperation, uint64_t u64Arg)
256{
257 SMCState *pThis = PDMINS_2_DATA(pDevIns, SMCState *);
258 int rc = VERR_INVALID_FUNCTION;
259
260 if (uOperation == SMC_CALLR0_READ_OSK)
261 {
262 rc = RTOnce(&g_SmcR0Once, devR0SmcInitOnce, NULL);
263 if ( RT_SUCCESS(rc)
264 && g_fHaveOsk)
265 {
266 AssertCompile(sizeof(g_abOsk0) == sizeof(pThis->abOsk0));
267 AssertCompile(sizeof(g_abOsk1) == sizeof(pThis->abOsk1));
268 memcpy(pThis->abOsk0, g_abOsk0, sizeof(pThis->abOsk0));
269 memcpy(pThis->abOsk1, g_abOsk1, sizeof(pThis->abOsk1));
270 }
271 }
272 return rc;
273}
274
275#endif /* IN_RING0 */
276#ifdef IN_RING3
277
278/**
279 * Saves a state of the SMC device.
280 *
281 * @returns VBox status code.
282 * @param pDevIns The device instance.
283 * @param pSSMHandle The handle to save the state to.
284 */
285static DECLCALLBACK(int) smcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
286{
287 SMCState *pThis = PDMINS_2_DATA(pDevIns, SMCState *);
288
289 /** @todo: implement serialization */
290 return VINF_SUCCESS;
291}
292
293
294/**
295 * Loads a SMC device state.
296 *
297 * @returns VBox status code.
298 * @param pDevIns The device instance.
299 * @param pSSMHandle The handle to the saved state.
300 * @param uVersion The data unit version number.
301 * @param uPass The data pass.
302 */
303static DECLCALLBACK(int) smcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t uVersion, uint32_t uPass)
304{
305 SMCState *pThis = PDMINS_2_DATA(pDevIns, SMCState *);
306
307 if (uVersion != SMC_SAVED_STATE_VERSION)
308 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
309 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
310
311 /** @todo: implement serialization */
312 return VINF_SUCCESS;
313}
314
315/**
316 * Reset notification.
317 *
318 * @returns VBox status.
319 * @param pDevIns The device instance data.
320 */
321static DECLCALLBACK(void) smcReset(PPDMDEVINS pDevIns)
322{
323 SMCState *pThis = PDMINS_2_DATA(pDevIns, SMCState *);
324 LogFlow(("smcReset: \n"));
325}
326
327
328/**
329 * Info handler, device version.
330 *
331 * @param pDevIns Device instance which registered the info.
332 * @param pHlp Callback functions for doing output.
333 * @param pszArgs Argument string. Optional and specific to the handler.
334 */
335static DECLCALLBACK(void) smcInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
336{
337 SMCState *pThis = PDMINS_2_DATA(pDevIns, SMCState *);
338}
339
340
341static void applesmc_fill_data(SMCState *s)
342{
343 struct AppleSMCData *d;
344 for (d=data; d->len; d++)
345 {
346 uint32_t key_data = *((uint32_t*)d->key);
347 uint32_t key_current = *((uint32_t*)s->key);
348 if (key_data == key_current)
349 {
350 Log(("APPLESMC: Key matched (%s Len=%d Data=%s)\n", d->key, d->len, d->data));
351 memcpy(s->data, d->data, d->len);
352 return;
353 }
354 }
355}
356
357/**
358 * Port I/O Handler for IN operations.
359 *
360 * @returns VBox status code.
361 *
362 * @param pDevIns The device instance.
363 * @param pvUser User argument - ignored.
364 * @param uPort Port number used for the IN operation.
365 * @param pu32 Where to store the result.
366 * @param cb Number of bytes read.
367 */
368PDMBOTHCBDECL(int) smcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
369{
370 SMCState * s = PDMINS_2_DATA(pDevIns, SMCState *);
371 uint8_t retval = 0;
372
373 NOREF(pvUser);
374 Log(("SMC port read: %x (%d)\n", Port, cb));
375
376 /** @todo: status code? */
377 if (cb != 1)
378 return VERR_IOM_IOPORT_UNUSED;
379
380 switch (Port)
381 {
382 case APPLESMC_CMD_PORT:
383 {
384 retval = s->status;
385 break;
386 }
387 case APPLESMC_DATA_PORT:
388 {
389 switch (s->cmd) {
390 case APPLESMC_READ_CMD:
391 if(s->data_pos < s->data_len)
392 {
393 retval = s->data[s->data_pos];
394 Log(("APPLESMC: READ_DATA[%d] = %#hhx\n", s->data_pos, retval));
395 s->data_pos++;
396 if(s->data_pos == s->data_len)
397 {
398 s->status = 0x00;
399 Log(("APPLESMC: EOF\n"));
400 }
401 else
402 s->status = 0x05;
403 }
404 }
405 break;
406 }
407 }
408
409 *pu32 = retval;
410
411 return VINF_SUCCESS;
412}
413
414
415/**
416 * Port I/O Handler for OUT operations.
417 *
418 * @returns VBox status code.
419 *
420 * @param pDevIns The device instance.
421 * @param pvUser User argument - ignored.
422 * @param uPort Port number used for the IN operation.
423 * @param u32 The value to output.
424 * @param cb The value size in bytes.
425 */
426PDMBOTHCBDECL(int) smcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
427{
428 SMCState* s = PDMINS_2_DATA(pDevIns, SMCState *);
429
430 NOREF(pvUser);
431
432 Log(("SMC port write: %x (%d) %x\n", Port, cb, u32));
433 /** @todo: status code? */
434 if (cb != 1)
435 return VINF_SUCCESS;
436
437 switch (Port)
438 {
439 case APPLESMC_CMD_PORT:
440 {
441 switch (u32)
442 {
443 case APPLESMC_READ_CMD:
444 s->status = 0x0c;
445 break;
446 }
447 s->cmd = u32;
448 s->read_pos = 0;
449 s->data_pos = 0;
450 break;
451 }
452 case APPLESMC_DATA_PORT:
453 {
454 switch(s->cmd)
455 {
456 case APPLESMC_READ_CMD:
457 if (s->read_pos < 4)
458 {
459 s->key[s->read_pos] = u32;
460 s->status = 0x04;
461 }
462 else
463 if (s->read_pos == 4)
464 {
465 s->data_len = u32;
466 s->status = 0x05;
467 s->data_pos = 0;
468 Log(("APPLESMC: Key = %c%c%c%c Len = %d\n", s->key[0], s->key[1], s->key[2], s->key[3], u32));
469 applesmc_fill_data(s);
470 }
471 s->read_pos++;
472 break;
473 }
474 }
475 }
476 return VINF_SUCCESS;
477}
478
479
480/**
481 * @interface_method_impl{PDMDEVREG,pfnConstruct}
482 */
483static DECLCALLBACK(int) smcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
484{
485 SMCState *pThis = PDMINS_2_DATA(pDevIns, SMCState *);
486 Assert(iInstance == 0);
487
488 /*
489 * Store state.
490 */
491 pThis->pDevIns = pDevIns;
492
493 /*
494 * Validate and read the configuration.
495 */
496 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DeviceKey|GetKeyFromRealSMC", "");
497
498 /*
499 * Read the DeviceKey config value.
500 */
501 char *pszDeviceKey;
502 int rc = CFGMR3QueryStringAllocDef(pCfg, "DeviceKey", &pszDeviceKey, "");
503 if (RT_FAILURE(rc))
504 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
505 N_("Configuration error: Querying \"DeviceKey\" as a string failed"));
506
507 size_t cchDeviceKey = strlen(pszDeviceKey);
508 if (cchDeviceKey > 0)
509 memcpy(&pThis->abOsk0[0], pszDeviceKey, RT_MIN(cchDeviceKey, sizeof(pThis->abOsk0)));
510 if (cchDeviceKey > sizeof(pThis->abOsk0))
511 memcpy(&pThis->abOsk1[0], &pszDeviceKey[sizeof(pThis->abOsk0)],
512 RT_MIN(cchDeviceKey - sizeof(pThis->abOsk0), sizeof(pThis->abOsk1)));
513
514 MMR3HeapFree(pszDeviceKey);
515
516 /*
517 * Query the key from the real hardware if asked to do so.
518 */
519 bool fGetKeyFromRealSMC;
520 rc = CFGMR3QueryBoolDef(pCfg, "GetKeyFromRealSMC", &fGetKeyFromRealSMC, false);
521 if (RT_FAILURE(rc))
522 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
523 N_("Configuration error: Querying \"GetKeyFromRealSMC\" as a boolean failed"));
524 if (fGetKeyFromRealSMC)
525 {
526 rc = PDMDevHlpCallR0(pDevIns, SMC_CALLR0_READ_OSK, 0 /*u64Arg*/);
527 if (RT_FAILURE(rc))
528 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
529 N_("Failed to query SMC value from the host"));
530 }
531
532 /*
533 * For practical/historical reasons, the OSK[0|1] data is stored in a
534 * global buffer in ring-3.
535 */
536 AssertCompile(sizeof(osk) == sizeof(pThis->abOsk0) + sizeof(pThis->abOsk1));
537 AssertCompile(sizeof(char) == sizeof(uint8_t));
538 memcpy(osk, pThis->abOsk0, sizeof(pThis->abOsk0));
539 memcpy(&osk[sizeof(pThis->abOsk0)], pThis->abOsk1, sizeof(pThis->abOsk1));
540
541 /*
542 * Register the IO ports.
543 */
544 rc = PDMDevHlpIOPortRegister(pDevIns, APPLESMC_DATA_PORT, 1, NULL,
545 smcIOPortWrite, smcIOPortRead,
546 NULL, NULL, "SMC Data");
547 if (RT_FAILURE(rc))
548 return rc;
549 rc = PDMDevHlpIOPortRegister(pDevIns, APPLESMC_CMD_PORT, 1, NULL,
550 smcIOPortWrite, smcIOPortRead,
551 NULL, NULL, "SMC Commands");
552 if (RT_FAILURE(rc))
553 return rc;
554
555 /* Register saved state management */
556 rc = PDMDevHlpSSMRegister(pDevIns, SMC_SAVED_STATE_VERSION, sizeof(*pThis), smcSaveExec, smcLoadExec);
557 if (RT_FAILURE(rc))
558 return rc;
559
560 /*
561 * Initialize the device state.
562 */
563 smcReset(pDevIns);
564
565 /**
566 * @todo: Register statistics.
567 */
568 PDMDevHlpDBGFInfoRegister(pDevIns, "smc", "Display SMC status. (no arguments)", smcInfo);
569
570 return VINF_SUCCESS;
571}
572
573/**
574 * The device registration structure.
575 */
576const PDMDEVREG g_DeviceSMC =
577{
578 /* u32Version */
579 PDM_DEVREG_VERSION,
580 /* szName */
581 "smc",
582 /* szRCMod */
583 "VBoxDD2GC.gc",
584 /* szR0Mod */
585 "VBoxDD2R0.r0",
586 /* pszDescription */
587 "System Management Controller (SMC) Device",
588 /* fFlags */
589 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36| PDM_DEVREG_FLAGS_R0,
590 /* fClass */
591 PDM_DEVREG_CLASS_MISC,
592 /* cMaxInstances */
593 1,
594 /* cbInstance */
595 sizeof(SMCState),
596 /* pfnConstruct */
597 smcConstruct,
598 /* pfnDestruct */
599 NULL,
600 /* pfnRelocate */
601 NULL,
602 /* pfnMemSetup */
603 NULL,
604 /* pfnPowerOn */
605 NULL,
606 /* pfnReset */
607 smcReset,
608 /* pfnSuspend */
609 NULL,
610 /* pfnResume */
611 NULL,
612 /* pfnAttach */
613 NULL,
614 /* pfnDetach */
615 NULL,
616 /* pfnQueryInterface. */
617 NULL,
618 /* pfnInitComplete */
619 NULL,
620 /* pfnPowerOff */
621 NULL,
622 /* pfnSoftReset */
623 NULL,
624 /* u32VersionEnd */
625 PDM_DEVREG_VERSION
626};
627
628#endif /* IN_RING3 */
629
630#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