#qemu-only -> submit upstream qemu Index: kvm-75/qemu/Makefile.target =================================================================== --- kvm-75.orig/qemu/Makefile.target +++ kvm-75/qemu/Makefile.target @@ -610,7 +610,7 @@ ifeq ($(TARGET_BASE_ARCH), i386) OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o -OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o extboot.o +OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o extboot.o hpet.o ifeq ($(USE_KVM_PIT), 1) OBJS+= i8254-kvm.o endif Index: kvm-75/qemu/hw/hpet.c =================================================================== --- /dev/null +++ kvm-75/qemu/hw/hpet.c @@ -0,0 +1,322 @@ +/* + * 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_DEBUG + +#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) + +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(); + qemu_irq irq; +} HPETTimer; + +typedef struct HPETState { + uint64_t hpet_counter; + int64_t next_periodic_time; + uint8_t active; + qemu_irq *irqs; + HPETTimer timer[HPET_NUM_TIMERS]; +} HPETState; + +static void update_irq(struct HPETTimer *timer) +{ + qemu_set_irq(timer->irq, timer->active && timer->state->active); +} + +static void update_irq_all(struct HPETState *s) +{ + int i; + for(i=0; itimer[i]); +} + +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)); + } + s->active = 1; + update_irq(s); +} + +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; itimer[i].type == HPET_TIMER_TYPE_LEVEL) + retval |= s->timer[i].active << i; + } + return retval; + } + case 0x100 ... 0x3ff: + { + uint8_t timer_id = (addr - HPET_BASE - 0x100) / 0x20; + HPETTimer *timer = &s->timer[timer_id]; + + switch((addr - HPET_BASE - 0x100) % 0x20) { + case 0x0: + return ((timer->delivery == HPET_TIMER_DELIVERY_FSB) << 14) + | (timer->apic_port << 9) + | HPET_TIMER_CAP_PER_INT + | (timer->periodic << 3) + | (timer->enabled << 2) + | (timer->type << 1); + case 0x4: // Interrupt capabilities + return 0x00ff; + case 0x8: // comparator register + return timer->comparator; + case 0xc: + return 0x0; + } + } + break; + } + +#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: // set interrupt enabled flag + if(value < 2) { + s->active = value; + update_irq_all(s); + } else { +#ifdef HPET_DEBUG + printf("qemu: invalid hpet_write l at %#x = %#x\n", addr, value); +#endif + } + break; + case 0x20: + { + int i; + for(i=0; itimer[i].type == HPET_TIMER_TYPE_LEVEL) { + if(value & (1 << i)) { + s->timer[i].active = 0; + update_irq(&s->timer[i]); + } + } + } + } + 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; + case 0x100 ... 0x3ff: + { + uint8_t timer_id = (addr - HPET_BASE - 0x100) / 0x20; + HPETTimer *timer = &s->timer[timer_id]; + + switch((addr - HPET_BASE - 0x100) % 0x20) { + case 0x0: + if(value & 1) break; // reserved + timer->delivery = (value >> 14) & 1; + timer->apic_port = (value >> 9) & 16; + timer->irq = s->irqs[timer->apic_port]; + timer->periodic = (value >> 3) & 1; + timer->enabled = (value >> 2) & 1; + timer->type = (value >> 1) & 1; +#ifdef HPET_DEBUG + printf("qemu: hpet_write l at %#x = %#x\n", addr, value); +#endif + hpet_check(timer); + 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 + timer->comparator = value; + hpet_check(timer); + break; + case 0xc: +#ifdef HPET_DEBUG + printf("qemu: invalid hpet_write l at %#x = %#x\n", addr, value); +#endif + break; + } + } + default: + printf("qemu: invalid hpet_write l at %#x = %#x\n", addr, value); + } + +} + +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)); + s->irqs = irq; + + for(i=0; itimer[i]; + timer->comparator = 0xffffffff; + timer->state = s; + timer->timer = qemu_new_timer(vm_clock, hpet_timer, s->timer+i); + switch(i) { + case 0: + timer->apic_port = 2; + break; + case 1: + timer->apic_port = 8; + break; + default: + timer->apic_port = 0; + break; + } + s->timer[i].irq = irq[timer->apic_port]; + } + + /* HPET Area */ + + iomemtype = cpu_register_io_memory(0, hpet_ram_read, + hpet_ram_write, s); + + cpu_register_physical_memory(HPET_BASE, 0x400, iomemtype); +}