applesmc.c (16154B)
1/* 2 * Apple SMC controller 3 * 4 * Copyright (c) 2007 Alexander Graf 5 * 6 * Authors: Alexander Graf <agraf@suse.de> 7 * Susanne Graf <suse@csgraf.de> 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 21 * 22 * ***************************************************************** 23 * 24 * In all Intel-based Apple hardware there is an SMC chip to control the 25 * backlight, fans and several other generic device parameters. It also 26 * contains the magic keys used to dongle Mac OS X to the device. 27 * 28 * This driver was mostly created by looking at the Linux AppleSMC driver 29 * implementation and does not support IRQ. 30 * 31 */ 32 33#include "qemu/osdep.h" 34#include "hw/isa/isa.h" 35#include "hw/qdev-properties.h" 36#include "ui/console.h" 37#include "qemu/module.h" 38#include "qemu/timer.h" 39#include "qom/object.h" 40 41#if defined(__APPLE__) && defined(__MACH__) 42#include <IOKit/IOKitLib.h> 43 44enum { 45 kSMCSuccess = 0x00, 46 kSMCKeyNotFound = 0x84 47}; 48 49enum { 50 kSMCUserClientOpen = 0x00, 51 kSMCUserClientClose = 0x01, 52 kSMCHandleYPCEvent = 0x02, 53 kSMCReadKey = 0x05, 54 kSMCGetKeyInfo = 0x09 55}; 56 57typedef struct SMCVersion { 58 uint8_t major; 59 uint8_t minor; 60 uint8_t build; 61 uint8_t reserved; 62 uint16_t release; 63} SMCVersion; 64 65typedef struct SMCPLimitData { 66 uint16_t version; 67 uint16_t length; 68 uint32_t cpuPLimit; 69 uint32_t gpuPLimit; 70 uint32_t memPLimit; 71} SMCPLimitData; 72 73typedef struct SMCKeyInfoData { 74 IOByteCount dataSize; 75 uint32_t dataType; 76 uint8_t dataAttributes; 77} SMCKeyInfoData; 78 79typedef struct { 80 uint32_t key; 81 SMCVersion vers; 82 SMCPLimitData pLimitData; 83 SMCKeyInfoData keyInfo; 84 uint8_t result; 85 uint8_t status; 86 uint8_t data8; 87 uint32_t data32; 88 uint8_t bytes[32]; 89} SMCParamStruct; 90 91static IOReturn smc_call_struct_method(uint32_t selector, 92 SMCParamStruct *inputStruct, 93 SMCParamStruct *outputStruct) 94{ 95 IOReturn ret; 96 97 size_t inputStructCnt = sizeof(SMCParamStruct); 98 size_t outputStructCnt = sizeof(SMCParamStruct); 99 100 io_service_t smcService = IO_OBJECT_NULL; 101 io_connect_t smcConnect = IO_OBJECT_NULL; 102 103 smcService = IOServiceGetMatchingService(kIOMasterPortDefault, 104 IOServiceMatching("AppleSMC")); 105 if (smcService == IO_OBJECT_NULL) { 106 ret = kIOReturnNotFound; 107 goto exit; 108 } 109 110 ret = IOServiceOpen(smcService, mach_task_self(), 1, &smcConnect); 111 if (ret != kIOReturnSuccess) { 112 smcConnect = IO_OBJECT_NULL; 113 goto exit; 114 } 115 if (smcConnect == IO_OBJECT_NULL) { 116 ret = kIOReturnError; 117 goto exit; 118 } 119 120 ret = IOConnectCallMethod(smcConnect, kSMCUserClientOpen, 121 NULL, 0, NULL, 0, 122 NULL, NULL, NULL, NULL); 123 if (ret != kIOReturnSuccess) { 124 goto exit; 125 } 126 127 ret = IOConnectCallStructMethod(smcConnect, selector, 128 inputStruct, inputStructCnt, 129 outputStruct, &outputStructCnt); 130 131exit: 132 if (smcConnect != IO_OBJECT_NULL) { 133 IOConnectCallMethod(smcConnect, kSMCUserClientClose, 134 NULL, 0, NULL, 0, NULL, 135 NULL, NULL, NULL); 136 IOServiceClose(smcConnect); 137 } 138 139 return ret; 140} 141 142static IOReturn smc_read_key(uint32_t key, 143 uint8_t *bytes, 144 IOByteCount *dataSize) 145{ 146 IOReturn ret; 147 148 SMCParamStruct inputStruct; 149 SMCParamStruct outputStruct; 150 151 if (key == 0 || bytes == NULL) { 152 ret = kIOReturnCannotWire; 153 goto exit; 154 } 155 156 /* determine key's data size */ 157 memset(&inputStruct, 0, sizeof(SMCParamStruct)); 158 inputStruct.data8 = kSMCGetKeyInfo; 159 inputStruct.key = key; 160 161 memset(&outputStruct, 0, sizeof(SMCParamStruct)); 162 ret = smc_call_struct_method(kSMCHandleYPCEvent, &inputStruct, &outputStruct); 163 if (ret != kIOReturnSuccess) { 164 goto exit; 165 } 166 if (outputStruct.result == kSMCKeyNotFound) { 167 ret = kIOReturnNotFound; 168 goto exit; 169 } 170 if (outputStruct.result != kSMCSuccess) { 171 ret = kIOReturnInternalError; 172 goto exit; 173 } 174 175 /* get key value */ 176 memset(&inputStruct, 0, sizeof(SMCParamStruct)); 177 inputStruct.data8 = kSMCReadKey; 178 inputStruct.key = key; 179 inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize; 180 181 memset(&outputStruct, 0, sizeof(SMCParamStruct)); 182 ret = smc_call_struct_method(kSMCHandleYPCEvent, &inputStruct, &outputStruct); 183 if (ret != kIOReturnSuccess) { 184 goto exit; 185 } 186 if (outputStruct.result == kSMCKeyNotFound) { 187 ret = kIOReturnNotFound; 188 goto exit; 189 } 190 if (outputStruct.result != kSMCSuccess) { 191 ret = kIOReturnInternalError; 192 goto exit; 193 } 194 195 memset(bytes, 0, *dataSize); 196 if (*dataSize > inputStruct.keyInfo.dataSize) { 197 *dataSize = inputStruct.keyInfo.dataSize; 198 } 199 memcpy(bytes, outputStruct.bytes, *dataSize); 200 201exit: 202 return ret; 203} 204#endif 205 206/* #define DEBUG_SMC */ 207 208#define APPLESMC_DEFAULT_IOBASE 0x300 209 210enum { 211 APPLESMC_DATA_PORT = 0x00, 212 APPLESMC_CMD_PORT = 0x04, 213 APPLESMC_ERR_PORT = 0x1e, 214 APPLESMC_NUM_PORTS = 0x20, 215}; 216 217enum { 218 APPLESMC_READ_CMD = 0x10, 219 APPLESMC_WRITE_CMD = 0x11, 220 APPLESMC_GET_KEY_BY_INDEX_CMD = 0x12, 221 APPLESMC_GET_KEY_TYPE_CMD = 0x13, 222}; 223 224enum { 225 APPLESMC_ST_CMD_DONE = 0x00, 226 APPLESMC_ST_DATA_READY = 0x01, 227 APPLESMC_ST_BUSY = 0x02, 228 APPLESMC_ST_ACK = 0x04, 229 APPLESMC_ST_NEW_CMD = 0x08, 230}; 231 232enum { 233 APPLESMC_ST_1E_CMD_INTRUPTED = 0x80, 234 APPLESMC_ST_1E_STILL_BAD_CMD = 0x81, 235 APPLESMC_ST_1E_BAD_CMD = 0x82, 236 APPLESMC_ST_1E_NOEXIST = 0x84, 237 APPLESMC_ST_1E_WRITEONLY = 0x85, 238 APPLESMC_ST_1E_READONLY = 0x86, 239 APPLESMC_ST_1E_BAD_INDEX = 0xb8, 240}; 241 242#ifdef DEBUG_SMC 243#define smc_debug(...) fprintf(stderr, "AppleSMC: " __VA_ARGS__) 244#else 245#define smc_debug(...) do { } while (0) 246#endif 247 248static char default_osk[64] = "This is a dummy key. Enter the real key " 249 "using the -osk parameter"; 250 251struct AppleSMCData { 252 uint8_t len; 253 const char *key; 254 const char *data; 255 QLIST_ENTRY(AppleSMCData) node; 256}; 257 258OBJECT_DECLARE_SIMPLE_TYPE(AppleSMCState, APPLE_SMC) 259 260struct AppleSMCState { 261 ISADevice parent_obj; 262 263 MemoryRegion io_data; 264 MemoryRegion io_cmd; 265 MemoryRegion io_err; 266 uint32_t iobase; 267 uint8_t cmd; 268 uint8_t status; 269 uint8_t status_1e; 270 uint8_t last_ret; 271 char key[4]; 272 uint8_t read_pos; 273 uint8_t data_len; 274 uint8_t data_pos; 275 uint8_t data[255]; 276 char *osk; 277 QLIST_HEAD(, AppleSMCData) data_def; 278}; 279 280static void applesmc_io_cmd_write(void *opaque, hwaddr addr, uint64_t val, 281 unsigned size) 282{ 283 AppleSMCState *s = opaque; 284 uint8_t status = s->status & 0x0f; 285 286 smc_debug("CMD received: 0x%02x\n", (uint8_t)val); 287 switch (val) { 288 case APPLESMC_READ_CMD: 289 /* did last command run through OK? */ 290 if (status == APPLESMC_ST_CMD_DONE || status == APPLESMC_ST_NEW_CMD) { 291 s->cmd = val; 292 s->status = APPLESMC_ST_NEW_CMD | APPLESMC_ST_ACK; 293 } else { 294 smc_debug("ERROR: previous command interrupted!\n"); 295 s->status = APPLESMC_ST_NEW_CMD; 296 s->status_1e = APPLESMC_ST_1E_CMD_INTRUPTED; 297 } 298 break; 299 default: 300 smc_debug("UNEXPECTED CMD 0x%02x\n", (uint8_t)val); 301 s->status = APPLESMC_ST_NEW_CMD; 302 s->status_1e = APPLESMC_ST_1E_BAD_CMD; 303 } 304 s->read_pos = 0; 305 s->data_pos = 0; 306} 307 308static struct AppleSMCData *applesmc_find_key(AppleSMCState *s) 309{ 310 struct AppleSMCData *d; 311 312 QLIST_FOREACH(d, &s->data_def, node) { 313 if (!memcmp(d->key, s->key, 4)) { 314 return d; 315 } 316 } 317 return NULL; 318} 319 320static void applesmc_io_data_write(void *opaque, hwaddr addr, uint64_t val, 321 unsigned size) 322{ 323 AppleSMCState *s = opaque; 324 struct AppleSMCData *d; 325 326 smc_debug("DATA received: 0x%02x\n", (uint8_t)val); 327 switch (s->cmd) { 328 case APPLESMC_READ_CMD: 329 if ((s->status & 0x0f) == APPLESMC_ST_CMD_DONE) { 330 break; 331 } 332 if (s->read_pos < 4) { 333 s->key[s->read_pos] = val; 334 s->status = APPLESMC_ST_ACK; 335 } else if (s->read_pos == 4) { 336 d = applesmc_find_key(s); 337 if (d != NULL) { 338 memcpy(s->data, d->data, d->len); 339 s->data_len = d->len; 340 s->data_pos = 0; 341 s->status = APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY; 342 s->status_1e = APPLESMC_ST_CMD_DONE; /* clear on valid key */ 343 } else { 344 smc_debug("READ_CMD: key '%c%c%c%c' not found!\n", 345 s->key[0], s->key[1], s->key[2], s->key[3]); 346 s->status = APPLESMC_ST_CMD_DONE; 347 s->status_1e = APPLESMC_ST_1E_NOEXIST; 348 } 349 } 350 s->read_pos++; 351 break; 352 default: 353 s->status = APPLESMC_ST_CMD_DONE; 354 s->status_1e = APPLESMC_ST_1E_STILL_BAD_CMD; 355 } 356} 357 358static void applesmc_io_err_write(void *opaque, hwaddr addr, uint64_t val, 359 unsigned size) 360{ 361 smc_debug("ERR_CODE received: 0x%02x, ignoring!\n", (uint8_t)val); 362 /* NOTE: writing to the error port not supported! */ 363} 364 365static uint64_t applesmc_io_data_read(void *opaque, hwaddr addr, unsigned size) 366{ 367 AppleSMCState *s = opaque; 368 369 switch (s->cmd) { 370 case APPLESMC_READ_CMD: 371 if (!(s->status & APPLESMC_ST_DATA_READY)) { 372 break; 373 } 374 if (s->data_pos < s->data_len) { 375 s->last_ret = s->data[s->data_pos]; 376 smc_debug("READ '%c%c%c%c'[%d] = %02x\n", 377 s->key[0], s->key[1], s->key[2], s->key[3], 378 s->data_pos, s->last_ret); 379 s->data_pos++; 380 if (s->data_pos == s->data_len) { 381 s->status = APPLESMC_ST_CMD_DONE; 382 smc_debug("READ '%c%c%c%c' Len=%d complete!\n", 383 s->key[0], s->key[1], s->key[2], s->key[3], 384 s->data_len); 385 } else { 386 s->status = APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY; 387 } 388 } 389 break; 390 default: 391 s->status = APPLESMC_ST_CMD_DONE; 392 s->status_1e = APPLESMC_ST_1E_STILL_BAD_CMD; 393 } 394 smc_debug("DATA sent: 0x%02x\n", s->last_ret); 395 396 return s->last_ret; 397} 398 399static uint64_t applesmc_io_cmd_read(void *opaque, hwaddr addr, unsigned size) 400{ 401 AppleSMCState *s = opaque; 402 403 smc_debug("CMD sent: 0x%02x\n", s->status); 404 return s->status; 405} 406 407static uint64_t applesmc_io_err_read(void *opaque, hwaddr addr, unsigned size) 408{ 409 AppleSMCState *s = opaque; 410 411 /* NOTE: read does not clear the 1e status */ 412 smc_debug("ERR_CODE sent: 0x%02x\n", s->status_1e); 413 return s->status_1e; 414} 415 416static void applesmc_add_key(AppleSMCState *s, const char *key, 417 int len, const char *data) 418{ 419 struct AppleSMCData *def; 420 421 def = g_malloc0(sizeof(struct AppleSMCData)); 422 def->key = key; 423 def->len = len; 424 def->data = data; 425 426 QLIST_INSERT_HEAD(&s->data_def, def, node); 427} 428 429static void qdev_applesmc_isa_reset(DeviceState *dev) 430{ 431 AppleSMCState *s = APPLE_SMC(dev); 432 struct AppleSMCData *d, *next; 433 434 /* Remove existing entries */ 435 QLIST_FOREACH_SAFE(d, &s->data_def, node, next) { 436 QLIST_REMOVE(d, node); 437 } 438 s->status = 0x00; 439 s->status_1e = 0x00; 440 s->last_ret = 0x00; 441 442 applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03"); 443 applesmc_add_key(s, "OSK0", 32, s->osk); 444 applesmc_add_key(s, "OSK1", 32, s->osk + 32); 445 applesmc_add_key(s, "NATJ", 1, "\0"); 446 applesmc_add_key(s, "MSSP", 1, "\0"); 447 applesmc_add_key(s, "MSSD", 1, "\0x3"); 448} 449 450static const MemoryRegionOps applesmc_data_io_ops = { 451 .write = applesmc_io_data_write, 452 .read = applesmc_io_data_read, 453 .endianness = DEVICE_NATIVE_ENDIAN, 454 .impl = { 455 .min_access_size = 1, 456 .max_access_size = 1, 457 }, 458}; 459 460static const MemoryRegionOps applesmc_cmd_io_ops = { 461 .write = applesmc_io_cmd_write, 462 .read = applesmc_io_cmd_read, 463 .endianness = DEVICE_NATIVE_ENDIAN, 464 .impl = { 465 .min_access_size = 1, 466 .max_access_size = 1, 467 }, 468}; 469 470static const MemoryRegionOps applesmc_err_io_ops = { 471 .write = applesmc_io_err_write, 472 .read = applesmc_io_err_read, 473 .endianness = DEVICE_NATIVE_ENDIAN, 474 .impl = { 475 .min_access_size = 1, 476 .max_access_size = 1, 477 }, 478}; 479 480static void applesmc_isa_realize(DeviceState *dev, Error **errp) 481{ 482 AppleSMCState *s = APPLE_SMC(dev); 483 bool valid_key = false; 484 485 memory_region_init_io(&s->io_data, OBJECT(s), &applesmc_data_io_ops, s, 486 "applesmc-data", 1); 487 isa_register_ioport(&s->parent_obj, &s->io_data, 488 s->iobase + APPLESMC_DATA_PORT); 489 490 memory_region_init_io(&s->io_cmd, OBJECT(s), &applesmc_cmd_io_ops, s, 491 "applesmc-cmd", 1); 492 isa_register_ioport(&s->parent_obj, &s->io_cmd, 493 s->iobase + APPLESMC_CMD_PORT); 494 495 memory_region_init_io(&s->io_err, OBJECT(s), &applesmc_err_io_ops, s, 496 "applesmc-err", 1); 497 isa_register_ioport(&s->parent_obj, &s->io_err, 498 s->iobase + APPLESMC_ERR_PORT); 499 500 if (s->osk) { 501 valid_key = strlen(s->osk) == 64; 502 } else { 503#if defined(__APPLE__) && defined(__MACH__) 504 IOReturn ret; 505 IOByteCount size = 32; 506 507 ret = smc_read_key('OSK0', (uint8_t *) default_osk, &size); 508 if (ret != kIOReturnSuccess) { 509 goto failure; 510 } 511 512 ret = smc_read_key('OSK1', (uint8_t *) default_osk + size, &size); 513 if (ret != kIOReturnSuccess) { 514 goto failure; 515 } 516 517 warn_report("Using AppleSMC with host key"); 518 valid_key = true; 519 s->osk = default_osk; 520failure:; 521#endif 522 } 523 524 if (!valid_key) { 525 warn_report("Using AppleSMC with invalid key"); 526 s->osk = default_osk; 527 } 528 529 QLIST_INIT(&s->data_def); 530 qdev_applesmc_isa_reset(dev); 531} 532 533static Property applesmc_isa_properties[] = { 534 DEFINE_PROP_UINT32(APPLESMC_PROP_IO_BASE, AppleSMCState, iobase, 535 APPLESMC_DEFAULT_IOBASE), 536 DEFINE_PROP_STRING("osk", AppleSMCState, osk), 537 DEFINE_PROP_END_OF_LIST(), 538}; 539 540static void qdev_applesmc_class_init(ObjectClass *klass, void *data) 541{ 542 DeviceClass *dc = DEVICE_CLASS(klass); 543 544 dc->realize = applesmc_isa_realize; 545 dc->reset = qdev_applesmc_isa_reset; 546 device_class_set_props(dc, applesmc_isa_properties); 547 set_bit(DEVICE_CATEGORY_MISC, dc->categories); 548} 549 550static const TypeInfo applesmc_isa_info = { 551 .name = TYPE_APPLE_SMC, 552 .parent = TYPE_ISA_DEVICE, 553 .instance_size = sizeof(AppleSMCState), 554 .class_init = qdev_applesmc_class_init, 555}; 556 557static void applesmc_register_types(void) 558{ 559 type_register_static(&applesmc_isa_info); 560} 561 562type_init(applesmc_register_types)