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/error-report.h" 38 #include "qemu/module.h" 39 #include "qemu/timer.h" 40 #include "qom/object.h" 41 #include "hw/acpi/acpi_aml_interface.h" 42 43 /* #define DEBUG_SMC */ 44 45 #define APPLESMC_DEFAULT_IOBASE 0x300 46 #define TYPE_APPLE_SMC "isa-applesmc" 47 #define APPLESMC_MAX_DATA_LENGTH 32 48 #define APPLESMC_PROP_IO_BASE "iobase" 49 50 enum { 51 APPLESMC_DATA_PORT = 0x00, 52 APPLESMC_CMD_PORT = 0x04, 53 APPLESMC_ERR_PORT = 0x1e, 54 APPLESMC_NUM_PORTS = 0x20, 55 }; 56 57 enum { 58 APPLESMC_READ_CMD = 0x10, 59 APPLESMC_WRITE_CMD = 0x11, 60 APPLESMC_GET_KEY_BY_INDEX_CMD = 0x12, 61 APPLESMC_GET_KEY_TYPE_CMD = 0x13, 62 }; 63 64 enum { 65 APPLESMC_ST_CMD_DONE = 0x00, 66 APPLESMC_ST_DATA_READY = 0x01, 67 APPLESMC_ST_BUSY = 0x02, 68 APPLESMC_ST_ACK = 0x04, 69 APPLESMC_ST_NEW_CMD = 0x08, 70 }; 71 72 enum { 73 APPLESMC_ST_1E_CMD_INTRUPTED = 0x80, 74 APPLESMC_ST_1E_STILL_BAD_CMD = 0x81, 75 APPLESMC_ST_1E_BAD_CMD = 0x82, 76 APPLESMC_ST_1E_NOEXIST = 0x84, 77 APPLESMC_ST_1E_WRITEONLY = 0x85, 78 APPLESMC_ST_1E_READONLY = 0x86, 79 APPLESMC_ST_1E_BAD_INDEX = 0xb8, 80 }; 81 82 #ifdef DEBUG_SMC 83 #define smc_debug(...) fprintf(stderr, "AppleSMC: " __VA_ARGS__) 84 #else 85 #define smc_debug(...) do { } while (0) 86 #endif 87 88 static char default_osk[64] = "This is a dummy key. Enter the real key " 89 "using the -osk parameter"; 90 91 struct AppleSMCData { 92 uint8_t len; 93 const char *key; 94 const char *data; 95 QLIST_ENTRY(AppleSMCData) node; 96 }; 97 98 OBJECT_DECLARE_SIMPLE_TYPE(AppleSMCState, APPLE_SMC) 99 100 struct AppleSMCState { 101 ISADevice parent_obj; 102 103 MemoryRegion io_data; 104 MemoryRegion io_cmd; 105 MemoryRegion io_err; 106 uint32_t iobase; 107 uint8_t cmd; 108 uint8_t status; 109 uint8_t status_1e; 110 uint8_t last_ret; 111 char key[4]; 112 uint8_t read_pos; 113 uint8_t data_len; 114 uint8_t data_pos; 115 uint8_t data[255]; 116 char *osk; 117 QLIST_HEAD(, AppleSMCData) data_def; 118 }; 119 120 static void applesmc_io_cmd_write(void *opaque, hwaddr addr, uint64_t val, 121 unsigned size) 122 { 123 AppleSMCState *s = opaque; 124 uint8_t status = s->status & 0x0f; 125 126 smc_debug("CMD received: 0x%02x\n", (uint8_t)val); 127 switch (val) { 128 case APPLESMC_READ_CMD: 129 /* did last command run through OK? */ 130 if (status == APPLESMC_ST_CMD_DONE || status == APPLESMC_ST_NEW_CMD) { 131 s->cmd = val; 132 s->status = APPLESMC_ST_NEW_CMD | APPLESMC_ST_ACK; 133 } else { 134 smc_debug("ERROR: previous command interrupted!\n"); 135 s->status = APPLESMC_ST_NEW_CMD; 136 s->status_1e = APPLESMC_ST_1E_CMD_INTRUPTED; 137 } 138 break; 139 default: 140 smc_debug("UNEXPECTED CMD 0x%02x\n", (uint8_t)val); 141 s->status = APPLESMC_ST_NEW_CMD; 142 s->status_1e = APPLESMC_ST_1E_BAD_CMD; 143 } 144 s->read_pos = 0; 145 s->data_pos = 0; 146 } 147 148 static const struct AppleSMCData *applesmc_find_key(AppleSMCState *s) 149 { 150 struct AppleSMCData *d; 151 152 QLIST_FOREACH(d, &s->data_def, node) { 153 if (!memcmp(d->key, s->key, 4)) { 154 return d; 155 } 156 } 157 return NULL; 158 } 159 160 static void applesmc_io_data_write(void *opaque, hwaddr addr, uint64_t val, 161 unsigned size) 162 { 163 AppleSMCState *s = opaque; 164 const struct AppleSMCData *d; 165 166 smc_debug("DATA received: 0x%02x\n", (uint8_t)val); 167 switch (s->cmd) { 168 case APPLESMC_READ_CMD: 169 if ((s->status & 0x0f) == APPLESMC_ST_CMD_DONE) { 170 break; 171 } 172 if (s->read_pos < 4) { 173 s->key[s->read_pos] = val; 174 s->status = APPLESMC_ST_ACK; 175 } else if (s->read_pos == 4) { 176 d = applesmc_find_key(s); 177 if (d != NULL) { 178 memcpy(s->data, d->data, d->len); 179 s->data_len = d->len; 180 s->data_pos = 0; 181 s->status = APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY; 182 s->status_1e = APPLESMC_ST_CMD_DONE; /* clear on valid key */ 183 } else { 184 smc_debug("READ_CMD: key '%c%c%c%c' not found!\n", 185 s->key[0], s->key[1], s->key[2], s->key[3]); 186 s->status = APPLESMC_ST_CMD_DONE; 187 s->status_1e = APPLESMC_ST_1E_NOEXIST; 188 } 189 } 190 s->read_pos++; 191 break; 192 default: 193 s->status = APPLESMC_ST_CMD_DONE; 194 s->status_1e = APPLESMC_ST_1E_STILL_BAD_CMD; 195 } 196 } 197 198 static void applesmc_io_err_write(void *opaque, hwaddr addr, uint64_t val, 199 unsigned size) 200 { 201 smc_debug("ERR_CODE received: 0x%02x, ignoring!\n", (uint8_t)val); 202 /* NOTE: writing to the error port not supported! */ 203 } 204 205 static uint64_t applesmc_io_data_read(void *opaque, hwaddr addr, unsigned size) 206 { 207 AppleSMCState *s = opaque; 208 209 switch (s->cmd) { 210 case APPLESMC_READ_CMD: 211 if (!(s->status & APPLESMC_ST_DATA_READY)) { 212 break; 213 } 214 if (s->data_pos < s->data_len) { 215 s->last_ret = s->data[s->data_pos]; 216 smc_debug("READ '%c%c%c%c'[%d] = %02x\n", 217 s->key[0], s->key[1], s->key[2], s->key[3], 218 s->data_pos, s->last_ret); 219 s->data_pos++; 220 if (s->data_pos == s->data_len) { 221 s->status = APPLESMC_ST_CMD_DONE; 222 smc_debug("READ '%c%c%c%c' Len=%d complete!\n", 223 s->key[0], s->key[1], s->key[2], s->key[3], 224 s->data_len); 225 } else { 226 s->status = APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY; 227 } 228 } 229 break; 230 default: 231 s->status = APPLESMC_ST_CMD_DONE; 232 s->status_1e = APPLESMC_ST_1E_STILL_BAD_CMD; 233 } 234 smc_debug("DATA sent: 0x%02x\n", s->last_ret); 235 236 return s->last_ret; 237 } 238 239 static uint64_t applesmc_io_cmd_read(void *opaque, hwaddr addr, unsigned size) 240 { 241 AppleSMCState *s = opaque; 242 243 smc_debug("CMD sent: 0x%02x\n", s->status); 244 return s->status; 245 } 246 247 static uint64_t applesmc_io_err_read(void *opaque, hwaddr addr, unsigned size) 248 { 249 AppleSMCState *s = opaque; 250 251 /* NOTE: read does not clear the 1e status */ 252 smc_debug("ERR_CODE sent: 0x%02x\n", s->status_1e); 253 return s->status_1e; 254 } 255 256 static void applesmc_add_key(AppleSMCState *s, const char *key, 257 int len, const char *data) 258 { 259 struct AppleSMCData *def; 260 261 def = g_new0(struct AppleSMCData, 1); 262 def->key = key; 263 def->len = len; 264 def->data = data; 265 266 QLIST_INSERT_HEAD(&s->data_def, def, node); 267 } 268 269 static void qdev_applesmc_isa_reset(DeviceState *dev) 270 { 271 AppleSMCState *s = APPLE_SMC(dev); 272 273 s->status = 0x00; 274 s->status_1e = 0x00; 275 s->last_ret = 0x00; 276 } 277 278 static const MemoryRegionOps applesmc_data_io_ops = { 279 .write = applesmc_io_data_write, 280 .read = applesmc_io_data_read, 281 .endianness = DEVICE_NATIVE_ENDIAN, 282 .impl = { 283 .min_access_size = 1, 284 .max_access_size = 1, 285 }, 286 }; 287 288 static const MemoryRegionOps applesmc_cmd_io_ops = { 289 .write = applesmc_io_cmd_write, 290 .read = applesmc_io_cmd_read, 291 .endianness = DEVICE_NATIVE_ENDIAN, 292 .impl = { 293 .min_access_size = 1, 294 .max_access_size = 1, 295 }, 296 }; 297 298 static const MemoryRegionOps applesmc_err_io_ops = { 299 .write = applesmc_io_err_write, 300 .read = applesmc_io_err_read, 301 .endianness = DEVICE_NATIVE_ENDIAN, 302 .impl = { 303 .min_access_size = 1, 304 .max_access_size = 1, 305 }, 306 }; 307 308 static void applesmc_isa_realize(DeviceState *dev, Error **errp) 309 { 310 AppleSMCState *s = APPLE_SMC(dev); 311 312 memory_region_init_io(&s->io_data, OBJECT(s), &applesmc_data_io_ops, s, 313 "applesmc-data", 1); 314 isa_register_ioport(&s->parent_obj, &s->io_data, 315 s->iobase + APPLESMC_DATA_PORT); 316 317 memory_region_init_io(&s->io_cmd, OBJECT(s), &applesmc_cmd_io_ops, s, 318 "applesmc-cmd", 1); 319 isa_register_ioport(&s->parent_obj, &s->io_cmd, 320 s->iobase + APPLESMC_CMD_PORT); 321 322 memory_region_init_io(&s->io_err, OBJECT(s), &applesmc_err_io_ops, s, 323 "applesmc-err", 1); 324 isa_register_ioport(&s->parent_obj, &s->io_err, 325 s->iobase + APPLESMC_ERR_PORT); 326 327 if (!s->osk || (strlen(s->osk) != 64)) { 328 warn_report("Using AppleSMC with invalid key"); 329 s->osk = default_osk; 330 } 331 332 QLIST_INIT(&s->data_def); 333 applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03"); 334 applesmc_add_key(s, "OSK0", 32, s->osk); 335 applesmc_add_key(s, "OSK1", 32, s->osk + 32); 336 applesmc_add_key(s, "NATJ", 1, "\0"); 337 applesmc_add_key(s, "MSSP", 1, "\0"); 338 applesmc_add_key(s, "MSSD", 1, "\0x3"); 339 } 340 341 static void applesmc_unrealize(DeviceState *dev) 342 { 343 AppleSMCState *s = APPLE_SMC(dev); 344 struct AppleSMCData *d, *next; 345 346 /* Remove existing entries */ 347 QLIST_FOREACH_SAFE(d, &s->data_def, node, next) { 348 QLIST_REMOVE(d, node); 349 g_free(d); 350 } 351 } 352 353 static Property applesmc_isa_properties[] = { 354 DEFINE_PROP_UINT32(APPLESMC_PROP_IO_BASE, AppleSMCState, iobase, 355 APPLESMC_DEFAULT_IOBASE), 356 DEFINE_PROP_STRING("osk", AppleSMCState, osk), 357 DEFINE_PROP_END_OF_LIST(), 358 }; 359 360 static void build_applesmc_aml(AcpiDevAmlIf *adev, Aml *scope) 361 { 362 Aml *crs; 363 AppleSMCState *s = APPLE_SMC(adev); 364 uint32_t iobase = s->iobase; 365 Aml *dev = aml_device("SMC"); 366 367 aml_append(dev, aml_name_decl("_HID", aml_eisaid("APP0001"))); 368 /* device present, functioning, decoding, not shown in UI */ 369 aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); 370 crs = aml_resource_template(); 371 aml_append(crs, 372 aml_io(AML_DECODE16, iobase, iobase, 0x01, APPLESMC_MAX_DATA_LENGTH) 373 ); 374 aml_append(crs, aml_irq_no_flags(6)); 375 aml_append(dev, aml_name_decl("_CRS", crs)); 376 aml_append(scope, dev); 377 } 378 379 static void qdev_applesmc_class_init(ObjectClass *klass, void *data) 380 { 381 DeviceClass *dc = DEVICE_CLASS(klass); 382 AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); 383 384 dc->realize = applesmc_isa_realize; 385 dc->unrealize = applesmc_unrealize; 386 dc->reset = qdev_applesmc_isa_reset; 387 device_class_set_props(dc, applesmc_isa_properties); 388 set_bit(DEVICE_CATEGORY_MISC, dc->categories); 389 adevc->build_dev_aml = build_applesmc_aml; 390 } 391 392 static const TypeInfo applesmc_isa_info = { 393 .name = TYPE_APPLE_SMC, 394 .parent = TYPE_ISA_DEVICE, 395 .instance_size = sizeof(AppleSMCState), 396 .class_init = qdev_applesmc_class_init, 397 .interfaces = (InterfaceInfo[]) { 398 { TYPE_ACPI_DEV_AML_IF }, 399 { }, 400 }, 401 }; 402 403 static void applesmc_register_types(void) 404 { 405 type_register_static(&applesmc_isa_info); 406 } 407 408 type_init(applesmc_register_types) 409