1 /////////////////////////////////////////////////////////////////////////
2 // $Id: hpet.cc 14229 2021-04-18 17:20:41Z vruppert $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 //  High Precision Event Timer emulation ported from Qemu
6 //
7 //  Copyright (c) 2007 Alexander Graf
8 //  Copyright (c) 2008 IBM Corporation
9 //
10 //  Authors: Beth Kon <bkon@us.ibm.com>
11 //
12 //  Copyright (C) 2017-2021  The Bochs Project
13 //
14 //  This library is free software; you can redistribute it and/or
15 //  modify it under the terms of the GNU Lesser General Public
16 //  License as published by the Free Software Foundation; either
17 //  version 2 of the License, or (at your option) any later version.
18 //
19 //  This library is distributed in the hope that it will be useful,
20 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22 //  Lesser General Public License for more details.
23 //
24 //  You should have received a copy of the GNU Lesser General Public
25 //  License along with this library; if not, write to the Free Software
26 //  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
27 /////////////////////////////////////////////////////////////////////////
28 
29 // Define BX_PLUGGABLE in files that can be compiled into plugins.  For
30 // platforms that require a special tag on exported symbols, BX_PLUGGABLE
31 // is used to know when we are exporting symbols and when we are importing.
32 #define BX_PLUGGABLE
33 
34 #include "iodev.h"
35 
36 #if BX_SUPPORT_PCI
37 
38 #include "hpet.h"
39 
40 #define LOG_THIS theHPET->
41 
42 bx_hpet_c *theHPET = NULL;
43 
44 // device plugin entry point
45 
PLUGIN_ENTRY_FOR_MODULE(hpet)46 PLUGIN_ENTRY_FOR_MODULE(hpet)
47 {
48   if (mode == PLUGIN_INIT) {
49     theHPET = new bx_hpet_c();
50     BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theHPET, BX_PLUGIN_HPET);
51   } else if (mode == PLUGIN_FINI) {
52     delete theHPET;
53   } else if (mode == PLUGIN_PROBE) {
54     return (int)PLUGTYPE_STANDARD;
55   }
56   return(0); // Success
57 }
58 
59 // helper functions
60 
61 // Start is assumed to be not later than end.
62 // If start == end, it describes one point in time.
63 // Returns true if value happened after start but before end.
hpet_time_between(Bit64u start,Bit64u end,Bit64u value)64 static Bit32u hpet_time_between(Bit64u start, Bit64u end, Bit64u value)
65 {
66   if (start <= end) { // No wraparound after start and before end
67     return (start <= value) && (value <= end);
68   } else { // Wraparound
69     return (start <= value) || (value <= end);
70   }
71 }
72 
73 /* Returns earliest 64-bit tick value that is after reference
74  * and has same lower 32 bits as value
75  */
hpet_cmp32_to_cmp64(Bit64u reference,Bit32u value)76 static Bit64u hpet_cmp32_to_cmp64(Bit64u reference, Bit32u value)
77 {
78   if ((Bit32u)reference <= value) {
79     return (reference & 0xFFFFFFFF00000000ull) | (Bit64u)value;
80   } else {
81     return ((reference + 0x100000000ull) & 0xFFFFFFFF00000000ull) | (Bit64u)value;
82   }
83 }
84 
ticks_to_ns(Bit64u value)85 static Bit64u ticks_to_ns(Bit64u value)
86 {
87     return value * HPET_CLK_PERIOD;
88 }
89 
ns_to_ticks(Bit64u value)90 static Bit64u ns_to_ticks(Bit64u value)
91 {
92     return value / HPET_CLK_PERIOD;
93 }
94 
hpet_fixup_reg(Bit64u _new,Bit64u old,Bit64u mask)95 static Bit64u hpet_fixup_reg(Bit64u _new, Bit64u old, Bit64u mask)
96 {
97     _new &= mask;
98     _new |= old & ~mask;
99     return _new;
100 }
101 
activating_bit(Bit64u old,Bit64u _new,Bit64u mask)102 static int activating_bit(Bit64u old, Bit64u _new, Bit64u mask)
103 {
104     return (!(old & mask) && (_new & mask));
105 }
106 
deactivating_bit(Bit64u old,Bit64u _new,Bit64u mask)107 static int deactivating_bit(Bit64u old, Bit64u _new, Bit64u mask)
108 {
109     return ((old & mask) && !(_new & mask));
110 }
111 
112 // static memory read/write functions
113 
hpet_read(bx_phy_address a20addr,unsigned len,void * data,void * param)114 static bool hpet_read(bx_phy_address a20addr, unsigned len, void *data, void *param)
115 {
116   Bit32u value1;
117   Bit64u value2;
118 
119   if (len == 4) { // must be 32-bit aligned
120     if ((a20addr & 0x3) != 0) {
121       BX_PANIC(("Unaligned HPET read at address 0x" FMT_PHY_ADDRX, a20addr));
122       return 1;
123     }
124     value1 = theHPET->read_aligned(a20addr);
125     *((Bit32u *)data) = value1;
126     return 1;
127   } else if (len == 8) { // must be 64-bit aligned
128     if ((a20addr & 0x7) != 0) {
129       BX_PANIC(("Unaligned HPET read at address 0x" FMT_PHY_ADDRX, a20addr));
130       return 1;
131     }
132     value1 = theHPET->read_aligned(a20addr);
133     value2 = theHPET->read_aligned(a20addr + 4);
134     *((Bit64u *)data) = (value1 | (value2 << 32));
135     return 1;
136   } else {
137     BX_PANIC(("Unsupported HPET read at address 0x" FMT_PHY_ADDRX, a20addr));
138   }
139   return 1;
140 }
141 
hpet_write(bx_phy_address a20addr,unsigned len,void * data,void * param)142 static bool hpet_write(bx_phy_address a20addr, unsigned len, void *data, void *param)
143 {
144   if (len == 4) { // must be 32-bit aligned
145     if ((a20addr & 0x3) != 0) {
146       BX_PANIC(("Unaligned HPET write at address 0x" FMT_PHY_ADDRX, a20addr));
147       return 1;
148     }
149     theHPET->write_aligned(a20addr, *((Bit32u*) data));
150     return 1;
151   } else if (len == 8) { // must be 64-bit aligned
152     if ((a20addr & 0x7) != 0) {
153       BX_PANIC(("Unaligned HPET write at address 0x" FMT_PHY_ADDRX, a20addr));
154       return 1;
155     }
156     Bit64u val64 = *((Bit64u*) data);
157     theHPET->write_aligned(a20addr, (Bit32u)val64);
158     theHPET->write_aligned(a20addr + 4, (Bit32u)(val64 >> 32));
159   } else {
160     BX_PANIC(("Unsupported HPET write at address 0x" FMT_PHY_ADDRX, a20addr));
161   }
162   return 1;
163 }
164 
165 // the device object
166 
bx_hpet_c()167 bx_hpet_c::bx_hpet_c()
168 {
169   put("HPET");
170   memset(&s, 0, sizeof(s));
171 }
172 
~bx_hpet_c()173 bx_hpet_c::~bx_hpet_c()
174 {
175   SIM->get_bochs_root()->remove("hpet");
176   BX_DEBUG(("Exit"));
177 }
178 
init(void)179 void bx_hpet_c::init(void)
180 {
181   BX_INFO(("initializing HPET"));
182   s.num_timers = HPET_MIN_TIMERS;
183   s.capability = BX_CONST64(0x8086a001) | ((s.num_timers - 1) << 8);
184   s.capability |= ((Bit64u)(HPET_CLK_PERIOD * FS_PER_NS) << 32);
185   s.isr = 0x00;
186   DEV_register_memory_handlers(theHPET, hpet_read, hpet_write,
187       HPET_BASE, HPET_BASE + HPET_LEN - 1);
188   for (int i = 0; i < s.num_timers; i++) {
189     s.timer[i].tn = i;
190     s.timer[i].timer_id =
191       DEV_register_timer(this, timer_handler, 1, 0, 0, "hpet");
192     bx_pc_system.setTimerParam(s.timer[i].timer_id, i);
193   }
194 #if BX_DEBUGGER
195   // register device for the 'info device' command (calls debug_dump())
196   bx_dbg_register_debug_info("hpet", this);
197 #endif
198 }
199 
reset(unsigned type)200 void bx_hpet_c::reset(unsigned type)
201 {
202   for (int i = 0; i < s.num_timers; i++) {
203     HPETTimer *timer = &s.timer[i];
204 
205     hpet_del_timer(timer);
206     timer->cmp = ~BX_CONST64(0);
207     timer->period = ~BX_CONST64(0);
208     timer->config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP | (HPET_ROUTING_CAP << 32);
209     timer->last_checked = BX_CONST64(0);
210   }
211   s.hpet_counter = BX_CONST64(0);
212   s.hpet_reference_value = BX_CONST64(0);
213   s.hpet_reference_time = BX_CONST64(0);
214   s.config = BX_CONST64(0);
215 }
216 
register_state(void)217 void bx_hpet_c::register_state(void)
218 {
219   char tnum[16];
220   bx_list_c *tim;
221 
222   bx_list_c *list = new bx_list_c(SIM->get_bochs_root(), "hpet", "HPET State");
223   BXRS_HEX_PARAM_FIELD(list, config, s.config);
224   BXRS_HEX_PARAM_FIELD(list, isr, s.isr);
225   BXRS_HEX_PARAM_FIELD(list, hpet_counter, s.hpet_counter);
226   for (int i = 0; i < s.num_timers; i++) {
227     sprintf(tnum, "timer%d", i);
228     tim = new bx_list_c(list, tnum);
229     BXRS_HEX_PARAM_FIELD(tim, config, s.timer[i].config);
230     BXRS_HEX_PARAM_FIELD(tim, cmp, s.timer[i].cmp);
231     BXRS_HEX_PARAM_FIELD(tim, fsb, s.timer[i].fsb);
232     BXRS_DEC_PARAM_FIELD(tim, period, s.timer[i].period);
233   }
234 }
235 
hpet_get_ticks(void)236 Bit64u bx_hpet_c::hpet_get_ticks(void)
237 {
238   return ns_to_ticks(bx_pc_system.time_nsec() - s.hpet_reference_time) + s.hpet_reference_value;
239 }
240 
241 /*
242  * calculate diff between comparator value and current ticks
243  */
hpet_calculate_diff(HPETTimer * t,Bit64u current)244 Bit64u bx_hpet_c::hpet_calculate_diff(HPETTimer *t, Bit64u current)
245 {
246   if (t->config & HPET_TN_32BIT) {
247     Bit32u diff, cmp;
248 
249     cmp = (Bit32u)t->cmp;
250     diff = cmp - (Bit32u)current;
251     return (Bit64u)diff;
252   } else {
253     Bit64u diff2, cmp2;
254 
255     cmp2 = t->cmp;
256     diff2 = cmp2 - current;
257     return diff2;
258   }
259 }
260 
update_irq(HPETTimer * timer,bool set)261 void bx_hpet_c::update_irq(HPETTimer *timer, bool set)
262 {
263   Bit64u mask;
264   int route;
265   BX_DEBUG(("Timer %d irq level set to %d", timer->tn, set));
266 
267   if ((timer->tn <= 1) && hpet_in_legacy_mode()) {
268     /* if LegacyReplacementRoute bit is set, HPET specification requires
269      * timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC,
270      * timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC.
271      */
272     route = (timer->tn == 0) ? 0 : RTC_ISA_IRQ;
273   } else {
274     route = timer_int_route(timer);
275   }
276   mask = (BX_CONST64(1) << timer->tn);
277   if (!set || !hpet_enabled()) {
278     DEV_pic_lower_irq(route);
279   } else {
280     if (timer->config & HPET_TN_TYPE_LEVEL) {
281       /* If HPET_TN_ENABLE bit is 0, "the timer will still operate and
282        * generate appropriate status bits, but will not cause an interrupt"
283        */
284       s.isr |= mask;
285     }
286     if (timer_enabled(timer)) {
287       if (timer_fsb_route(timer)) {
288         Bit32u val32 = (Bit32u)timer->fsb;
289         DEV_MEM_WRITE_PHYSICAL((bx_phy_address) (timer->fsb >> 32), sizeof(Bit32u), (Bit8u *) &val32);
290       } else if (timer->config & HPET_TN_TYPE_LEVEL) {
291         DEV_pic_raise_irq(route);
292       } else {
293         DEV_pic_lower_irq(route);
294         DEV_pic_raise_irq(route);
295       }
296     }
297   }
298 }
299 
timer_handler(void * this_ptr)300 void bx_hpet_c::timer_handler(void *this_ptr)
301 {
302   bx_hpet_c *class_ptr = (bx_hpet_c *) this_ptr;
303   class_ptr->hpet_timer();
304 }
305 
hpet_timer()306 void bx_hpet_c::hpet_timer()
307 {
308   HPETTimer *t = &s.timer[bx_pc_system.triggeredTimerParam()];
309   Bit64u cur_time = bx_pc_system.time_nsec();
310   Bit64u cur_tick = hpet_get_ticks();
311 
312   if (timer_is_periodic(t)) {
313     if (t->config & HPET_TN_32BIT) {
314       Bit64u cmp64 = hpet_cmp32_to_cmp64(t->last_checked, (Bit32u)t->cmp);
315       if (hpet_time_between(t->last_checked, cur_tick, cmp64)) {
316         update_irq(t, 1);
317         if ((Bit32u)t->period != 0) {
318           do {
319             cmp64 += (Bit64u)(Bit32u)t->period;
320           } while (hpet_time_between(t->last_checked, cur_tick, cmp64));
321           t->cmp = (Bit32u)cmp64;
322         }
323       }
324     } else { // 64-bit timer
325       if (hpet_time_between(t->last_checked, cur_tick, t->cmp)) {
326         update_irq(t, 1);
327         if (t->period != 0) {
328           do {
329             t->cmp += t->period;
330           } while (hpet_time_between(t->last_checked, cur_tick, t->cmp));
331         }
332       }
333     }
334   } else { // One-shot timer
335     if (t->config & HPET_TN_32BIT) {
336       Bit64u cmp64 = hpet_cmp32_to_cmp64(t->last_checked, (Bit32u)t->cmp);
337       Bit64u wrap = hpet_cmp32_to_cmp64(t->last_checked, 0);
338       if (hpet_time_between(t->last_checked, cur_tick, cmp64) || hpet_time_between(t->last_checked, cur_tick, wrap)) {
339         update_irq(t, 1);
340       }
341     } else { // 64-bit timer
342       if (hpet_time_between(t->last_checked, cur_tick, t->cmp)) {
343         update_irq(t, 1);
344       }
345     }
346   }
347   hpet_set_timer(t);
348   t->last_checked = cur_tick;
349 
350   Bit64u ticks_passed = ns_to_ticks(cur_time - s.hpet_reference_time);
351   if (ticks_passed != 0) {
352     s.hpet_reference_time += ticks_to_ns(ticks_passed);
353     s.hpet_reference_value += ticks_passed;
354   }
355 }
356 
hpet_set_timer(HPETTimer * t)357 void bx_hpet_c::hpet_set_timer(HPETTimer *t)
358 {
359   Bit64u cur_tick = hpet_get_ticks();
360   Bit64u diff = hpet_calculate_diff(t, cur_tick);
361   if (diff == 0) {
362     if (t->config & HPET_TN_32BIT) {
363       diff = 0x100000000ull;
364     } else {
365       diff = HPET_MAX_ALLOWED_PERIOD;
366     }
367   }
368   /* hpet spec says in one-shot 32-bit mode, generate an interrupt when
369    * counter wraps in addition to an interrupt with comparator match.
370    */
371   if (!timer_is_periodic(t)) {
372     if (t->config & HPET_TN_32BIT) {
373       Bit64u wrap_diff = 0x100000000ull - (Bit64u)(Bit32u)cur_tick;
374       if (wrap_diff < diff) diff = wrap_diff;
375     }
376   }
377   if (diff < HPET_MIN_ALLOWED_PERIOD) diff = HPET_MIN_ALLOWED_PERIOD;
378   if (diff > HPET_MAX_ALLOWED_PERIOD) diff = HPET_MAX_ALLOWED_PERIOD;
379   BX_DEBUG(("Timer %d to fire in 0x%lX ticks", t->tn, diff));
380   bx_pc_system.activate_timer_nsec(t->timer_id, ticks_to_ns(diff), 0);
381 }
382 
hpet_del_timer(HPETTimer * t)383 void bx_hpet_c::hpet_del_timer(HPETTimer *t)
384 {
385   BX_DEBUG(("Timer %d deactivated", t->tn));
386   bx_pc_system.deactivate_timer(t->timer_id);
387   update_irq(t, 0);
388 }
389 
read_aligned(bx_phy_address address)390 Bit32u bx_hpet_c::read_aligned(bx_phy_address address)
391 {
392   Bit32u value = 0;
393 
394   // BX_DEBUG(("read aligned addr=0x" FMT_PHY_ADDRX, address));
395   Bit16u index = (Bit16u)(address & 0x3ff);
396   if (index < 0x100) {
397     switch (index) {
398       case HPET_ID:
399         value = (Bit32u)s.capability;
400         break;
401       case HPET_PERIOD:
402         value = (Bit32u)(s.capability >> 32);
403         break;
404       case HPET_CFG:
405         value = (Bit32u)s.config;
406         break;
407       case HPET_CFG + 4:
408         value = (Bit32u)(s.config >> 32);
409         break;
410       case HPET_STATUS:
411         value = (Bit32u)s.isr;
412         break;
413       case HPET_STATUS + 4:
414         value = (Bit32u)(s.isr >> 32);
415         break;
416       case HPET_COUNTER:
417         if (hpet_enabled()) {
418           value = (Bit32u)hpet_get_ticks();
419         } else {
420           value = (Bit32u)s.hpet_counter;
421         }
422         break;
423       case HPET_COUNTER + 4:
424         if (hpet_enabled()) {
425           value = (Bit32u)(hpet_get_ticks() >> 32);
426         } else {
427           value = (Bit32u)(s.hpet_counter >> 32);
428         }
429         break;
430       default:
431         BX_ERROR(("read from reserved offset 0x%04x", index));
432     }
433   } else {
434     Bit8u id = (index - 0x100) / 0x20;
435     if (id >= s.num_timers) {
436       BX_ERROR(("read: timer id out of range"));
437       return 0;
438     }
439     HPETTimer *timer = &s.timer[id];
440     switch (index & 0x1f) {
441       case HPET_TN_CFG:
442         value = (Bit32u)timer->config;
443         break;
444       case HPET_TN_CFG + 4:
445         value = (Bit32u)(timer->config >> 32);
446         break;
447       case HPET_TN_CMP:
448         value = (Bit32u)timer->cmp;
449         break;
450       case HPET_TN_CMP + 4:
451         value = (Bit32u)(timer->cmp >> 32);
452         break;
453       case HPET_TN_ROUTE:
454         value = (Bit32u)timer->fsb;
455         break;
456       case HPET_TN_ROUTE + 4:
457         value = (Bit32u)(timer->fsb >> 32);
458         break;
459       default:
460         BX_ERROR(("read from reserved offset 0x%04x", index));
461     }
462   }
463   return value;
464 }
465 
write_aligned(bx_phy_address address,Bit32u value)466 void bx_hpet_c::write_aligned(bx_phy_address address, Bit32u value)
467 {
468   int i;
469   Bit16u index = (Bit16u)(address & 0x3ff);
470   Bit64u val;
471   Bit64u new_val = value;
472   Bit64u old_val = read_aligned(address);
473 
474   BX_DEBUG(("write aligned addr=0x" FMT_PHY_ADDRX ", data=0x%08x", address, value));
475   if (index < 0x100) {
476     switch (index) {
477       case HPET_ID:
478         break;
479       case HPET_ID + 4:
480         break;
481       case HPET_CFG:
482         val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK);
483         s.config = (s.config & BX_CONST64(0xffffffff00000000)) | val;
484         if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
485           /* Enable main counter and interrupt generation. */
486           s.hpet_reference_value = s.hpet_counter;
487           s.hpet_reference_time = bx_pc_system.time_nsec();
488           for (i = 0; i < s.num_timers; i++) {
489             if (timer_enabled(&s.timer[i]) && (s.isr & (BX_CONST64(1) << i))) {
490               update_irq(&s.timer[i], 1);
491             }
492             hpet_set_timer(&s.timer[i]);
493           }
494         } else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
495           /* Halt main counter and disable interrupt generation. */
496           s.hpet_counter = hpet_get_ticks();
497           for (i = 0; i < s.num_timers; i++) {
498             hpet_del_timer(&s.timer[i]);
499           }
500         }
501         /* i8254 and RTC output pins are disabled
502          * when HPET is in legacy mode */
503         if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
504           BX_INFO(("Entering legacy mode"));
505           DEV_pit_enable_irq(0);
506           DEV_cmos_enable_irq(0);
507         } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
508           BX_INFO(("Leaving legacy mode"));
509           DEV_pit_enable_irq(1);
510           DEV_cmos_enable_irq(1);
511         }
512         break;
513       case HPET_CFG + 4:
514         break;
515       case HPET_STATUS:
516         val = new_val & s.isr;
517         for (i = 0; i < s.num_timers; i++) {
518           if (val & (BX_CONST64(1) << i)) {
519             update_irq(&s.timer[i], 0);
520             s.isr &= ~(BX_CONST64(1) << i);
521           }
522         }
523         break;
524       case HPET_STATUS + 4:
525         break;
526       case HPET_COUNTER:
527         if (hpet_enabled()) {
528           BX_ERROR(("Writing counter while HPET enabled!"));
529         } else {
530           s.hpet_counter = (s.hpet_counter & BX_CONST64(0xffffffff00000000)) | value;
531           for (i = 0; i < s.num_timers; i++) {
532             s.timer[i].last_checked = s.hpet_counter;
533           }
534         }
535         break;
536       case HPET_COUNTER + 4:
537         if (hpet_enabled()) {
538           BX_ERROR(("Writing counter while HPET enabled!"));
539         } else {
540           s.hpet_counter = (s.hpet_counter & BX_CONST64(0xffffffff)) | (((Bit64u)value) << 32);
541           for (i = 0; i < s.num_timers; i++) {
542             s.timer[i].last_checked = s.hpet_counter;
543           }
544         }
545         break;
546       default:
547         BX_ERROR(("write to reserved offset 0x%04x", index));
548     }
549   } else {
550     Bit8u id = (index - 0x100) / 0x20;
551     if (id >= s.num_timers) {
552       BX_ERROR(("write: timer id out of range"));
553       return;
554     }
555     HPETTimer *timer = &s.timer[id];
556     switch (index & 0x1f) {
557       case HPET_TN_CFG:
558         val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK);
559         timer->config = (timer->config & BX_CONST64(0xffffffff00000000)) | val;
560         if (timer->config & HPET_TN_32BIT) {
561           timer->cmp = (Bit32u)timer->cmp;
562           timer->period = (Bit32u)timer->period;
563         }
564         if (timer_fsb_route(timer) || !(timer->config & HPET_TN_TYPE_LEVEL)) {
565           s.isr &= ~(BX_CONST64(1) << id);
566         }
567         if (timer_enabled(timer) && hpet_enabled()) {
568           if (s.isr & (BX_CONST64(1) << id)) {
569             update_irq(timer, 1);
570           } else {
571             update_irq(timer, 0);
572           }
573         }
574         if (hpet_enabled()) {
575           hpet_set_timer(timer);
576         }
577         break;
578       case HPET_TN_CFG + 4:
579         break;
580       case HPET_TN_CMP:
581         if (!timer_is_periodic(timer) || (timer->config & HPET_TN_SETVAL)) {
582           timer->cmp = (timer->cmp & BX_CONST64(0xffffffff00000000)) | new_val;
583         }
584         timer->period = (timer->period & BX_CONST64(0xffffffff00000000)) | new_val;
585         timer->config &= ~HPET_TN_SETVAL;
586         if (hpet_enabled()) {
587           hpet_set_timer(timer);
588         }
589         break;
590       case HPET_TN_CMP + 4:
591         if (timer->config & HPET_TN_32BIT) break;
592         if (!timer_is_periodic(timer) || (timer->config & HPET_TN_SETVAL)) {
593           timer->cmp = (timer->cmp & BX_CONST64(0xffffffff)) | (new_val << 32);
594         }
595         timer->period = (timer->period & BX_CONST64(0xffffffff)) | (new_val << 32);
596         timer->config &= ~HPET_TN_SETVAL;
597         if (hpet_enabled()) {
598           hpet_set_timer(timer);
599         }
600         break;
601       case HPET_TN_ROUTE:
602         timer->fsb = (timer->fsb & BX_CONST64(0xffffffff00000000)) | new_val;
603         break;
604       case HPET_TN_ROUTE + 4:
605         timer->fsb = (new_val << 32) | (timer->fsb & 0xffffffff);
606         break;
607       default:
608         BX_ERROR(("write to reserved offset 0x%04x", index));
609     }
610   }
611 }
612 
613 #if BX_DEBUGGER
debug_dump(int argc,char ** argv)614 void bx_hpet_c::debug_dump(int argc, char **argv)
615 {
616   Bit64u value;
617 
618   dbg_printf("HPET\n\n");
619   dbg_printf("enable config    = %d\n", s.config & 1);
620   dbg_printf("legacy config    = %d\n", (s.config >> 1) & 1);
621   dbg_printf("interrupt status = 0x%08x\n", (Bit32u)s.isr);
622   if (hpet_enabled()) {
623     value = hpet_get_ticks();
624   } else {
625     value = s.hpet_counter;
626   }
627   dbg_printf("main counter = 0x" FMT_LL "x\n\n", value);
628   for (int i = 0; i < s.num_timers; i++) {
629     HPETTimer *timer = &s.timer[i];
630     dbg_printf("timer #%d (%d-bit)\n", i, ((timer->config & HPET_TN_32BIT) > 0) ? 32:64);
631     dbg_printf("interrupt enable = %d\n", timer_enabled(timer) > 0);
632     dbg_printf("periodic mode    = %d\n", timer_is_periodic(timer) > 0);
633     dbg_printf("level sensitive  = %d\n", (timer->config & HPET_TN_TYPE_LEVEL) > 0);
634     if (timer->config & HPET_TN_32BIT) {
635       dbg_printf("comparator value = 0x%08x\n", (Bit32u)timer->cmp);
636       dbg_printf("period           = 0x%08x\n", (Bit32u)timer->period);
637     } else {
638       dbg_printf("comparator value = 0x" FMT_LL "x\n", timer->cmp);
639       dbg_printf("period           = 0x" FMT_LL "x\n", timer->period);
640     }
641   }
642   if (argc > 0) {
643     dbg_printf("\nAdditional options not supported\n");
644   }
645 }
646 #endif
647 
648 #endif /* if BX_SUPPORT_PCI */
649