This patch adds an HPET emulator device. Normally the HPET is not used by Mac OS X, but required to exist. This is mostly a dummy implementation to satisfy Mac OS X and I am not sure if it is safe to use. As it is only activated on the IntelMac target, I do not expect any problems with that though.
Index: qemu-snapshot-2008-01-08_05/Makefile.target =================================================================== --- qemu-snapshot-2008-01-08_05.orig/Makefile.target +++ qemu-snapshot-2008-01-08_05/Makefile.target @@ -441,7 +441,7 @@ ifeq ($(TARGET_BASE_ARCH), i386) VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o VL_OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o -VL_OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o smbios.o +VL_OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o smbios.o hpet.o CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE endif ifeq ($(TARGET_BASE_ARCH), ppc) Index: qemu-snapshot-2008-01-08_05/hw/hpet.c =================================================================== --- /dev/null +++ qemu-snapshot-2008-01-08_05/hw/hpet.c @@ -0,0 +1,294 @@ +/* + * High Precisition Event Timer emulation + * + * Copyright (c) 2007 Alexander Graf + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ***************************************************************** + * + * This driver attempts to emulate an HPET device in software. It is by no + * means complete and is prone to break on certain conditions. + * + */ +#include "hw.h" +#include "console.h" +#include "qemu-timer.h" + + +#define HPET_BASE 0xfed00000 + +#define HPET_NUM_TIMERS 3 +#define HPET_TIMER_TYPE_LEVEL 1 +#define HPET_TIMER_TYPE_EDGE 0 +#define HPET_TIMER_DELIVERY_APIC 0 +#define HPET_TIMER_DELIVERY_FSB 1 +#define HPET_TIMER_CAP_FSB_INT_DEL (1 << 15) +#define HPET_TIMER_CAP_PER_INT (1 << 4) + +qemu_irq hpet_irq; + +struct HPETState; +typedef struct HPETTimer { + QEMUTimer *timer; + struct HPETState *state; + uint8_t type; + uint8_t active; + uint8_t delivery; + uint8_t apic_port; + uint8_t periodic; + uint8_t enabled; + uint32_t comparator; // if(hpet_counter == comparator) IRQ(); +} HPETTimer; + +typedef struct HPETState { + QEMUTimer *periodic_timer; + uint64_t hpet_counter; + int64_t next_periodic_time; + HPETTimer timer[HPET_NUM_TIMERS]; +} HPETState; + +static void hpet_timer(void *opaque) +{ + HPETTimer *s = (HPETTimer*)opaque; + printf("hpet i!\n"); + if(s->periodic) { + printf("periodic hpet!\n"); + qemu_mod_timer(s->timer, qemu_get_clock(vm_clock) + ((s->comparator) * (ticks_per_sec * 99) / 100)); + } + // XXX use s->apic_port + qemu_irq_raise(hpet_irq); +} + +static void hpet_periodic_timer(void *opaque) +{ + HPETState *s = opaque; + + s->hpet_counter += ticks_per_sec; + s->next_periodic_time += ticks_per_sec; + // rtc_timer_update(s, s->next_periodic_time); + // s->cmos_data[RTC_REG_C] |= 0xc0; + qemu_mod_timer(s->periodic_timer, s->next_periodic_time); + printf("hpet p!\n"); + qemu_irq_raise(0); +} + +static void hpet_check(HPETTimer *s) +{ + if(s->enabled) { + if(s->periodic) + qemu_mod_timer(s->timer, qemu_get_clock(vm_clock) + s->comparator * (ticks_per_sec * 99) / 100); + else + qemu_mod_timer(s->timer, qemu_get_clock(vm_clock) + ((s->comparator - s->state->hpet_counter) * (ticks_per_sec * 99) / 100)); + } +} + +static uint32_t hpet_ram_readb(void *opaque, target_phys_addr_t addr) +{ +#ifdef HPET_DEBUG + printf("qemu: hpet_read b at %#lx\n", addr); +#endif + return 10; +} + +static uint32_t hpet_ram_readw(void *opaque, target_phys_addr_t addr) +{ +#ifdef HPET_DEBUG + printf("qemu: hpet_read w at %#lx\n", addr); +#endif + return 10; +} + +static uint32_t hpet_ram_readl(void *opaque, target_phys_addr_t addr) +{ + HPETState *s = (HPETState *)opaque; +#ifdef HPET_DEBUG + printf("qemu: hpet_read l at %#lx\n", addr); +#endif + switch(addr - HPET_BASE) { + case 0x00: + return 0x8086a201; + case 0x04: + return 0x0429b17f; + case 0x10: + case 0x14: + return 0; + case 0xf0: + return s->hpet_counter; + case 0xf4: + return 0; + case 0x20: + { + uint32_t retval = 0; + int i; + for(i=0; i<HPET_NUM_TIMERS; i++) { + if(s->timer[i].type == HPET_TIMER_TYPE_LEVEL) + retval |= s->timer[i].active << i; + } + return retval; + } + default: + { + uint8_t timer_id = (addr - HPET_BASE - 0x100) / 0x20; + switch((addr - HPET_BASE - 0x100) % 0x20) { + case 0x0: + return ((s->timer[timer_id].delivery == HPET_TIMER_DELIVERY_FSB) << 14) + | (s->timer[timer_id].apic_port << 9) + | HPET_TIMER_CAP_PER_INT + | (s->timer[timer_id].periodic << 3) + | (s->timer[timer_id].enabled << 2) + | (s->timer[timer_id].type << 1); + case 0x4: // Interrupt capabilities + return 0x00ff; + case 0x8: // comparator register + return s->timer[timer_id].comparator; + case 0xc: + return 0x0; + } + } + } + +#ifdef HPET_DEBUG + printf("qemu: invalid hpet_read l at %#x\n", addr); +#endif + return 10; +} + +static void hpet_ram_writeb(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ +#ifdef HPET_DEBUG + printf("qemu: invalid hpet_write b at %#x = %#x\n", addr, value); +#endif +} + +static void hpet_ram_writew(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ +#ifdef HPET_DEBUG + printf("qemu: invalid hpet_write w at %#x = %#x\n", addr, value); +#endif +} + +static void hpet_ram_writel(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + HPETState *s = (HPETState *)opaque; +#ifdef HPET_DEBUG + printf("qemu: hpet_write l at %#x = %#x\n", addr, value); +#endif + switch(addr - HPET_BASE) { + case 0x00: + return; + case 0x10: + case 0x14: + if(value == 0) { + // disable hpet interrupts + } else if(value == 1) { + // enable all hpet interrupts +// HPETState *s = (HPETState*) opaque; +// qemu_mod_timer(s->periodic_timer, qemu_get_clock(vm_clock) + (ticks_per_sec * 99) / 100); + } else { +#ifdef HPET_DEBUG + printf("qemu: invalid hpet_write l at %#x = %#x\n", addr, value); +#endif + } + break; + case 0xf0: + s->hpet_counter = (s->hpet_counter & (0xffffffffULL << 32)) | value; +#ifdef HPET_DEBUG + printf("qemu: HPET counter 0xf0 set to %#x -> %#llx\n", value, s->hpet_counter); +#endif + break; + case 0xf4: + s->hpet_counter = (s->hpet_counter & 0xffffffffULL) | (((uint64_t)value) << 32); +#ifdef HPET_DEBUG + printf("qemu: HPET counter 0xf4 set to %#x -> %#llx\n", value, s->hpet_counter); +#endif + break; + default: + { + uint8_t timer_id = (addr - HPET_BASE - 0x100) / 0x20; + switch((addr - HPET_BASE - 0x100) % 0x20) { + case 0x0: + if(value & 1) break; // reserved + s->timer[timer_id].delivery = (value >> 14) & 1; + s->timer[timer_id].apic_port = (value >> 9) & 16; + s->timer[timer_id].periodic = (value >> 3) & 1; + s->timer[timer_id].enabled = (value >> 2) & 1; + s->timer[timer_id].type = (value >> 1) & 1; +#ifdef HPET_DEBUG + printf("qemu: hpet_write l at %#x = %#x\n", addr, value); +#endif + hpet_check(&(s->timer[timer_id])); + break; +#ifdef HPET_DEBUG + case 0x4: // Interrupt capabilities + printf("qemu: invalid hpet_write l at %#x = %#x\n", addr, value); + break; +#endif + case 0x8: // comparator register + s->timer[timer_id].comparator = value; + hpet_check(&(s->timer[timer_id])); + break; + case 0xc: +#ifdef HPET_DEBUG + printf("qemu: invalid hpet_write l at %#x = %#x\n", addr, value); +#endif + break; + } + } + } + +} + +static CPUReadMemoryFunc *hpet_ram_read[] = { + hpet_ram_readb, + hpet_ram_readw, + hpet_ram_readl, +}; + +static CPUWriteMemoryFunc *hpet_ram_write[] = { + hpet_ram_writeb, + hpet_ram_writew, + hpet_ram_writel, +}; + + +void hpet_init(qemu_irq irq) { + int iomemtype, i; + HPETState *s; + + /* XXX this is a dirty hack for HPET support w/o LPC + Actually this is a config descriptor for the RCBA */ + s = qemu_mallocz(sizeof(HPETState)); + + for(i=0; i<HPET_NUM_TIMERS; i++) { + s->timer[i].comparator = 0xffffffff; + s->timer[i].state = s; + s->timer[i].timer = qemu_new_timer(vm_clock, hpet_timer, s->timer+i); + } + + /* HPET Area */ + + iomemtype = cpu_register_io_memory(0, hpet_ram_read, + hpet_ram_write, s); + + cpu_register_physical_memory(HPET_BASE, 0x400, iomemtype); + + hpet_irq = irq; + s->periodic_timer = qemu_new_timer(vm_clock, + hpet_periodic_timer, s); +}