2 * This file is part of the Palacios Virtual Machine Monitor developed
3 * by the V3VEE Project with funding from the United States National
4 * Science Foundation and the Department of Energy.
6 * The V3VEE Project is a joint project between Northwestern University
7 * and the University of New Mexico. You can find out more at
10 * Copyright (c) 2014, Kyle C. Hale <kh@u.northwestern.edu>
11 * Copyright (c) 2014, The V3VEE Project <http://www.v3vee.org>
12 * All rights reserved.
14 * Authors: Kyle C. Hale <kh@u.northwestern.edu>
16 * Emulated HPET device
18 * This is free software. You are permitted to use,
19 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
22 #include <palacios/vmm.h>
23 #include <palacios/vm_guest.h>
24 #include <palacios/vmm_types.h>
25 #include <palacios/vmm_queue.h>
26 #include <palacios/vmm_lock.h>
27 #include <palacios/vmm_debug.h>
30 #ifndef V3_CONFIG_DEBUG_HPET
32 #define PrintDebug(fmt, args...)
36 #define HPET_REGION_SIZE 1024
37 #define HPET_DEFAULT_BASE_ADDR 0xFED00000ULL
38 #define HPET_NUM_TIMERS 3
39 #define V3_VENDOR 0xA333
40 #define NANOSECS 1000000000ULL
41 #define FEMTOSECS 1000000000000000ULL
43 #define HPET_TIMER_INT_ROUTE_CAP_SHIFT 32
44 /* HPET irqs can be routed to IOAPIC [23..20] */
45 #define HPET_TIMER_INT_ROUTE_CAP (0x00f00000ULL \ << HPET_TIMER_INT_ROUTE_CAP_SHIFT)
47 /* Memory-Mapped Register Offsets */
48 #define GEN_CAP_REG_OFFSET 0x000 // r
49 #define RSVD_OFFSET 0x008
50 #define GEN_CFG_REG_OFFSET 0x010 // rw
51 #define RSVD_OFFSET1 0x018
52 #define GEN_ISR_OFFSET 0x020 // rw clear
53 #define RSVD_OFFSET2 0x028
54 #define MAIN_CNTR_VAL_REG_OFFSET 0x0F0 // rw
55 #define RSVD_OFFSET3 0x0F8
56 #define RSVD_OFFSET4 0x118
57 #define RSVD_OFFSET5 0x138
58 #define RSVD_OFFSET6 0x158
59 #define RSVD_3_31_OFFSET 0x160
60 #define TIMER_N_OFFSET_CFG(n) (0x100 + (n) * 0x20) // rw
61 #define TIMER_N_OFFSET_CMP(n) (0x108 + (n) * 0x20) // rw
62 #define TIMER_N_OFFSET_FSB_IRR(n) (0x110 + (n) * 0x20) // rw
64 /* extract an architectural field for timer N */
65 #define TIMER_N(f, addr) (((addr) - TIMER_N_OFFSET_##f(0)) / (TIMER_N_OFFSET_##f(1) - TIMER_N_OFFSET_##f(0)))
68 #define is_hpet_enabled(hpet) (hpet->regs.cfg.enable_cnf)
70 /* timer-specific utility macros */
71 #define in_periodic_mode(hpet, n) (hpet->regs.timers[n].caps.tn_type_cnf)
72 #define timer_32bit(hpet, n) (hpet->regs.timers[n].caps.tn_32mode_cnf)
73 #define is_timer_enabled(hpet, n) (hpet->regs.timers[n].caps.tn_int_enb_cnf)
74 #define is_timer_ltrig(hpet, n) (hpet->regs.timers[n].caps.tn_int_type_cnf)
75 #define set_timer_bit(x, n) ((x) |= (1UL << (n)))
76 #define unset_timer_bit(x, n) ((x) &= (~(1UL << (n))))
79 #define HPET_CFG_ENABLE 0x001
80 #define HPET_CFG_LEGACY 0x002
81 #define HPET_LEGACY_8254 2
82 #define HPET_LEGACY_RTC 8
84 #define HPET_TIMER_LEVEL 0x002
85 #define HPET_TIMER_ENABLE 0x004
86 #define HPET_TIMER_PERIODIC 0x008
87 #define HPET_TIMER_PERIODIC_CAP 0x010
88 #define HPET_TIMER_64BIT_CAP 0x020
89 #define HPET_TIMER_SETVAL 0x040
90 #define HPET_TIMER_32BIT 0x100
91 #define HPET_TIMER_ROUTE 0x3e00
92 #define HPET_TIMER_FSB 0x4000
93 #define HPET_TIMER_FSB_CAP 0x8000
94 #define HPET_TIMER_RESERVED 0xffff0081
95 #define HPET_TIMER_ROUTE_SHIFT 9
97 /* we're going to set the HPET timer freq. to be 1/16th rate of Palacios system time */
98 #define HPET_PERIOD 16
100 #define SYS_TICKS_PER_NS(hpet) ((hpet)->system_freq/NANOSECS)
101 #define hpet_guest_time(hpet) (v3_get_guest_time(&(hpet)->core->time_state) / (HPET_PERIOD*SYS_TICKS_PER_NS(hpet)))
102 #define HPET_SMALL_WINDOW(hpet) (((hpet)->system_freq >> 10) / HPET_PERIOD)
104 #define tick_to_ns(hpet, tick) \
105 (((((tick) > (hpet)->max_ns_res) ? \
106 ~0ULL : (tick) * (hpet)->ns_per_tick) >> 10))
109 /* General Capabilities and ID Register */
110 struct hpet_cap_reg {
115 uint8_t num_tim_cap : 5;
116 uint8_t count_size_cap : 1;
118 uint8_t leg_route_cap : 1;
120 uint32_t counter_clk_period;
121 } __attribute__((packed));
122 } __attribute__((packed));
123 } __attribute__((packed));
126 /* General Configuration Register */
127 struct hpet_gen_cfg_reg {
131 uint8_t enable_cnf : 1;
132 uint8_t leg_rt_cnf : 1;
136 } __attribute__((packed));
137 } __attribute__((packed));
138 } __attribute__((packed));
141 /* General Interrupt Status Register */
142 struct hpet_gen_irq_status_reg {
146 uint8_t t0_int_sts : 1;
147 uint8_t t1_int_sts : 1;
148 uint8_t t2_int_sts : 1;
151 } __attribute__((packed));
152 } __attribute__((packed));
153 } __attribute__((packed));
155 /* Timer N Configuration and Capabilities Register */
156 struct timer_cfg_cap_reg {
161 uint8_t tn_int_type_cnf : 1;
162 uint8_t tn_int_enb_cnf : 1;
163 uint8_t tn_type_cnf : 1;
164 uint8_t tn_per_int_cap : 1;
165 uint8_t tn_size_cap : 1;
166 uint8_t tn_val_set_cnf : 1;
168 uint8_t tn_32mode_cnf : 1;
169 uint8_t tn_int_route_cnf : 5;
170 uint8_t tn_fsb_en_cnf : 1;
171 uint8_t tn_fsb_int_del_cap : 1;
173 uint32_t tn_int_route_cap;
174 } __attribute__((packed));
175 } __attribute__((packed));
176 } __attribute__((packed));
179 /* Timer N Comparator Register */
180 struct timer_comp_reg {
186 } __attribute__((packed));
187 } __attribute__((packed));
188 } __attribute__((packed));
191 * Timer N FSB Interrupt Route Reigster
195 struct timer_fsb_int_route_reg {
199 uint32_t tn_fsb_int_val;
200 uint32_t tn_fsb_int_addr;
201 } __attribute__((packed));
202 } __attribute__((packed));
203 } __attribute__((packed));
206 /* architectural state for timer N */
208 struct timer_cfg_cap_reg caps;
209 struct timer_comp_reg comp;
210 struct timer_fsb_int_route_reg fsb_int_route; // not supported
212 } __attribute__((packed));
215 /* hidden state for timer N */
216 struct hpet_timer_state {
220 struct hpet_state * hpet;
221 struct v3_timer * timer;
227 /* archietected registers */
229 uint32_t raw_regs[HPET_REGION_SIZE/4]; // 1K total is mapped in
232 /* memory mapped registers */
233 struct hpet_cap_reg caps; // 0x000
234 uint64_t rsvd1; // 0x008
235 struct hpet_gen_cfg_reg cfg; // 0x010
236 uint64_t rsvd2; // 0x018
237 struct hpet_gen_irq_status_reg irq_status; // 0x020
238 uint64_t rsvd3[50]; // 0x028
239 uint64_t main_counter; // 0x0f0
240 uint64_t rsvd4; // 0x0f8
241 struct timer_regs timers[HPET_NUM_TIMERS]; // 0x100, 0x120, etc.
243 } __attribute__((packed)) regs;
244 } __attribute__((packed));
248 uint64_t counter_offset;
249 struct guest_info * core;
250 struct hpet_timer_state timer_states[HPET_NUM_TIMERS];
254 uint64_t system_freq;
255 uint64_t ns_per_tick; // how many nanosecs per HPET tick
256 uint64_t max_ns_res; // max number of ticks we can rep in nanosec */
258 /* this is the initial value written to the comparator
259 * for periodic timers, we increment the comparator by this
260 * many ticks after raising each irq
262 uint64_t period[HPET_NUM_TIMERS];
266 * for each timer, we compare the main counter
267 * against this. when it matches, raise irq
269 uint64_t comparator[HPET_NUM_TIMERS];
274 static int hpet_read (struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data);
275 static int hpet_write (struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data);
278 /* return first set bit */
279 static inline unsigned int
280 first_bit (unsigned long x)
282 __asm__ __volatile__ ("bsf %1,%0" : "=r" (x) : "r" (x));
283 return (unsigned int)x;
288 remove_hpet_timers (struct hpet_state * hpet)
292 for (i = 0; i < HPET_NUM_TIMERS; i++) {
293 if (hpet->timer_states[i].timer) {
294 v3_remove_timer(hpet->core, hpet->timer_states[i].timer);
295 hpet->timer_states[i].timer = NULL;
301 /* make sure accesses don't go across register boundaries */
303 check_hpet_access (struct guest_info * core, addr_t guest_addr, uint_t length)
305 /* is access aligned to the access length? */
306 if (guest_addr & (--length) || length > 8) {
307 PrintError(core->vm_info, core, "HPET: access across register boundary\n");
314 /* get the value that we should return when main counter is read */
315 static inline uint64_t
316 read_hpet_counter (struct hpet_state * hpet)
318 if (is_hpet_enabled(hpet)) {
319 return hpet_guest_time(hpet) + hpet->counter_offset;
321 return hpet->regs.main_counter;
327 /* return the *most recent* comparator value
328 * as a side-effect, the comparator for timer N is
329 * updated to reflect any missed timer interrupts */
331 read_hpet_comparator (struct hpet_state * hpet, unsigned int timer_n)
336 comp = hpet->comparator[timer_n];
338 if (in_periodic_mode(hpet, timer_n)) {
339 // advance comp by # of periods since last update
340 uint64_t period = hpet->period[timer_n];
342 elapsed = read_hpet_counter(hpet) + period - 1 - comp;
343 comp += (elapsed / period) * period;
344 hpet->comparator[timer_n] = comp;
348 // if we're in 32-bit mode, truncate
349 if (timer_32bit(hpet, timer_n)) {
350 comp = (uint32_t)comp;
353 hpet->regs.timers[timer_n].comp.value = comp;
359 init_hpet_state (struct hpet_state * hpet)
363 hpet->base_addr = HPET_DEFAULT_BASE_ADDR;
365 memset(hpet->raw_regs, 0, HPET_REGION_SIZE);
367 hpet->regs.caps.rev_id = 1; // must be non-zero
368 hpet->regs.caps.num_tim_cap = HPET_NUM_TIMERS - 1;
369 hpet->regs.caps.count_size_cap = 1; // 64-bit mode
370 hpet->regs.caps.leg_route_cap = 1; // we support legacy interrupt routing
371 hpet->regs.caps.vendor_id = V3_VENDOR;
373 // # of femtosecs per HPET tick, HPET frequency is 1/16th of palacios time
374 hpet->regs.caps.counter_clk_period = FEMTOSECS*HPET_PERIOD/hpet->system_freq;
376 // timer-specific archictectural state
377 for (i = 0; i < HPET_NUM_TIMERS; i++) {
378 hpet->regs.timers[i].caps.tn_int_route_cap = HPET_TIMER_INT_ROUTE_CAP_SHIFT; // we support routing through the IOAPIC
379 hpet->regs.timers[i].caps.tn_size_cap = 1; // 64-bit
380 hpet->regs.timers[i].caps.tn_per_int_cap = 1; // this timer supports periodic mode
381 hpet->regs.timers[i].comp.value = ~0ULL; // initial value for comparator: default value should be all 1's
386 static inline uint64_t
387 hpet_get_reg (struct hpet_state * hpet, addr_t guest_addr)
389 /* we don't care about the lower 3 bits at this point */
392 switch (guest_addr) {
393 case GEN_CAP_REG_OFFSET:
394 return hpet->regs.caps.value;
395 case GEN_CFG_REG_OFFSET:
396 return hpet->regs.cfg.value;
398 return hpet->regs.irq_status.value;
399 case MAIN_CNTR_VAL_REG_OFFSET:
400 return read_hpet_counter(hpet);
401 case TIMER_N_OFFSET_CFG(0):
402 case TIMER_N_OFFSET_CFG(1):
403 case TIMER_N_OFFSET_CFG(2):
404 return hpet->regs.timers[TIMER_N(CFG, guest_addr)].caps.value;
405 case TIMER_N_OFFSET_CMP(0):
406 case TIMER_N_OFFSET_CMP(1):
407 case TIMER_N_OFFSET_CMP(2):
408 return read_hpet_comparator(hpet, TIMER_N(CMP, guest_addr));
409 case TIMER_N_OFFSET_FSB_IRR(0):
410 case TIMER_N_OFFSET_FSB_IRR(1):
411 case TIMER_N_OFFSET_FSB_IRR(2):
412 return hpet->regs.timers[TIMER_N(FSB_IRR, guest_addr)].fsb_int_route.value;
420 hpet_read (struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data)
422 struct hpet_state * hpet = (struct hpet_state *)priv_data;
427 /* we only care about the lower 9 bits */
428 guest_addr &= HPET_REGION_SIZE - 1;
430 if (check_hpet_access(core, guest_addr, length) != 0) {
435 flags = v3_lock_irqsave(hpet->lock);
437 reg = hpet_get_reg(hpet, guest_addr);
441 /* shift and mask out high-order bits if this is smaller than a 64-bit read */
443 res = (reg >> ((guest_addr & 7) * 8)) & ((1ULL << (length * 8)) - 1);
446 v3_unlock_irqrestore(hpet->lock, flags);
448 PrintDebug(core->vm_info, core, "HPET: core %u: at %p: Read HPET address space (%p), length=%u, val=0x%lx\n",
449 core->vcpu_id, hpet, (void *)guest_addr, length, res);
450 *(unsigned long*)dst = res;
455 /* timer functions */
458 hpet_update_time (struct guest_info * core,
459 uint64_t cpu_cycles, uint64_t cpu_freq,
462 struct hpet_timer_state * htimer = (struct hpet_timer_state *)(priv_data);
463 struct hpet_state * hpet = htimer->hpet;
464 unsigned int nr = htimer->timer_num;
465 uint64_t hpet_ticks = read_hpet_counter(hpet);
467 /* KCH TODO: handle missed timer interrupts?? */
468 if (hpet_ticks >= hpet->comparator[nr]) {
469 v3_raise_irq(core->vm_info, htimer->timer_num);
471 /* we do this to update the comparator value,
472 * e.g. in case we missed an interrupt */
473 read_hpet_comparator(hpet, nr);
478 static struct v3_timer_ops timer_ops = {
479 .update_timer = hpet_update_time,
484 hpet_stop_timer (struct hpet_state * hpet, unsigned int n)
487 V3_ASSERT(hpet->core->vm_info, hpet->core, n < HPET_NUM_TIMERS);
488 if (hpet->timer_states[n].timer) {
489 v3_remove_timer(hpet->core, hpet->timer_states[n].timer);
490 hpet->timer_states[n].timer = NULL;
492 // synch the comparator for reads that may happen while we're stopped
493 read_hpet_comparator(hpet, n);
498 hpet_start_timer (struct hpet_state * hpet, unsigned int n)
500 uint64_t timer_comp, tick;
502 V3_ASSERT(hpet->core->vm_info, hpet->core, n < HPET_NUM_TIMERS);
503 if (n == 0 && hpet->regs.cfg.leg_rt_cnf) {
504 /* KCH TODO: the PIT shouldn't be generating irqs on chan 0 if this bit is set
505 * AFAIK we don't have an interface to do this just yet
509 if (!is_timer_enabled(hpet, n)) {
513 timer_comp = read_hpet_comparator(hpet, n);
514 tick = read_hpet_counter(hpet);
516 if (timer_32bit(hpet, n)) {
517 timer_comp = (uint32_t)read_hpet_comparator(hpet, n);
518 tick = (uint32_t)read_hpet_counter(hpet);
521 /* if LegacyReplacementRoute is set,
522 * "Timer 0 will be routed to IRQ0 in Non-APIC or IRQ2 in the I/O APIC
523 * Timer 1 will be routed to IRQ8 in Non-APIC or IRQ8 in the I/O APIC
524 * Timer 2-n will be routed as per the routing in the timer n config registers"
525 * see IA-PC HPET Spec pp.12-13
527 if ((n <= 1) && (hpet->regs.cfg.leg_rt_cnf)) {
528 hpet->timer_states[n].irq = (n == 0) ? 0 : 8;
530 hpet->timer_states[n].irq = hpet->regs.timers[n].caps.tn_int_route_cnf;
533 hpet->timer_states[n].oneshot = !in_periodic_mode(hpet, n);
534 hpet->timer_states[n].timer = v3_add_timer(hpet->core, &timer_ops, &hpet->timer_states[n]);
536 if (hpet->timer_states[n].timer == NULL) {
537 PrintError(hpet->core->vm_info, hpet->core, "HPET: Failed to attach HPET timer %d to core %d\n", n, hpet->core->vcpu_id);
543 /* this ensures that only allowed bits in a given register are
545 static inline uint64_t
546 hpet_mask_write (uint64_t new, uint64_t old, uint64_t mask)
555 hpet_write (struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data)
557 struct hpet_state * hpet = (struct hpet_state *)(priv_data);
560 unsigned long to_start = 0;
561 unsigned long to_stop = 0;
564 #define mark_timer_to_start(n) (set_timer_bit(to_start, n))
565 #define mark_timer_to_stop(n) (set_timer_bit(to_stop, n))
566 #define mark_timer_to_reset(n) (mark_timer_to_stop(n), mark_timer_to_start(n))
568 guest_addr &= HPET_REGION_SIZE - 1;
570 PrintDebug(core->vm_info, core, "HPET: core %u: write to address space (%p) (val=%x)\n",
571 core->vcpu_id, (void *)guest_addr, *(uint32_t *)src);
573 if (check_hpet_access(core, guest_addr, length) != 0) {
577 flags = v3_lock_irqsave(hpet->lock);
579 old = hpet_get_reg(hpet, guest_addr);
580 new = *(unsigned long *)src;
582 /* this is a trick to convert a non-8byte write into one,
583 * making sure that we only change the bits of the word
584 * within the length of the write */
586 // say it's a 2-byte write to offset 6
587 new = hpet_mask_write(new << (guest_addr & 7) * 8, // left shift the value by 48
589 ((1ULL << (length*8)) - 1) << ((guest_addr & 7) * 8)); // then mask out all but the last 2 bytes
592 switch (guest_addr & ~7) {
594 case GEN_CFG_REG_OFFSET:
595 hpet->regs.cfg.value = hpet_mask_write(new, old, 0x3);
597 /* we're starting the timer */
598 if ( !(old & HPET_CFG_ENABLE) && (new & HPET_CFG_ENABLE) ) {
600 hpet->counter_offset = hpet->regs.main_counter - hpet_guest_time(hpet);
601 //hpet->counter_offset = hpet_guest_time(hpet) - hpet->regs.main_counter;
602 PrintDebug(core->vm_info, core, "HPET: starting the hpet, setting offset to 0x%llx\n", hpet->counter_offset);
604 for (i = 0; i < HPET_NUM_TIMERS; i++) {
605 hpet->comparator[i] = timer_32bit(hpet, i) ?
606 (uint32_t)hpet->regs.timers[i].comp.value :
607 hpet->regs.timers[i].comp.value;
608 if (is_timer_enabled(hpet, i)) {
609 mark_timer_to_start(i);
613 /* we're stopping the timer */
614 } else if ( (old & HPET_CFG_ENABLE) && !(new & HPET_CFG_ENABLE) ) {
615 PrintDebug(core->vm_info, core, "HPET: stopping the hpet\n");
617 hpet->regs.main_counter = hpet->counter_offset + hpet_guest_time(hpet);
619 for (i = 0; i < HPET_NUM_TIMERS; i++) {
620 if (is_timer_enabled(hpet, i)) {
621 mark_timer_to_stop(i);
628 case MAIN_CNTR_VAL_REG_OFFSET:
629 hpet->regs.main_counter = new;
630 PrintDebug(core->vm_info, core, "HPET: writing the main counter (0x%llx)\n", new);
631 if (is_hpet_enabled(hpet)) {
632 PrintError(core->vm_info, core, "HPET: writing main counter in unhalted state\n");
633 for (i = 0; i < HPET_NUM_TIMERS; i++) {
634 if (is_timer_enabled(hpet, i)) {
635 mark_timer_to_reset(i);
641 case TIMER_N_OFFSET_CFG(0):
642 case TIMER_N_OFFSET_CFG(1):
643 case TIMER_N_OFFSET_CFG(2):
644 /* KCH: software should set the main counter to 0 before setting a comparator */
645 timer_n = TIMER_N(CFG, guest_addr);
646 hpet->regs.timers[timer_n].caps.value = hpet_mask_write(new, old, 0x3F4E);
648 if (is_timer_ltrig(hpet, timer_n)) {
649 PrintError(core->vm_info, core, "HPET: level-triggered interrupts not supported\n");
653 /* are we switching the timer to 32-bit mode? */
654 if (new & HPET_TIMER_32BIT) {
655 hpet->regs.timers[timer_n].comp.value = (uint32_t) hpet->regs.timers[timer_n].comp.value;
656 hpet->period[timer_n] = (uint32_t)hpet->period[timer_n];
659 if (is_hpet_enabled(hpet)) {
661 if (new & HPET_TIMER_ENABLE) {
662 /* we're switching to or from periodic mode, reset */
663 if ((new ^ old) & HPET_TIMER_PERIODIC) {
664 PrintDebug(core->vm_info, core, "HPET: changing periodic mode on timer %d\n", timer_n);
665 mark_timer_to_reset(timer_n);
666 /* we're switching from 64 to 32, reset */
667 } else if ((new & HPET_TIMER_32BIT) && !(old & HPET_TIMER_32BIT)) {
668 PrintDebug(core->vm_info, core, "HPET: changing timer %d from 32-bit to 64-bit mode\n", timer_n);
669 mark_timer_to_reset(timer_n);
670 } else if (!(old & HPET_TIMER_ENABLE)) {
671 PrintDebug(core->vm_info, core, "HPET: activating timer %d\n", timer_n);
672 mark_timer_to_start(timer_n);
675 } else if (old & HPET_TIMER_ENABLE) {
676 PrintDebug(core->vm_info, core, "HPET: deactivating timer %d\n", timer_n);
677 mark_timer_to_stop(timer_n);
682 case TIMER_N_OFFSET_CMP(0):
683 case TIMER_N_OFFSET_CMP(1):
684 case TIMER_N_OFFSET_CMP(2):
685 timer_n = TIMER_N(CMP, guest_addr);
687 if (timer_32bit(hpet, timer_n)) {
691 PrintDebug(core->vm_info, core, "HPET: writing comparator reg on timer %d (val=0x%llx)\n", timer_n, new);
693 hpet->regs.timers[timer_n].comp.value = new;
695 /* this bit means sw can set the comp directly
696 * The bit gets cleared on a write to the comp
698 if (hpet->regs.timers[timer_n].caps.tn_val_set_cnf) {
699 hpet->regs.timers[timer_n].caps.tn_val_set_cnf = 0;
700 } else if (in_periodic_mode(hpet, timer_n)) {
702 /* set bounds on the period */
703 if (tick_to_ns(hpet, new) < 100000) {
704 new = (100000 << 10) / hpet->ns_per_tick;
707 new &= ((timer_32bit(hpet, timer_n)) ? ~0UL : ~0ULL) >> 1;
708 hpet->period[timer_n] = new;
709 PrintDebug(core->vm_info, core, "HPET: period for timer %d to (0x%llx)\n", timer_n, hpet->period[timer_n]);
712 hpet->comparator[timer_n] = new;
714 if (is_hpet_enabled(hpet) && is_timer_enabled(hpet, timer_n)) {
715 mark_timer_to_reset(timer_n);
719 case TIMER_N_OFFSET_FSB_IRR(0):
720 case TIMER_N_OFFSET_FSB_IRR(1):
721 case TIMER_N_OFFSET_FSB_IRR(2):
722 timer_n = TIMER_N(FSB_IRR, guest_addr);
723 PrintDebug(core->vm_info, core, "HPET: writing to fsb_irr reg for timer %d (val=0x%llx\n", timer_n, new);
724 hpet->regs.timers[timer_n].fsb_int_route.value = new;
728 /* just ignore writes to unsupported regs */
732 /* now we update the timers that we marked */
735 i = first_bit(to_stop);
736 unset_timer_bit(to_stop, i);
737 hpet_stop_timer(hpet, i);
742 i = first_bit(to_start);
743 unset_timer_bit(to_start, i);
744 hpet_start_timer(hpet, i);
747 #undef mark_timer_to_start
748 #undef mark_timer_to_stop
749 #undef mark_timer_to_reset
751 v3_unlock_irqrestore(hpet->lock, flags);
755 v3_unlock_irqrestore(hpet->lock, flags);
761 hpet_free (struct hpet_state * hpet)
763 struct v3_vm_info * vm = NULL;
764 struct guest_info * core = NULL;
773 remove_hpet_timers(hpet);
775 v3_lock_deinit(&(hpet->lock));
777 if (v3_unhook_mem(vm, core->vcpu_id, hpet->base_addr)) {
778 PrintError(vm, VCORE_NONE, "HPET: could not unhook memory region\n");
786 #ifdef V3_CONFIG_CHECKPOINT
789 hpet_save (struct v3_chkpt_ctx * ctx, void * private_data)
791 PrintError(VM_NONE, VCORE_NONE, "Unimplemented\n");
797 hpet_load (struct v3_chkpt_ctx * ctx, void * private_data)
799 PrintError(VM_NONE, VCORE_NONE, "Unimplemented\n");
805 static struct v3_device_ops dev_ops = {
806 .free = (int (*)(void *))hpet_free,
807 #ifdef V3_CONFIG_CHECKPOINT
815 hpet_init (struct v3_vm_info * vm, v3_cfg_tree_t * cfg)
817 char * dev_id = v3_cfg_val(cfg, "ID");
818 struct hpet_state * hpet;
819 struct guest_info * core = NULL;
820 struct vm_device * dev = NULL;
823 core = &vm->cores[0];
825 PrintDebug(vm, VCORE_NONE, "HPET: Creating HPET\n");
827 hpet = (struct hpet_state *)V3_Malloc(sizeof(struct hpet_state));
830 PrintError(vm, VCORE_NONE, "HPET: Failed to allocate space for HPET\n");
836 dev = v3_add_device(vm, dev_id, &dev_ops, hpet);
839 PrintError(vm, VCORE_NONE, "HPET: Could not attach device %s\n", dev_id);
843 hpet->system_freq = core->time_state.host_cpu_freq * 1000; // convert from kHz
844 hpet->ns_per_tick = ((NANOSECS * HPET_PERIOD) << 10) / hpet->system_freq;
845 hpet->max_ns_res = ~0ULL / hpet->ns_per_tick;
847 PrintDebug(core->vm_info, core, "HPET: System frequency detected as %lluHz\n", hpet->system_freq);
849 v3_lock_init(&(hpet->lock));
851 PrintDebug(core->vm_info, core, "HPET: Initializing %d timers\n", HPET_NUM_TIMERS);
853 for (i = 0; i < HPET_NUM_TIMERS; i++) {
854 hpet->timer_states[i].timer_num = i;
855 hpet->timer_states[i].hpet = hpet;
856 hpet->timer_states[i].timer = NULL;
859 /* init architecturally visible state */
860 init_hpet_state(hpet);
862 PrintDebug(core->vm_info, core "HPET: Hooking HPET mem region at %p\n", (void*)hpet->base_addr);
863 if (v3_hook_full_mem(vm,
866 hpet->base_addr + HPET_REGION_SIZE,
870 PrintError(vm, VCORE_NONE, "HPET: Failed to map HPET memory region\n");
874 PrintDebug(vm, VCORE_NONE, "HPET: Initialization complete\n");
879 v3_lock_deinit(&(hpet->lock));
880 v3_remove_device(dev);
886 device_register("HPET", hpet_init)