/* * 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 * Copyright (c) 2014, The V3VEE Project * All rights reserved. * * Authors: Kyle C. Hale * * Emulated HPET device * * This is free software. You are permitted to use, * redistribute, and modify it as specified in the file "V3VEE_LICENSE". */ #include #include #include #include #include #include #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 0; } static int hpet_load (struct v3_chkpt_ctx * ctx, void * private_data) { PrintError(VM_NONE, VCORE_NONE, "Unimplemented\n"); return 0; } #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)