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