mirror of
				https://git.proxmox.com/git/qemu
				synced 2025-10-31 07:39:44 +00:00 
			
		
		
		
	AppleSMC device emulation
Intel Macs have a chip called the "AppleSMC" which they use to control certain Apple specific parts of the hardware, like the keyboard background light. That chip is also used to store a key that Mac OS X uses to decrypt binaries. This patch adds emulation for that chip, so we're getting one step further to having Mac OS X run natively on Qemu. Signed-off-by: Alexander Graf <agraf@suse.de> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
This commit is contained in:
		
							parent
							
								
									1f5c3f8ca1
								
							
						
					
					
						commit
						1ddda5cd36
					
				| @ -192,7 +192,7 @@ obj-y += e1000.o | ||||
| obj-i386-y += vga.o | ||||
| obj-i386-y += mc146818rtc.o i8259.o pc.o | ||||
| obj-i386-y += cirrus_vga.o apic.o ioapic.o piix_pci.o | ||||
| obj-i386-y += vmmouse.o vmport.o hpet.o | ||||
| obj-i386-y += vmmouse.o vmport.o hpet.o applesmc.o | ||||
| obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o | ||||
| obj-i386-y += debugcon.o multiboot.o | ||||
| obj-i386-y += pc_piix.o | ||||
|  | ||||
							
								
								
									
										241
									
								
								hw/applesmc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								hw/applesmc.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,241 @@ | ||||
| /*
 | ||||
|  *  Apple SMC controller | ||||
|  * | ||||
|  *  Copyright (c) 2007 Alexander Graf | ||||
|  * | ||||
|  *  Authors: Alexander Graf <agraf@suse.de> | ||||
|  *           Susanne Graf <suse@csgraf.de> | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | ||||
|  * | ||||
|  * ***************************************************************** | ||||
|  * | ||||
|  * In all Intel-based Apple hardware there is an SMC chip to control the | ||||
|  * backlight, fans and several other generic device parameters. It also | ||||
|  * contains the magic keys used to dongle Mac OS X to the device. | ||||
|  * | ||||
|  * This driver was mostly created by looking at the Linux AppleSMC driver | ||||
|  * implementation and does not support IRQ. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "hw.h" | ||||
| #include "isa.h" | ||||
| #include "console.h" | ||||
| #include "qemu-timer.h" | ||||
| 
 | ||||
| /* #define DEBUG_SMC */ | ||||
| 
 | ||||
| #define APPLESMC_DEFAULT_IOBASE        0x300 | ||||
| /* data port used by Apple SMC */ | ||||
| #define APPLESMC_DATA_PORT             0x0 | ||||
| /* command/status port used by Apple SMC */ | ||||
| #define APPLESMC_CMD_PORT              0x4 | ||||
| #define APPLESMC_NR_PORTS              32 | ||||
| #define APPLESMC_MAX_DATA_LENGTH       32 | ||||
| 
 | ||||
| #define APPLESMC_READ_CMD              0x10 | ||||
| #define APPLESMC_WRITE_CMD             0x11 | ||||
| #define APPLESMC_GET_KEY_BY_INDEX_CMD  0x12 | ||||
| #define APPLESMC_GET_KEY_TYPE_CMD      0x13 | ||||
| 
 | ||||
| #ifdef DEBUG_SMC | ||||
| #define smc_debug(...) fprintf(stderr, "AppleSMC: " __VA_ARGS__) | ||||
| #else | ||||
| #define smc_debug(...) do { } while(0) | ||||
| #endif | ||||
| 
 | ||||
| static char default_osk[64] = "This is a dummy key. Enter the real key " | ||||
|                               "using the -osk parameter"; | ||||
| 
 | ||||
| struct AppleSMCData { | ||||
|     uint8_t len; | ||||
|     const char *key; | ||||
|     const char *data; | ||||
|     QLIST_ENTRY(AppleSMCData) node; | ||||
| }; | ||||
| 
 | ||||
| struct AppleSMCStatus { | ||||
|     ISADevice dev; | ||||
|     uint32_t iobase; | ||||
|     uint8_t cmd; | ||||
|     uint8_t status; | ||||
|     uint8_t key[4]; | ||||
|     uint8_t read_pos; | ||||
|     uint8_t data_len; | ||||
|     uint8_t data_pos; | ||||
|     uint8_t data[255]; | ||||
|     uint8_t charactic[4]; | ||||
|     char *osk; | ||||
|     QLIST_HEAD(, AppleSMCData) data_def; | ||||
| }; | ||||
| 
 | ||||
| static void applesmc_io_cmd_writeb(void *opaque, uint32_t addr, uint32_t val) | ||||
| { | ||||
|     struct AppleSMCStatus *s = opaque; | ||||
| 
 | ||||
|     smc_debug("CMD Write B: %#x = %#x\n", addr, val); | ||||
|     switch(val) { | ||||
|         case APPLESMC_READ_CMD: | ||||
|             s->status = 0x0c; | ||||
|             break; | ||||
|     } | ||||
|     s->cmd = val; | ||||
|     s->read_pos = 0; | ||||
|     s->data_pos = 0; | ||||
| } | ||||
| 
 | ||||
| static void applesmc_fill_data(struct AppleSMCStatus *s) | ||||
| { | ||||
|     struct AppleSMCData *d; | ||||
| 
 | ||||
|     QLIST_FOREACH(d, &s->data_def, node) { | ||||
|         if (!memcmp(d->key, s->key, 4)) { | ||||
|             smc_debug("Key matched (%s Len=%d Data=%s)\n", d->key, | ||||
|                       d->len, d->data); | ||||
|             memcpy(s->data, d->data, d->len); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void applesmc_io_data_writeb(void *opaque, uint32_t addr, uint32_t val) | ||||
| { | ||||
|     struct AppleSMCStatus *s = opaque; | ||||
| 
 | ||||
|     smc_debug("DATA Write B: %#x = %#x\n", addr, val); | ||||
|     switch(s->cmd) { | ||||
|         case APPLESMC_READ_CMD: | ||||
|             if(s->read_pos < 4) { | ||||
|                 s->key[s->read_pos] = val; | ||||
|                 s->status = 0x04; | ||||
|             } else if(s->read_pos == 4) { | ||||
|                 s->data_len = val; | ||||
|                 s->status = 0x05; | ||||
|                 s->data_pos = 0; | ||||
|                 smc_debug("Key = %c%c%c%c Len = %d\n", s->key[0], | ||||
|                           s->key[1], s->key[2], s->key[3], val); | ||||
|                 applesmc_fill_data(s); | ||||
|             } | ||||
|             s->read_pos++; | ||||
|             break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static uint32_t applesmc_io_data_readb(void *opaque, uint32_t addr1) | ||||
| { | ||||
|     struct AppleSMCStatus *s = opaque; | ||||
|     uint8_t retval = 0; | ||||
| 
 | ||||
|     switch(s->cmd) { | ||||
|         case APPLESMC_READ_CMD: | ||||
|             if(s->data_pos < s->data_len) { | ||||
|                 retval = s->data[s->data_pos]; | ||||
|                 smc_debug("READ_DATA[%d] = %#hhx\n", s->data_pos, | ||||
|                           retval); | ||||
|                 s->data_pos++; | ||||
|                 if(s->data_pos == s->data_len) { | ||||
|                     s->status = 0x00; | ||||
|                     smc_debug("EOF\n"); | ||||
|                 } else | ||||
|                     s->status = 0x05; | ||||
|             } | ||||
|     } | ||||
|     smc_debug("DATA Read b: %#x = %#x\n", addr1, retval); | ||||
| 
 | ||||
|     return retval; | ||||
| } | ||||
| 
 | ||||
| static uint32_t applesmc_io_cmd_readb(void *opaque, uint32_t addr1) | ||||
| { | ||||
|     struct AppleSMCStatus *s = opaque; | ||||
| 
 | ||||
|     smc_debug("CMD Read B: %#x\n", addr1); | ||||
|     return s->status; | ||||
| } | ||||
| 
 | ||||
| static void applesmc_add_key(struct AppleSMCStatus *s, const char *key, | ||||
|                              int len, const char *data) | ||||
| { | ||||
|     struct AppleSMCData *def; | ||||
| 
 | ||||
|     def = qemu_mallocz(sizeof(struct AppleSMCData)); | ||||
|     def->key = key; | ||||
|     def->len = len; | ||||
|     def->data = data; | ||||
| 
 | ||||
|     QLIST_INSERT_HEAD(&s->data_def, def, node); | ||||
| } | ||||
| 
 | ||||
| static void qdev_applesmc_isa_reset(DeviceState *dev) | ||||
| { | ||||
|     struct AppleSMCStatus *s = DO_UPCAST(struct AppleSMCStatus, dev.qdev, dev); | ||||
|     struct AppleSMCData *d, *next; | ||||
| 
 | ||||
|     /* Remove existing entries */ | ||||
|     QLIST_FOREACH_SAFE(d, &s->data_def, node, next) { | ||||
|         QLIST_REMOVE(d, node); | ||||
|     } | ||||
| 
 | ||||
|     applesmc_add_key(s, "REV ", 6, "\0x01\0x13\0x0f\0x00\0x00\0x03"); | ||||
|     applesmc_add_key(s, "OSK0", 32, s->osk); | ||||
|     applesmc_add_key(s, "OSK1", 32, s->osk + 32); | ||||
|     applesmc_add_key(s, "NATJ", 1, "\0"); | ||||
|     applesmc_add_key(s, "MSSP", 1, "\0"); | ||||
|     applesmc_add_key(s, "MSSD", 1, "\0x3"); | ||||
| } | ||||
| 
 | ||||
| static int applesmc_isa_init(ISADevice *dev) | ||||
| { | ||||
|     struct AppleSMCStatus *s = DO_UPCAST(struct AppleSMCStatus, dev, dev); | ||||
| 
 | ||||
|     register_ioport_read(s->iobase + APPLESMC_DATA_PORT, 4, 1, | ||||
|                          applesmc_io_data_readb, s); | ||||
|     register_ioport_read(s->iobase + APPLESMC_CMD_PORT, 4, 1, | ||||
|                          applesmc_io_cmd_readb, s); | ||||
|     register_ioport_write(s->iobase + APPLESMC_DATA_PORT, 4, 1, | ||||
|                           applesmc_io_data_writeb, s); | ||||
|     register_ioport_write(s->iobase + APPLESMC_CMD_PORT, 4, 1, | ||||
|                           applesmc_io_cmd_writeb, s); | ||||
| 
 | ||||
|     if (!s->osk || (strlen(s->osk) != 64)) { | ||||
|         fprintf(stderr, "WARNING: Using AppleSMC with invalid key\n"); | ||||
|         s->osk = default_osk; | ||||
|     } | ||||
| 
 | ||||
|     QLIST_INIT(&s->data_def); | ||||
|     qdev_applesmc_isa_reset(&dev->qdev); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static ISADeviceInfo applesmc_isa_info = { | ||||
|     .qdev.name  = "isa-applesmc", | ||||
|     .qdev.size  = sizeof(struct AppleSMCStatus), | ||||
|     .qdev.reset = qdev_applesmc_isa_reset, | ||||
|     .init       = applesmc_isa_init, | ||||
|     .qdev.props = (Property[]) { | ||||
|         DEFINE_PROP_HEX32("iobase", struct AppleSMCStatus, iobase, | ||||
|                           APPLESMC_DEFAULT_IOBASE), | ||||
|         DEFINE_PROP_STRING("osk", struct AppleSMCStatus, osk), | ||||
|         DEFINE_PROP_END_OF_LIST(), | ||||
|     }, | ||||
| }; | ||||
| 
 | ||||
| static void applesmc_register_devices(void) | ||||
| { | ||||
|     isa_qdev_register(&applesmc_isa_info); | ||||
| } | ||||
| 
 | ||||
| device_init(applesmc_register_devices) | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Alexander Graf
						Alexander Graf