Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Added HPET (High Precision Event Timer) device
Kyle Hale [Thu, 6 Mar 2014 19:19:28 +0000 (13:19 -0600)]
This is an initial implementation of a virtual HPET. Does not support
checkpoint save/restore yet. The HPET is currently defined to run at
1/16th of guest time

Note that a typical guest will check for the existence of the HPET
via an ACPI table.   To force the current SeaBios to build this table
apply the following patch to it.  This will eventually be automatic.

rem diff --git a/bios/seabios/src/acpi.c b/bios/seabios/src/acpi.c
rem index 95575a1..da7c9e9 100644
rem --- a/bios/seabios/src/acpi.c
rem +++ b/bios/seabios/src/acpi.c
rem @@ -673,9 +673,9 @@ acpi_bios_init(void)
rem     ACPI_INIT_TABLE(build_fadt(pci));
rem     ACPI_INIT_TABLE(build_ssdt());
rem     ACPI_INIT_TABLE(build_madt());
rem -   if (pci) {
rem +   //if (pci) {
rem        ACPI_INIT_TABLE(build_hpet());
rem -   }
rem +   //}
rem     ACPI_INIT_TABLE(build_srat());
rem
rem     u16 i, external_tables = qemu_cfg_acpi_additional_tables();

palacios/src/devices/Kconfig
palacios/src/devices/Makefile
palacios/src/devices/hpet.c [new file with mode: 0644]

index f08d5b2..da55c11 100644 (file)
@@ -100,6 +100,19 @@ config DEBUG_PIT
        help 
          Enable debugging for the PIT  
 
+config HPET
+    bool "HPET"
+    default n
+    help
+      Includes the High-Precision Event Timer Device
+
+config DEBUG_HPET
+    bool "HPET debugging"
+    depends on HPET && DEBUG_ON
+    help
+      Enable debugging for the HPET
+
+
 config I440FX
        bool "i440fx Northbridge"
        depends on PCI
index 0dbc07e..4531090 100644 (file)
@@ -2,6 +2,7 @@ obj-$(V3_CONFIG_APIC) += apic.o
 obj-$(V3_CONFIG_IO_APIC) += io_apic.o
 obj-$(V3_CONFIG_MPTABLE) += mptable.o
 obj-$(V3_CONFIG_PIT) += 8254.o
+obj-$(V3_CONFIG_HPET) += hpet.o
 obj-$(V3_CONFIG_PIC) += 8259a.o
 obj-$(V3_CONFIG_BOCHS_DEBUG) += bochs_debug.o
 obj-$(V3_CONFIG_GENERIC) += generic.o
diff --git a/palacios/src/devices/hpet.c b/palacios/src/devices/hpet.c
new file mode 100644 (file)
index 0000000..5e65cd9
--- /dev/null
@@ -0,0 +1,886 @@
+/* 
+ * This file is part of the Palacios Virtual Machine Monitor developed
+ * by the V3VEE Project with funding from the United States National 
+ * Science Foundation and the Department of Energy.  
+ *
+ * The V3VEE Project is a joint project between Northwestern University
+ * and the University of New Mexico.  You can find out more at 
+ * http://www.v3vee.org
+ *
+ * Copyright (c) 2014, Kyle C. Hale <kh@u.northwestern.edu>
+ * Copyright (c) 2014, The V3VEE Project <http://www.v3vee.org> 
+ * All rights reserved.
+ *
+ * Authors: Kyle C. Hale <kh@u.northwestern.edu>
+ *         
+ * Emulated HPET device
+ *
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
+ */
+
+#include <palacios/vmm.h>
+#include <palacios/vm_guest.h>
+#include <palacios/vmm_types.h>
+#include <palacios/vmm_queue.h>
+#include <palacios/vmm_lock.h>
+#include <palacios/vmm_debug.h>
+
+
+#ifndef V3_CONFIG_DEBUG_HPET
+#undef PrintDebug
+#define PrintDebug(fmt, args...)
+#endif
+
+
+#define HPET_REGION_SIZE       1024
+#define HPET_DEFAULT_BASE_ADDR 0xFED00000ULL
+#define HPET_NUM_TIMERS        3
+#define V3_VENDOR              0xA333
+#define NANOSECS               1000000000ULL          
+#define FEMTOSECS              1000000000000000ULL     
+
+#define HPET_TIMER_INT_ROUTE_CAP_SHIFT 32
+/* HPET irqs can be routed to IOAPIC [23..20] */
+#define HPET_TIMER_INT_ROUTE_CAP      (0x00f00000ULL \ << HPET_TIMER_INT_ROUTE_CAP_SHIFT)
+
+/* Memory-Mapped Register Offsets */
+#define GEN_CAP_REG_OFFSET       0x000 // r
+#define RSVD_OFFSET              0x008
+#define GEN_CFG_REG_OFFSET       0x010 // rw
+#define RSVD_OFFSET1             0x018
+#define GEN_ISR_OFFSET           0x020 // rw clear
+#define RSVD_OFFSET2             0x028
+#define MAIN_CNTR_VAL_REG_OFFSET 0x0F0 // rw
+#define RSVD_OFFSET3             0x0F8
+#define RSVD_OFFSET4             0x118
+#define RSVD_OFFSET5             0x138
+#define RSVD_OFFSET6             0x158
+#define RSVD_3_31_OFFSET         0x160
+#define TIMER_N_OFFSET_CFG(n)     (0x100 + (n) * 0x20) // rw
+#define TIMER_N_OFFSET_CMP(n)     (0x108 + (n) * 0x20) // rw
+#define TIMER_N_OFFSET_FSB_IRR(n) (0x110 + (n) * 0x20) // rw
+
+/* extract an architectural field for timer N */
+#define TIMER_N(f, addr) (((addr) - TIMER_N_OFFSET_##f(0)) / (TIMER_N_OFFSET_##f(1) - TIMER_N_OFFSET_##f(0)))
+
+/* utility macros */
+#define is_hpet_enabled(hpet)         (hpet->regs.cfg.enable_cnf)
+
+/* timer-specific utility macros */
+#define in_periodic_mode(hpet, n)     (hpet->regs.timers[n].caps.tn_type_cnf)
+#define timer_32bit(hpet, n)          (hpet->regs.timers[n].caps.tn_32mode_cnf)
+#define is_timer_enabled(hpet, n)     (hpet->regs.timers[n].caps.tn_int_enb_cnf)
+#define is_timer_ltrig(hpet, n)       (hpet->regs.timers[n].caps.tn_int_type_cnf)
+#define set_timer_bit(x, n)           ((x) |= (1UL << (n)))
+#define unset_timer_bit(x, n)         ((x) &= (~(1UL << (n))))
+
+/* flag macros */
+#define HPET_CFG_ENABLE 0x001
+#define HPET_CFG_LEGACY 0x002
+#define HPET_LEGACY_8254    2
+#define HPET_LEGACY_RTC     8
+
+#define HPET_TIMER_LEVEL       0x002
+#define HPET_TIMER_ENABLE      0x004
+#define HPET_TIMER_PERIODIC    0x008
+#define HPET_TIMER_PERIODIC_CAP    0x010
+#define HPET_TIMER_64BIT_CAP   0x020
+#define HPET_TIMER_SETVAL      0x040
+#define HPET_TIMER_32BIT       0x100
+#define HPET_TIMER_ROUTE       0x3e00
+#define HPET_TIMER_FSB     0x4000
+#define HPET_TIMER_FSB_CAP     0x8000
+#define HPET_TIMER_RESERVED    0xffff0081
+#define HPET_TIMER_ROUTE_SHIFT 9
+
+/* we're going to set the HPET timer freq. to be 1/16th rate of Palacios system time */
+#define HPET_PERIOD 16   
+
+#define SYS_TICKS_PER_NS(hpet)       ((hpet)->system_freq/NANOSECS)
+#define hpet_guest_time(hpet)        (v3_get_guest_time(&(hpet)->core->time_state) / (HPET_PERIOD*SYS_TICKS_PER_NS(hpet)))
+#define HPET_SMALL_WINDOW(hpet)      (((hpet)->system_freq >> 10) / HPET_PERIOD)
+
+#define tick_to_ns(hpet, tick)                  \
+         (((((tick) > (hpet)->max_ns_res) ?     \
+         ~0ULL : (tick) * (hpet)->ns_per_tick) >> 10))
+
+
+/* General Capabilities and ID Register */
+struct hpet_cap_reg {
+    union {
+        uint64_t value;
+        struct {
+            uint8_t rev_id;
+            uint8_t num_tim_cap    : 5;
+            uint8_t count_size_cap : 1;
+            uint8_t rsvd           : 1;
+            uint8_t leg_route_cap  : 1;
+            uint16_t vendor_id;
+            uint32_t counter_clk_period; 
+        } __attribute__((packed));
+    } __attribute__((packed));
+} __attribute__((packed));
+
+
+/* General Configuration Register */
+struct hpet_gen_cfg_reg {
+    union {
+        uint64_t value;
+        struct {
+            uint8_t enable_cnf : 1;
+            uint8_t leg_rt_cnf : 1;
+            uint8_t rsvd       : 6;
+            uint8_t rsvd2;
+            uint64_t rsvd3     : 48;
+        } __attribute__((packed));
+    } __attribute__((packed));
+} __attribute__((packed));
+
+
+/* General Interrupt Status Register */
+struct hpet_gen_irq_status_reg {
+    union {
+        uint64_t value;
+        struct {
+            uint8_t t0_int_sts : 1;
+            uint8_t t1_int_sts : 1;
+            uint8_t t2_int_sts : 1;
+            uint32_t rsvd      : 29;
+            uint32_t rsvd2;
+        } __attribute__((packed));
+    } __attribute__((packed));
+} __attribute__((packed));
+
+/* Timer N Configuration and Capabilities Register */
+struct timer_cfg_cap_reg {
+    union {
+        uint64_t value;
+        struct {
+            uint8_t rsvd : 1;
+            uint8_t tn_int_type_cnf    : 1;
+            uint8_t tn_int_enb_cnf     : 1;
+            uint8_t tn_type_cnf        : 1;
+            uint8_t tn_per_int_cap     : 1;
+            uint8_t tn_size_cap        : 1;
+            uint8_t tn_val_set_cnf     : 1;
+            uint8_t rsvd2              : 1;
+            uint8_t tn_32mode_cnf      : 1;
+            uint8_t tn_int_route_cnf   : 5;
+            uint8_t tn_fsb_en_cnf      : 1;
+            uint8_t tn_fsb_int_del_cap : 1;
+            uint16_t rsvd3;
+            uint32_t tn_int_route_cap;
+        } __attribute__((packed));
+    } __attribute__((packed));
+} __attribute__((packed));
+
+
+/* Timer N Comparator Register */
+struct timer_comp_reg {
+    union {
+        uint64_t value;
+        struct {
+            uint32_t lo;
+            uint32_t hi;
+        } __attribute__((packed));
+    } __attribute__((packed));
+} __attribute__((packed));
+
+/* 
+ * Timer N FSB Interrupt Route Reigster
+ *
+ * not supported 
+ */
+struct timer_fsb_int_route_reg {
+    union {
+        uint64_t value;
+        struct {
+            uint32_t tn_fsb_int_val;
+            uint32_t tn_fsb_int_addr;
+        } __attribute__((packed));
+    } __attribute__((packed));
+} __attribute__((packed));
+
+
+/* architectural state for timer N */
+struct timer_regs {
+    struct timer_cfg_cap_reg       caps;
+    struct timer_comp_reg          comp;
+    struct timer_fsb_int_route_reg fsb_int_route; // not supported
+    uint64_t                       rsvd;
+} __attribute__((packed));
+
+
+/* hidden state for timer N */
+struct hpet_timer_state {
+    uint_t              timer_num; 
+    uint_t              oneshot;
+    uint_t              irq;
+    struct hpet_state * hpet;
+    struct v3_timer *   timer;
+};
+
+
+struct hpet_state {
+
+    /* archietected registers */
+    union {
+       uint32_t raw_regs[HPET_REGION_SIZE/4]; // 1K total is mapped in
+       struct {
+
+          /* memory mapped registers */
+          struct hpet_cap_reg            caps;                    // 0x000
+          uint64_t                       rsvd1;                   // 0x008
+          struct hpet_gen_cfg_reg        cfg;                     // 0x010
+          uint64_t                       rsvd2;                   // 0x018
+          struct hpet_gen_irq_status_reg irq_status;              // 0x020
+          uint64_t                       rsvd3[50];               // 0x028
+          uint64_t                       main_counter;            // 0x0f0
+          uint64_t                       rsvd4;                   // 0x0f8
+          struct timer_regs              timers[HPET_NUM_TIMERS]; // 0x100, 0x120, etc.
+
+       } __attribute__((packed)) regs;
+    } __attribute__((packed));
+
+    /* hidden state */
+    addr_t                  base_addr;
+    uint64_t                counter_offset;
+    struct guest_info *     core;
+    struct hpet_timer_state timer_states[HPET_NUM_TIMERS];
+    v3_lock_t               lock;
+
+    /* time-keeping */
+    uint64_t                system_freq; 
+    uint64_t                ns_per_tick; // how many nanosecs per HPET tick
+    uint64_t                max_ns_res;  // max number of ticks we can rep in nanosec */
+
+    /* this is the initial value written to the comparator 
+     * for periodic timers, we increment the comparator by this 
+     * many ticks after raising each irq 
+     */
+    uint64_t period[HPET_NUM_TIMERS]; 
+
+    /*
+     * shadow comparator
+     * for each timer, we compare the main counter
+     * against this. when it matches, raise irq 
+     */
+    uint64_t comparator[HPET_NUM_TIMERS];
+
+};
+
+
+static int hpet_read (struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data);
+static int hpet_write (struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data);
+
+
+/* return first set bit */
+static inline unsigned int 
+first_bit (unsigned long x)
+{
+    __asm__ __volatile__ ("bsf %1,%0" : "=r" (x) : "r" (x));
+    return (unsigned int)x;
+}
+
+
+static void
+remove_hpet_timers (struct hpet_state * hpet) 
+{
+    int i;
+
+    for (i = 0; i < HPET_NUM_TIMERS; i++) {
+        if (hpet->timer_states[i].timer) {
+            v3_remove_timer(hpet->core, hpet->timer_states[i].timer);
+            hpet->timer_states[i].timer = NULL;
+        }
+    }
+}
+
+
+/* make sure accesses don't go across register boundaries */
+static inline int 
+check_hpet_access (struct guest_info * core, addr_t guest_addr, uint_t length) 
+{
+    /* is access aligned to the access length? */
+    if (guest_addr & (--length) || length > 8) {
+        PrintError(core->vm_info, core, "HPET: access across register boundary\n");
+        return -1;
+    }
+    return 0;
+}
+
+
+/* get the value that we should return when main counter is read */
+static inline uint64_t 
+read_hpet_counter (struct hpet_state * hpet) 
+{ 
+    if (is_hpet_enabled(hpet)) {
+        return hpet_guest_time(hpet) + hpet->counter_offset;
+    } else {
+        return hpet->regs.main_counter;
+    }
+
+}
+
+
+/* return the *most recent* comparator value 
+ * as a side-effect, the comparator for timer N is
+ * updated to reflect any missed timer interrupts */
+static uint64_t 
+read_hpet_comparator (struct hpet_state * hpet, unsigned int timer_n) 
+{
+    uint64_t comp;
+    uint64_t elapsed;
+
+    comp = hpet->comparator[timer_n];
+    
+    if (in_periodic_mode(hpet, timer_n)) {
+        // advance comp by # of periods since last update
+        uint64_t period = hpet->period[timer_n];
+        if (period) {
+            elapsed = read_hpet_counter(hpet) + period - 1 - comp;
+            comp += (elapsed / period) * period;
+            hpet->comparator[timer_n] = comp;
+        }
+    }
+
+    // if we're in 32-bit mode, truncate
+    if (timer_32bit(hpet, timer_n)) {
+        comp = (uint32_t)comp;
+    }
+
+    hpet->regs.timers[timer_n].comp.value = comp;
+    return comp;
+}
+
+
+static void 
+init_hpet_state (struct hpet_state * hpet) 
+{
+    int i;
+
+    hpet->base_addr = HPET_DEFAULT_BASE_ADDR;
+
+    memset(hpet->raw_regs, 0, HPET_REGION_SIZE);
+
+    hpet->regs.caps.rev_id             = 1; // must be non-zero
+    hpet->regs.caps.num_tim_cap        = HPET_NUM_TIMERS - 1;
+    hpet->regs.caps.count_size_cap     = 1; // 64-bit mode
+    hpet->regs.caps.leg_route_cap      = 1; // we support legacy interrupt routing
+    hpet->regs.caps.vendor_id          = V3_VENDOR;
+
+    // # of femtosecs per HPET tick, HPET frequency is 1/16th of palacios time
+    hpet->regs.caps.counter_clk_period = FEMTOSECS*HPET_PERIOD/hpet->system_freq;
+
+    // timer-specific archictectural state 
+    for (i = 0; i < HPET_NUM_TIMERS; i++) {
+            hpet->regs.timers[i].caps.tn_int_route_cap = HPET_TIMER_INT_ROUTE_CAP_SHIFT; // we support routing through the IOAPIC
+            hpet->regs.timers[i].caps.tn_size_cap      = 1; // 64-bit
+            hpet->regs.timers[i].caps.tn_per_int_cap   = 1; // this timer supports periodic mode
+            hpet->regs.timers[i].comp.value            = ~0ULL; // initial value for comparator: default value should be all 1's
+    }
+}
+
+
+static inline uint64_t
+hpet_get_reg (struct hpet_state * hpet, addr_t guest_addr) 
+{
+    /* we don't care about the lower 3 bits at this point */
+    guest_addr &= ~7;
+
+    switch (guest_addr) {
+        case GEN_CAP_REG_OFFSET: 
+            return hpet->regs.caps.value;
+        case GEN_CFG_REG_OFFSET:
+            return hpet->regs.cfg.value;
+        case GEN_ISR_OFFSET:
+            return hpet->regs.irq_status.value;
+        case MAIN_CNTR_VAL_REG_OFFSET:
+            return read_hpet_counter(hpet);
+        case TIMER_N_OFFSET_CFG(0): 
+        case TIMER_N_OFFSET_CFG(1): 
+        case TIMER_N_OFFSET_CFG(2): 
+            return hpet->regs.timers[TIMER_N(CFG, guest_addr)].caps.value;
+        case TIMER_N_OFFSET_CMP(0):
+        case TIMER_N_OFFSET_CMP(1):
+        case TIMER_N_OFFSET_CMP(2):
+             return read_hpet_comparator(hpet, TIMER_N(CMP, guest_addr));
+        case TIMER_N_OFFSET_FSB_IRR(0):
+        case TIMER_N_OFFSET_FSB_IRR(1):
+        case TIMER_N_OFFSET_FSB_IRR(2):
+             return hpet->regs.timers[TIMER_N(FSB_IRR, guest_addr)].fsb_int_route.value;
+    }
+
+    return 0;
+}
+
+
+static int 
+hpet_read (struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data) 
+{
+    struct hpet_state * hpet = (struct hpet_state *)priv_data;
+    uint_t flags = 0;
+    uint64_t reg;
+    unsigned long res;
+
+    /* we only care about the lower 9 bits */
+    guest_addr &= HPET_REGION_SIZE - 1;
+
+    if (check_hpet_access(core, guest_addr, length) != 0) {
+        res = ~0UL;
+        goto out;
+    }
+
+    flags = v3_lock_irqsave(hpet->lock);
+
+    reg = hpet_get_reg(hpet, guest_addr);
+
+    res = reg;
+
+    /* shift and mask out high-order bits if this is smaller than a 64-bit read */
+    if (length != 8) {
+        res = (reg >> ((guest_addr & 7) * 8)) & ((1ULL << (length * 8)) - 1);
+    }
+
+    v3_unlock_irqrestore(hpet->lock, flags);
+out:
+    PrintDebug(core->vm_info, core, "HPET: core %u: at %p: Read HPET address space (%p), length=%u, val=0x%lx\n",
+              core->vcpu_id, hpet, (void *)guest_addr, length, res);
+    *(unsigned long*)dst = res;
+    return 0;
+}
+
+
+/* timer functions */
+
+static void 
+hpet_update_time (struct guest_info * core, 
+                             uint64_t cpu_cycles, uint64_t cpu_freq, 
+                             void * priv_data) 
+{
+    struct hpet_timer_state * htimer = (struct hpet_timer_state *)(priv_data);
+    struct hpet_state * hpet = htimer->hpet;
+    unsigned int nr = htimer->timer_num;
+    uint64_t hpet_ticks = read_hpet_counter(hpet);
+
+    /* KCH TODO: handle missed timer interrupts?? */
+    if (hpet_ticks >= hpet->comparator[nr]) {
+        v3_raise_irq(core->vm_info, htimer->timer_num);
+
+        /* we do this to update the comparator value,
+         * e.g. in case we missed an interrupt */
+        read_hpet_comparator(hpet, nr);
+    } 
+}
+
+
+static struct v3_timer_ops timer_ops = {
+    .update_timer = hpet_update_time,
+};
+
+
+static void 
+hpet_stop_timer (struct hpet_state * hpet, unsigned int n)
+{
+
+    V3_ASSERT(hpet->core->vm_info, hpet->core, n < HPET_NUM_TIMERS);
+    if (hpet->timer_states[n].timer) {
+        v3_remove_timer(hpet->core, hpet->timer_states[n].timer);
+        hpet->timer_states[n].timer = NULL;
+    }
+    // synch the comparator for reads that may happen while we're stopped
+    read_hpet_comparator(hpet, n);
+}
+
+
+static void 
+hpet_start_timer (struct hpet_state * hpet, unsigned int n) 
+{
+    uint64_t timer_comp, tick;
+
+    V3_ASSERT(hpet->core->vm_info, hpet->core, n < HPET_NUM_TIMERS);
+    if (n == 0 && hpet->regs.cfg.leg_rt_cnf) {
+        /* KCH TODO: the PIT shouldn't be generating irqs on chan 0 if this bit is set 
+         * AFAIK we don't have an interface to do this just yet
+         */
+    }
+
+    if (!is_timer_enabled(hpet, n)) {
+        return;
+    }
+
+    timer_comp = read_hpet_comparator(hpet, n);
+    tick = read_hpet_counter(hpet);
+
+    if (timer_32bit(hpet, n)) {
+        timer_comp = (uint32_t)read_hpet_comparator(hpet, n);
+        tick = (uint32_t)read_hpet_counter(hpet);
+    }
+        
+    /* if LegacyReplacementRoute is set, 
+     * "Timer 0 will be routed to IRQ0 in Non-APIC or IRQ2 in the I/O APIC
+     *  Timer 1 will be routed to IRQ8 in Non-APIC or IRQ8 in the I/O APIC
+     *  Timer 2-n will be routed as per the routing in the timer n config registers"
+     * see IA-PC HPET Spec pp.12-13
+     */
+    if ((n <= 1) && (hpet->regs.cfg.leg_rt_cnf)) {
+        hpet->timer_states[n].irq = (n == 0) ? 0 : 8;
+    } else {
+        hpet->timer_states[n].irq = hpet->regs.timers[n].caps.tn_int_route_cnf;
+    }
+
+    hpet->timer_states[n].oneshot = !in_periodic_mode(hpet, n);
+    hpet->timer_states[n].timer   = v3_add_timer(hpet->core, &timer_ops, &hpet->timer_states[n]);
+
+    if (hpet->timer_states[n].timer == NULL) {
+        PrintError(hpet->core->vm_info, hpet->core, "HPET: Failed to attach HPET timer %d to core %d\n", n, hpet->core->vcpu_id);
+        return;
+    }
+}
+
+
+/* this ensures that only allowed bits in a given register are 
+ * changed */
+static inline uint64_t 
+hpet_mask_write (uint64_t new, uint64_t old, uint64_t mask) 
+{
+    new &= mask;
+    new |= old & ~mask;
+    return new;
+}
+
+
+static int 
+hpet_write (struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data) 
+{
+    struct hpet_state * hpet = (struct hpet_state *)(priv_data);
+    uint_t flags = 0;
+    uint64_t old, new;
+    unsigned long to_start = 0;
+    unsigned long to_stop  = 0;
+    int timer_n, i;
+
+#define mark_timer_to_start(n)     (set_timer_bit(to_start, n))
+#define mark_timer_to_stop(n)      (set_timer_bit(to_stop, n))
+#define mark_timer_to_reset(n)     (mark_timer_to_stop(n), mark_timer_to_start(n))
+
+    guest_addr &= HPET_REGION_SIZE - 1;
+
+    PrintDebug(core->vm_info, core, "HPET: core %u: write to address space (%p) (val=%x)\n", 
+              core->vcpu_id, (void *)guest_addr, *(uint32_t *)src);
+
+    if (check_hpet_access(core, guest_addr, length) != 0) {
+        goto out_prelock;
+    }
+
+    flags = v3_lock_irqsave(hpet->lock);
+
+    old = hpet_get_reg(hpet, guest_addr);
+    new = *(unsigned long *)src;
+
+    /* this is a trick to convert a non-8byte write into one,
+     * making sure that we only change the bits of the word
+     * within the length of the write */
+    if (length != 8) {
+        // say it's a 2-byte write to offset 6
+        new = hpet_mask_write(new << (guest_addr & 7) * 8,  // left shift the value by 48
+                              old,
+                             ((1ULL << (length*8)) - 1) << ((guest_addr & 7) * 8)); // then mask out all but the last 2 bytes
+    }
+
+    switch (guest_addr & ~7) {
+
+        case GEN_CFG_REG_OFFSET:
+            hpet->regs.cfg.value = hpet_mask_write(new, old, 0x3);
+            /* we're starting the timer */
+            if ( !(old & HPET_CFG_ENABLE) && (new & HPET_CFG_ENABLE) ) {
+
+                hpet->counter_offset = hpet->regs.main_counter - hpet_guest_time(hpet);
+                //hpet->counter_offset = hpet_guest_time(hpet) - hpet->regs.main_counter;
+                PrintDebug(core->vm_info, core, "HPET: starting the hpet, setting offset to 0x%llx\n", hpet->counter_offset);
+
+                for (i = 0; i < HPET_NUM_TIMERS; i++) {
+                    hpet->comparator[i] = timer_32bit(hpet, i) ?
+                                            (uint32_t)hpet->regs.timers[i].comp.value :
+                                                      hpet->regs.timers[i].comp.value;
+                    if (is_timer_enabled(hpet, i)) {
+                        mark_timer_to_start(i);
+                    }
+                }
+
+            /* we're stopping the timer */
+            } else if ( (old & HPET_CFG_ENABLE) && !(new & HPET_CFG_ENABLE) ) {
+                PrintDebug(core->vm_info, core, "HPET: stopping the hpet\n");
+
+                hpet->regs.main_counter = hpet->counter_offset + hpet_guest_time(hpet);
+
+                for (i = 0; i < HPET_NUM_TIMERS; i++) {
+                    if (is_timer_enabled(hpet, i)) {
+                        mark_timer_to_stop(i);
+                    }
+                }
+
+            }
+            break;
+
+        case MAIN_CNTR_VAL_REG_OFFSET:
+            hpet->regs.main_counter = new;
+            PrintDebug(core->vm_info, core, "HPET: writing the main counter (0x%llx)\n", new);
+            if (is_hpet_enabled(hpet)) {
+                PrintError(core->vm_info, core, "HPET: writing main counter in unhalted state\n");
+                for (i = 0; i < HPET_NUM_TIMERS; i++) {
+                    if (is_timer_enabled(hpet, i)) {
+                        mark_timer_to_reset(i);
+                    }
+                }
+            }
+
+            break;
+        case TIMER_N_OFFSET_CFG(0): 
+        case TIMER_N_OFFSET_CFG(1): 
+        case TIMER_N_OFFSET_CFG(2): 
+            /* KCH: software should set the main counter to 0 before setting a comparator */
+            timer_n = TIMER_N(CFG, guest_addr);
+            hpet->regs.timers[timer_n].caps.value = hpet_mask_write(new, old, 0x3F4E);
+
+            if (is_timer_ltrig(hpet, timer_n)) {
+                PrintError(core->vm_info, core, "HPET: level-triggered interrupts not supported\n");
+                goto out_err;
+            }
+
+            /* are we switching the timer to 32-bit mode? */
+            if (new & HPET_TIMER_32BIT) {
+                hpet->regs.timers[timer_n].comp.value = (uint32_t) hpet->regs.timers[timer_n].comp.value;
+                hpet->period[timer_n] = (uint32_t)hpet->period[timer_n];
+            }
+
+            if (is_hpet_enabled(hpet)) {
+
+                if (new & HPET_TIMER_ENABLE) {
+                        /* we're switching to or from periodic mode, reset */
+                        if ((new ^ old) & HPET_TIMER_PERIODIC) {
+                            PrintDebug(core->vm_info, core, "HPET: changing periodic mode on timer %d\n", timer_n);
+                            mark_timer_to_reset(timer_n);
+                        /* we're switching from 64 to 32, reset */
+                        } else if ((new & HPET_TIMER_32BIT) && !(old & HPET_TIMER_32BIT)) {
+                            PrintDebug(core->vm_info, core, "HPET: changing timer %d from 32-bit to 64-bit mode\n", timer_n);
+                            mark_timer_to_reset(timer_n);
+                        } else if (!(old & HPET_TIMER_ENABLE)) {
+                            PrintDebug(core->vm_info, core, "HPET: activating timer %d\n", timer_n);
+                            mark_timer_to_start(timer_n);
+                        }
+
+                } else if (old & HPET_TIMER_ENABLE) {
+                    PrintDebug(core->vm_info, core, "HPET: deactivating timer %d\n", timer_n);
+                    mark_timer_to_stop(timer_n);
+                }
+            }
+
+            break;
+        case TIMER_N_OFFSET_CMP(0):
+        case TIMER_N_OFFSET_CMP(1):
+        case TIMER_N_OFFSET_CMP(2):
+            timer_n = TIMER_N(CMP, guest_addr);
+
+            if (timer_32bit(hpet, timer_n)) {
+                new = (uint32_t)new;
+            }
+            
+            PrintDebug(core->vm_info, core, "HPET: writing comparator reg on timer %d (val=0x%llx)\n", timer_n, new);
+
+            hpet->regs.timers[timer_n].comp.value = new;
+            
+            /* this bit means sw can set the comp directly
+             * The bit gets cleared on a write to the comp
+             */
+            if (hpet->regs.timers[timer_n].caps.tn_val_set_cnf) {
+                hpet->regs.timers[timer_n].caps.tn_val_set_cnf = 0;
+            } else if (in_periodic_mode(hpet, timer_n)) {
+                    
+                /* set bounds on the period */
+                if (tick_to_ns(hpet, new) < 100000) {
+                    new = (100000 << 10) / hpet->ns_per_tick;
+                }
+
+                new &= ((timer_32bit(hpet, timer_n)) ? ~0UL : ~0ULL) >> 1;
+                hpet->period[timer_n] = new;
+                PrintDebug(core->vm_info, core, "HPET: period for timer %d to (0x%llx)\n", timer_n, hpet->period[timer_n]);
+            }
+
+            hpet->comparator[timer_n] = new;
+
+            if (is_hpet_enabled(hpet) && is_timer_enabled(hpet, timer_n)) {
+                mark_timer_to_reset(timer_n);
+            }
+            
+            break;
+        case TIMER_N_OFFSET_FSB_IRR(0):
+        case TIMER_N_OFFSET_FSB_IRR(1):
+        case TIMER_N_OFFSET_FSB_IRR(2):
+            timer_n = TIMER_N(FSB_IRR, guest_addr);
+            PrintDebug(core->vm_info, core, "HPET: writing to fsb_irr reg for timer %d (val=0x%llx\n", timer_n, new);
+            hpet->regs.timers[timer_n].fsb_int_route.value = new;
+            break;
+
+        default:
+            /* just ignore writes to unsupported regs */
+           break;
+    }
+
+    /* now we update the timers that we marked */
+    while (to_stop)
+    {
+        i = first_bit(to_stop);
+        unset_timer_bit(to_stop, i);
+        hpet_stop_timer(hpet, i);
+    }
+
+    while (to_start)
+    {
+        i = first_bit(to_start);
+        unset_timer_bit(to_start, i);
+        hpet_start_timer(hpet, i);
+    }
+
+#undef mark_timer_to_start
+#undef mark_timer_to_stop
+#undef mark_timer_to_reset
+
+    v3_unlock_irqrestore(hpet->lock, flags);
+out_prelock:
+    return 0;
+out_err:
+    v3_unlock_irqrestore(hpet->lock, flags);
+    return -1;
+}
+
+
+static int 
+hpet_free (struct hpet_state * hpet) 
+{
+    struct v3_vm_info * vm   = NULL;
+       struct guest_info * core = NULL;
+
+    if (!hpet) {
+        return -1;
+    }
+
+    core = hpet->core;
+       vm   = core->vm_info;
+    
+    remove_hpet_timers(hpet);
+
+    v3_lock_deinit(&(hpet->lock));
+
+    if (v3_unhook_mem(vm, core->vcpu_id, hpet->base_addr)) {
+        PrintError(vm, VCORE_NONE, "HPET: could not unhook memory region\n");
+    }
+
+    V3_Free(hpet);
+    return 0;
+}
+
+
+#ifdef V3_CONFIG_CHECKPOINT
+
+static int 
+hpet_save (struct v3_chkpt_ctx * ctx, void * private_data) 
+{
+    PrintError(VM_NONE, VCORE_NONE, "Unimplemented\n");
+    return -1;
+}
+
+
+static int 
+hpet_load (struct v3_chkpt_ctx * ctx, void * private_data) 
+{
+    PrintError(VM_NONE, VCORE_NONE, "Unimplemented\n");
+    return -1;
+}
+
+#endif
+
+static struct v3_device_ops dev_ops = {
+    .free = (int (*)(void *))hpet_free,
+#ifdef V3_CONFIG_CHECKPOINT
+    .save = hpet_save,
+    .load = hpet_load
+#endif
+};
+
+
+static int 
+hpet_init (struct v3_vm_info * vm, v3_cfg_tree_t * cfg) 
+{
+    char * dev_id = v3_cfg_val(cfg, "ID");
+    struct hpet_state * hpet;
+    struct guest_info * core = NULL;
+    struct vm_device * dev = NULL;
+    int i;
+
+    core = &vm->cores[0];
+
+    PrintDebug(vm, VCORE_NONE, "HPET: Creating HPET\n");
+
+    hpet = (struct hpet_state *)V3_Malloc(sizeof(struct hpet_state));
+
+    if (!hpet) {
+        PrintError(vm, VCORE_NONE, "HPET: Failed to allocate space for HPET\n");
+        return -1;
+    }
+
+    hpet->core = core;
+
+    dev = v3_add_device(vm, dev_id, &dev_ops, hpet);
+
+    if (dev == NULL) {
+        PrintError(vm, VCORE_NONE, "HPET: Could not attach device %s\n", dev_id);
+        goto out_err;
+    }
+
+    hpet->system_freq = core->time_state.host_cpu_freq * 1000; // convert from kHz
+    hpet->ns_per_tick = ((NANOSECS * HPET_PERIOD) << 10) / hpet->system_freq;
+    hpet->max_ns_res  = ~0ULL / hpet->ns_per_tick;
+
+    PrintDebug(core->vm_info, core, "HPET: System frequency detected as %lluHz\n", hpet->system_freq);
+
+    v3_lock_init(&(hpet->lock)); 
+
+    PrintDebug(core->vm_info, core, "HPET: Initializing %d timers\n", HPET_NUM_TIMERS);
+
+    for (i = 0; i < HPET_NUM_TIMERS; i++) {
+        hpet->timer_states[i].timer_num = i;
+        hpet->timer_states[i].hpet      = hpet;
+        hpet->timer_states[i].timer     = NULL;
+    }
+
+    /* init architecturally visible state */
+       init_hpet_state(hpet);
+
+    PrintDebug(core->vm_info, core "HPET: Hooking HPET mem region at %p\n", (void*)hpet->base_addr);
+       if (v3_hook_full_mem(vm, 
+                         V3_MEM_CORE_ANY, 
+                         hpet->base_addr, 
+                         hpet->base_addr + HPET_REGION_SIZE, 
+                         hpet_read, 
+                         hpet_write, 
+                         hpet) < 0) {
+        PrintError(vm, VCORE_NONE, "HPET: Failed to map HPET memory region\n");
+        goto out_err1;
+    }
+
+    PrintDebug(vm, VCORE_NONE, "HPET: Initialization complete\n");
+
+    return 0;
+
+out_err1: 
+    v3_lock_deinit(&(hpet->lock));
+    v3_remove_device(dev);
+out_err:
+    V3_Free(hpet);
+    return -1;
+}
+
+device_register("HPET", hpet_init)