VirtualBox

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

Last change on this file since 42392 was 35353, checked in by vboxsync, 14 years ago

Move the misc files the in src/VBox/Devices/ directory into a build/ subdirectory, changing their names to match the target module.

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