qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

hpet.c (24947B)


      1 /*
      2  *  High Precision Event Timer emulation
      3  *
      4  *  Copyright (c) 2007 Alexander Graf
      5  *  Copyright (c) 2008 IBM Corporation
      6  *
      7  *  Authors: Beth Kon <bkon@us.ibm.com>
      8  *
      9  * This library is free software; you can redistribute it and/or
     10  * modify it under the terms of the GNU Lesser General Public
     11  * License as published by the Free Software Foundation; either
     12  * version 2.1 of the License, or (at your option) any later version.
     13  *
     14  * This library is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     17  * Lesser General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU Lesser General Public
     20  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
     21  *
     22  * *****************************************************************
     23  *
     24  * This driver attempts to emulate an HPET device in software.
     25  */
     26 
     27 #include "qemu/osdep.h"
     28 #include "hw/i386/pc.h"
     29 #include "hw/irq.h"
     30 #include "qapi/error.h"
     31 #include "qemu/error-report.h"
     32 #include "qemu/timer.h"
     33 #include "hw/timer/hpet.h"
     34 #include "hw/sysbus.h"
     35 #include "hw/rtc/mc146818rtc.h"
     36 #include "hw/rtc/mc146818rtc_regs.h"
     37 #include "migration/vmstate.h"
     38 #include "hw/timer/i8254.h"
     39 #include "exec/address-spaces.h"
     40 #include "qom/object.h"
     41 
     42 //#define HPET_DEBUG
     43 #ifdef HPET_DEBUG
     44 #define DPRINTF printf
     45 #else
     46 #define DPRINTF(...)
     47 #endif
     48 
     49 #define HPET_MSI_SUPPORT        0
     50 
     51 OBJECT_DECLARE_SIMPLE_TYPE(HPETState, HPET)
     52 
     53 struct HPETState;
     54 typedef struct HPETTimer {  /* timers */
     55     uint8_t tn;             /*timer number*/
     56     QEMUTimer *qemu_timer;
     57     struct HPETState *state;
     58     /* Memory-mapped, software visible timer registers */
     59     uint64_t config;        /* configuration/cap */
     60     uint64_t cmp;           /* comparator */
     61     uint64_t fsb;           /* FSB route */
     62     /* Hidden register state */
     63     uint64_t period;        /* Last value written to comparator */
     64     uint8_t wrap_flag;      /* timer pop will indicate wrap for one-shot 32-bit
     65                              * mode. Next pop will be actual timer expiration.
     66                              */
     67 } HPETTimer;
     68 
     69 struct HPETState {
     70     /*< private >*/
     71     SysBusDevice parent_obj;
     72     /*< public >*/
     73 
     74     MemoryRegion iomem;
     75     uint64_t hpet_offset;
     76     bool hpet_offset_saved;
     77     qemu_irq irqs[HPET_NUM_IRQ_ROUTES];
     78     uint32_t flags;
     79     uint8_t rtc_irq_level;
     80     qemu_irq pit_enabled;
     81     uint8_t num_timers;
     82     uint32_t intcap;
     83     HPETTimer timer[HPET_MAX_TIMERS];
     84 
     85     /* Memory-mapped, software visible registers */
     86     uint64_t capability;        /* capabilities */
     87     uint64_t config;            /* configuration */
     88     uint64_t isr;               /* interrupt status reg */
     89     uint64_t hpet_counter;      /* main counter */
     90     uint8_t  hpet_id;           /* instance id */
     91 };
     92 
     93 static uint32_t hpet_in_legacy_mode(HPETState *s)
     94 {
     95     return s->config & HPET_CFG_LEGACY;
     96 }
     97 
     98 static uint32_t timer_int_route(struct HPETTimer *timer)
     99 {
    100     return (timer->config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT;
    101 }
    102 
    103 static uint32_t timer_fsb_route(HPETTimer *t)
    104 {
    105     return t->config & HPET_TN_FSB_ENABLE;
    106 }
    107 
    108 static uint32_t hpet_enabled(HPETState *s)
    109 {
    110     return s->config & HPET_CFG_ENABLE;
    111 }
    112 
    113 static uint32_t timer_is_periodic(HPETTimer *t)
    114 {
    115     return t->config & HPET_TN_PERIODIC;
    116 }
    117 
    118 static uint32_t timer_enabled(HPETTimer *t)
    119 {
    120     return t->config & HPET_TN_ENABLE;
    121 }
    122 
    123 static uint32_t hpet_time_after(uint64_t a, uint64_t b)
    124 {
    125     return ((int32_t)(b - a) < 0);
    126 }
    127 
    128 static uint32_t hpet_time_after64(uint64_t a, uint64_t b)
    129 {
    130     return ((int64_t)(b - a) < 0);
    131 }
    132 
    133 static uint64_t ticks_to_ns(uint64_t value)
    134 {
    135     return value * HPET_CLK_PERIOD;
    136 }
    137 
    138 static uint64_t ns_to_ticks(uint64_t value)
    139 {
    140     return value / HPET_CLK_PERIOD;
    141 }
    142 
    143 static uint64_t hpet_fixup_reg(uint64_t new, uint64_t old, uint64_t mask)
    144 {
    145     new &= mask;
    146     new |= old & ~mask;
    147     return new;
    148 }
    149 
    150 static int activating_bit(uint64_t old, uint64_t new, uint64_t mask)
    151 {
    152     return (!(old & mask) && (new & mask));
    153 }
    154 
    155 static int deactivating_bit(uint64_t old, uint64_t new, uint64_t mask)
    156 {
    157     return ((old & mask) && !(new & mask));
    158 }
    159 
    160 static uint64_t hpet_get_ticks(HPETState *s)
    161 {
    162     return ns_to_ticks(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->hpet_offset);
    163 }
    164 
    165 /*
    166  * calculate diff between comparator value and current ticks
    167  */
    168 static inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current)
    169 {
    170 
    171     if (t->config & HPET_TN_32BIT) {
    172         uint32_t diff, cmp;
    173 
    174         cmp = (uint32_t)t->cmp;
    175         diff = cmp - (uint32_t)current;
    176         diff = (int32_t)diff > 0 ? diff : (uint32_t)1;
    177         return (uint64_t)diff;
    178     } else {
    179         uint64_t diff, cmp;
    180 
    181         cmp = t->cmp;
    182         diff = cmp - current;
    183         diff = (int64_t)diff > 0 ? diff : (uint64_t)1;
    184         return diff;
    185     }
    186 }
    187 
    188 static void update_irq(struct HPETTimer *timer, int set)
    189 {
    190     uint64_t mask;
    191     HPETState *s;
    192     int route;
    193 
    194     if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) {
    195         /* if LegacyReplacementRoute bit is set, HPET specification requires
    196          * timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC,
    197          * timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC.
    198          */
    199         route = (timer->tn == 0) ? 0 : RTC_ISA_IRQ;
    200     } else {
    201         route = timer_int_route(timer);
    202     }
    203     s = timer->state;
    204     mask = 1 << timer->tn;
    205     if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) {
    206         s->isr &= ~mask;
    207         if (!timer_fsb_route(timer)) {
    208             qemu_irq_lower(s->irqs[route]);
    209         }
    210     } else if (timer_fsb_route(timer)) {
    211         address_space_stl_le(&address_space_memory, timer->fsb >> 32,
    212                              timer->fsb & 0xffffffff, MEMTXATTRS_UNSPECIFIED,
    213                              NULL);
    214     } else if (timer->config & HPET_TN_TYPE_LEVEL) {
    215         s->isr |= mask;
    216         qemu_irq_raise(s->irqs[route]);
    217     } else {
    218         s->isr &= ~mask;
    219         qemu_irq_pulse(s->irqs[route]);
    220     }
    221 }
    222 
    223 static int hpet_pre_save(void *opaque)
    224 {
    225     HPETState *s = opaque;
    226 
    227     /* save current counter value */
    228     if (hpet_enabled(s)) {
    229         s->hpet_counter = hpet_get_ticks(s);
    230     }
    231 
    232     return 0;
    233 }
    234 
    235 static int hpet_pre_load(void *opaque)
    236 {
    237     HPETState *s = opaque;
    238 
    239     /* version 1 only supports 3, later versions will load the actual value */
    240     s->num_timers = HPET_MIN_TIMERS;
    241     return 0;
    242 }
    243 
    244 static bool hpet_validate_num_timers(void *opaque, int version_id)
    245 {
    246     HPETState *s = opaque;
    247 
    248     if (s->num_timers < HPET_MIN_TIMERS) {
    249         return false;
    250     } else if (s->num_timers > HPET_MAX_TIMERS) {
    251         return false;
    252     }
    253     return true;
    254 }
    255 
    256 static int hpet_post_load(void *opaque, int version_id)
    257 {
    258     HPETState *s = opaque;
    259 
    260     /* Recalculate the offset between the main counter and guest time */
    261     if (!s->hpet_offset_saved) {
    262         s->hpet_offset = ticks_to_ns(s->hpet_counter)
    263                         - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    264     }
    265 
    266     /* Push number of timers into capability returned via HPET_ID */
    267     s->capability &= ~HPET_ID_NUM_TIM_MASK;
    268     s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
    269     hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
    270 
    271     /* Derive HPET_MSI_SUPPORT from the capability of the first timer. */
    272     s->flags &= ~(1 << HPET_MSI_SUPPORT);
    273     if (s->timer[0].config & HPET_TN_FSB_CAP) {
    274         s->flags |= 1 << HPET_MSI_SUPPORT;
    275     }
    276     return 0;
    277 }
    278 
    279 static bool hpet_offset_needed(void *opaque)
    280 {
    281     HPETState *s = opaque;
    282 
    283     return hpet_enabled(s) && s->hpet_offset_saved;
    284 }
    285 
    286 static bool hpet_rtc_irq_level_needed(void *opaque)
    287 {
    288     HPETState *s = opaque;
    289 
    290     return s->rtc_irq_level != 0;
    291 }
    292 
    293 static const VMStateDescription vmstate_hpet_rtc_irq_level = {
    294     .name = "hpet/rtc_irq_level",
    295     .version_id = 1,
    296     .minimum_version_id = 1,
    297     .needed = hpet_rtc_irq_level_needed,
    298     .fields = (VMStateField[]) {
    299         VMSTATE_UINT8(rtc_irq_level, HPETState),
    300         VMSTATE_END_OF_LIST()
    301     }
    302 };
    303 
    304 static const VMStateDescription vmstate_hpet_offset = {
    305     .name = "hpet/offset",
    306     .version_id = 1,
    307     .minimum_version_id = 1,
    308     .needed = hpet_offset_needed,
    309     .fields = (VMStateField[]) {
    310         VMSTATE_UINT64(hpet_offset, HPETState),
    311         VMSTATE_END_OF_LIST()
    312     }
    313 };
    314 
    315 static const VMStateDescription vmstate_hpet_timer = {
    316     .name = "hpet_timer",
    317     .version_id = 1,
    318     .minimum_version_id = 1,
    319     .fields = (VMStateField[]) {
    320         VMSTATE_UINT8(tn, HPETTimer),
    321         VMSTATE_UINT64(config, HPETTimer),
    322         VMSTATE_UINT64(cmp, HPETTimer),
    323         VMSTATE_UINT64(fsb, HPETTimer),
    324         VMSTATE_UINT64(period, HPETTimer),
    325         VMSTATE_UINT8(wrap_flag, HPETTimer),
    326         VMSTATE_TIMER_PTR(qemu_timer, HPETTimer),
    327         VMSTATE_END_OF_LIST()
    328     }
    329 };
    330 
    331 static const VMStateDescription vmstate_hpet = {
    332     .name = "hpet",
    333     .version_id = 2,
    334     .minimum_version_id = 1,
    335     .pre_save = hpet_pre_save,
    336     .pre_load = hpet_pre_load,
    337     .post_load = hpet_post_load,
    338     .fields = (VMStateField[]) {
    339         VMSTATE_UINT64(config, HPETState),
    340         VMSTATE_UINT64(isr, HPETState),
    341         VMSTATE_UINT64(hpet_counter, HPETState),
    342         VMSTATE_UINT8_V(num_timers, HPETState, 2),
    343         VMSTATE_VALIDATE("num_timers in range", hpet_validate_num_timers),
    344         VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0,
    345                                     vmstate_hpet_timer, HPETTimer),
    346         VMSTATE_END_OF_LIST()
    347     },
    348     .subsections = (const VMStateDescription*[]) {
    349         &vmstate_hpet_rtc_irq_level,
    350         &vmstate_hpet_offset,
    351         NULL
    352     }
    353 };
    354 
    355 /*
    356  * timer expiration callback
    357  */
    358 static void hpet_timer(void *opaque)
    359 {
    360     HPETTimer *t = opaque;
    361     uint64_t diff;
    362 
    363     uint64_t period = t->period;
    364     uint64_t cur_tick = hpet_get_ticks(t->state);
    365 
    366     if (timer_is_periodic(t) && period != 0) {
    367         if (t->config & HPET_TN_32BIT) {
    368             while (hpet_time_after(cur_tick, t->cmp)) {
    369                 t->cmp = (uint32_t)(t->cmp + t->period);
    370             }
    371         } else {
    372             while (hpet_time_after64(cur_tick, t->cmp)) {
    373                 t->cmp += period;
    374             }
    375         }
    376         diff = hpet_calculate_diff(t, cur_tick);
    377         timer_mod(t->qemu_timer,
    378                        qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (int64_t)ticks_to_ns(diff));
    379     } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
    380         if (t->wrap_flag) {
    381             diff = hpet_calculate_diff(t, cur_tick);
    382             timer_mod(t->qemu_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
    383                            (int64_t)ticks_to_ns(diff));
    384             t->wrap_flag = 0;
    385         }
    386     }
    387     update_irq(t, 1);
    388 }
    389 
    390 static void hpet_set_timer(HPETTimer *t)
    391 {
    392     uint64_t diff;
    393     uint32_t wrap_diff;  /* how many ticks until we wrap? */
    394     uint64_t cur_tick = hpet_get_ticks(t->state);
    395 
    396     /* whenever new timer is being set up, make sure wrap_flag is 0 */
    397     t->wrap_flag = 0;
    398     diff = hpet_calculate_diff(t, cur_tick);
    399 
    400     /* hpet spec says in one-shot 32-bit mode, generate an interrupt when
    401      * counter wraps in addition to an interrupt with comparator match.
    402      */
    403     if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
    404         wrap_diff = 0xffffffff - (uint32_t)cur_tick;
    405         if (wrap_diff < (uint32_t)diff) {
    406             diff = wrap_diff;
    407             t->wrap_flag = 1;
    408         }
    409     }
    410     timer_mod(t->qemu_timer,
    411                    qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (int64_t)ticks_to_ns(diff));
    412 }
    413 
    414 static void hpet_del_timer(HPETTimer *t)
    415 {
    416     timer_del(t->qemu_timer);
    417     update_irq(t, 0);
    418 }
    419 
    420 static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
    421                               unsigned size)
    422 {
    423     HPETState *s = opaque;
    424     uint64_t cur_tick, index;
    425 
    426     DPRINTF("qemu: Enter hpet_ram_readl at %" PRIx64 "\n", addr);
    427     index = addr;
    428     /*address range of all TN regs*/
    429     if (index >= 0x100 && index <= 0x3ff) {
    430         uint8_t timer_id = (addr - 0x100) / 0x20;
    431         HPETTimer *timer = &s->timer[timer_id];
    432 
    433         if (timer_id > s->num_timers) {
    434             DPRINTF("qemu: timer id out of range\n");
    435             return 0;
    436         }
    437 
    438         switch ((addr - 0x100) % 0x20) {
    439         case HPET_TN_CFG:
    440             return timer->config;
    441         case HPET_TN_CFG + 4: // Interrupt capabilities
    442             return timer->config >> 32;
    443         case HPET_TN_CMP: // comparator register
    444             return timer->cmp;
    445         case HPET_TN_CMP + 4:
    446             return timer->cmp >> 32;
    447         case HPET_TN_ROUTE:
    448             return timer->fsb;
    449         case HPET_TN_ROUTE + 4:
    450             return timer->fsb >> 32;
    451         default:
    452             DPRINTF("qemu: invalid hpet_ram_readl\n");
    453             break;
    454         }
    455     } else {
    456         switch (index) {
    457         case HPET_ID:
    458             return s->capability;
    459         case HPET_PERIOD:
    460             return s->capability >> 32;
    461         case HPET_CFG:
    462             return s->config;
    463         case HPET_CFG + 4:
    464             DPRINTF("qemu: invalid HPET_CFG + 4 hpet_ram_readl\n");
    465             return 0;
    466         case HPET_COUNTER:
    467             if (hpet_enabled(s)) {
    468                 cur_tick = hpet_get_ticks(s);
    469             } else {
    470                 cur_tick = s->hpet_counter;
    471             }
    472             DPRINTF("qemu: reading counter  = %" PRIx64 "\n", cur_tick);
    473             return cur_tick;
    474         case HPET_COUNTER + 4:
    475             if (hpet_enabled(s)) {
    476                 cur_tick = hpet_get_ticks(s);
    477             } else {
    478                 cur_tick = s->hpet_counter;
    479             }
    480             DPRINTF("qemu: reading counter + 4  = %" PRIx64 "\n", cur_tick);
    481             return cur_tick >> 32;
    482         case HPET_STATUS:
    483             return s->isr;
    484         default:
    485             DPRINTF("qemu: invalid hpet_ram_readl\n");
    486             break;
    487         }
    488     }
    489     return 0;
    490 }
    491 
    492 static void hpet_ram_write(void *opaque, hwaddr addr,
    493                            uint64_t value, unsigned size)
    494 {
    495     int i;
    496     HPETState *s = opaque;
    497     uint64_t old_val, new_val, val, index;
    498 
    499     DPRINTF("qemu: Enter hpet_ram_writel at %" PRIx64 " = 0x%" PRIx64 "\n",
    500             addr, value);
    501     index = addr;
    502     old_val = hpet_ram_read(opaque, addr, 4);
    503     new_val = value;
    504 
    505     /*address range of all TN regs*/
    506     if (index >= 0x100 && index <= 0x3ff) {
    507         uint8_t timer_id = (addr - 0x100) / 0x20;
    508         HPETTimer *timer = &s->timer[timer_id];
    509 
    510         DPRINTF("qemu: hpet_ram_writel timer_id = 0x%x\n", timer_id);
    511         if (timer_id > s->num_timers) {
    512             DPRINTF("qemu: timer id out of range\n");
    513             return;
    514         }
    515         switch ((addr - 0x100) % 0x20) {
    516         case HPET_TN_CFG:
    517             DPRINTF("qemu: hpet_ram_writel HPET_TN_CFG\n");
    518             if (activating_bit(old_val, new_val, HPET_TN_FSB_ENABLE)) {
    519                 update_irq(timer, 0);
    520             }
    521             val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK);
    522             timer->config = (timer->config & 0xffffffff00000000ULL) | val;
    523             if (new_val & HPET_TN_32BIT) {
    524                 timer->cmp = (uint32_t)timer->cmp;
    525                 timer->period = (uint32_t)timer->period;
    526             }
    527             if (activating_bit(old_val, new_val, HPET_TN_ENABLE) &&
    528                 hpet_enabled(s)) {
    529                 hpet_set_timer(timer);
    530             } else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) {
    531                 hpet_del_timer(timer);
    532             }
    533             break;
    534         case HPET_TN_CFG + 4: // Interrupt capabilities
    535             DPRINTF("qemu: invalid HPET_TN_CFG+4 write\n");
    536             break;
    537         case HPET_TN_CMP: // comparator register
    538             DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP\n");
    539             if (timer->config & HPET_TN_32BIT) {
    540                 new_val = (uint32_t)new_val;
    541             }
    542             if (!timer_is_periodic(timer)
    543                 || (timer->config & HPET_TN_SETVAL)) {
    544                 timer->cmp = (timer->cmp & 0xffffffff00000000ULL) | new_val;
    545             }
    546             if (timer_is_periodic(timer)) {
    547                 /*
    548                  * FIXME: Clamp period to reasonable min value?
    549                  * Clamp period to reasonable max value
    550                  */
    551                 new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1;
    552                 timer->period =
    553                     (timer->period & 0xffffffff00000000ULL) | new_val;
    554             }
    555             timer->config &= ~HPET_TN_SETVAL;
    556             if (hpet_enabled(s)) {
    557                 hpet_set_timer(timer);
    558             }
    559             break;
    560         case HPET_TN_CMP + 4: // comparator register high order
    561             DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP + 4\n");
    562             if (!timer_is_periodic(timer)
    563                 || (timer->config & HPET_TN_SETVAL)) {
    564                 timer->cmp = (timer->cmp & 0xffffffffULL) | new_val << 32;
    565             } else {
    566                 /*
    567                  * FIXME: Clamp period to reasonable min value?
    568                  * Clamp period to reasonable max value
    569                  */
    570                 new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1;
    571                 timer->period =
    572                     (timer->period & 0xffffffffULL) | new_val << 32;
    573                 }
    574                 timer->config &= ~HPET_TN_SETVAL;
    575                 if (hpet_enabled(s)) {
    576                     hpet_set_timer(timer);
    577                 }
    578                 break;
    579         case HPET_TN_ROUTE:
    580             timer->fsb = (timer->fsb & 0xffffffff00000000ULL) | new_val;
    581             break;
    582         case HPET_TN_ROUTE + 4:
    583             timer->fsb = (new_val << 32) | (timer->fsb & 0xffffffff);
    584             break;
    585         default:
    586             DPRINTF("qemu: invalid hpet_ram_writel\n");
    587             break;
    588         }
    589         return;
    590     } else {
    591         switch (index) {
    592         case HPET_ID:
    593             return;
    594         case HPET_CFG:
    595             val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK);
    596             s->config = (s->config & 0xffffffff00000000ULL) | val;
    597             if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
    598                 /* Enable main counter and interrupt generation. */
    599                 s->hpet_offset =
    600                     ticks_to_ns(s->hpet_counter) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    601                 for (i = 0; i < s->num_timers; i++) {
    602                     if ((&s->timer[i])->cmp != ~0ULL) {
    603                         hpet_set_timer(&s->timer[i]);
    604                     }
    605                 }
    606             } else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
    607                 /* Halt main counter and disable interrupt generation. */
    608                 s->hpet_counter = hpet_get_ticks(s);
    609                 for (i = 0; i < s->num_timers; i++) {
    610                     hpet_del_timer(&s->timer[i]);
    611                 }
    612             }
    613             /* i8254 and RTC output pins are disabled
    614              * when HPET is in legacy mode */
    615             if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
    616                 qemu_set_irq(s->pit_enabled, 0);
    617                 qemu_irq_lower(s->irqs[0]);
    618                 qemu_irq_lower(s->irqs[RTC_ISA_IRQ]);
    619             } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
    620                 qemu_irq_lower(s->irqs[0]);
    621                 qemu_set_irq(s->pit_enabled, 1);
    622                 qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level);
    623             }
    624             break;
    625         case HPET_CFG + 4:
    626             DPRINTF("qemu: invalid HPET_CFG+4 write\n");
    627             break;
    628         case HPET_STATUS:
    629             val = new_val & s->isr;
    630             for (i = 0; i < s->num_timers; i++) {
    631                 if (val & (1 << i)) {
    632                     update_irq(&s->timer[i], 0);
    633                 }
    634             }
    635             break;
    636         case HPET_COUNTER:
    637             if (hpet_enabled(s)) {
    638                 DPRINTF("qemu: Writing counter while HPET enabled!\n");
    639             }
    640             s->hpet_counter =
    641                 (s->hpet_counter & 0xffffffff00000000ULL) | value;
    642             DPRINTF("qemu: HPET counter written. ctr = 0x%" PRIx64 " -> "
    643                     "%" PRIx64 "\n", value, s->hpet_counter);
    644             break;
    645         case HPET_COUNTER + 4:
    646             if (hpet_enabled(s)) {
    647                 DPRINTF("qemu: Writing counter while HPET enabled!\n");
    648             }
    649             s->hpet_counter =
    650                 (s->hpet_counter & 0xffffffffULL) | (((uint64_t)value) << 32);
    651             DPRINTF("qemu: HPET counter + 4 written. ctr = 0x%" PRIx64 " -> "
    652                     "%" PRIx64 "\n", value, s->hpet_counter);
    653             break;
    654         default:
    655             DPRINTF("qemu: invalid hpet_ram_writel\n");
    656             break;
    657         }
    658     }
    659 }
    660 
    661 static const MemoryRegionOps hpet_ram_ops = {
    662     .read = hpet_ram_read,
    663     .write = hpet_ram_write,
    664     .valid = {
    665         .min_access_size = 4,
    666         .max_access_size = 4,
    667     },
    668     .endianness = DEVICE_NATIVE_ENDIAN,
    669 };
    670 
    671 static void hpet_reset(DeviceState *d)
    672 {
    673     HPETState *s = HPET(d);
    674     SysBusDevice *sbd = SYS_BUS_DEVICE(d);
    675     int i;
    676 
    677     for (i = 0; i < s->num_timers; i++) {
    678         HPETTimer *timer = &s->timer[i];
    679 
    680         hpet_del_timer(timer);
    681         timer->cmp = ~0ULL;
    682         timer->config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP;
    683         if (s->flags & (1 << HPET_MSI_SUPPORT)) {
    684             timer->config |= HPET_TN_FSB_CAP;
    685         }
    686         /* advertise availability of ioapic int */
    687         timer->config |=  (uint64_t)s->intcap << 32;
    688         timer->period = 0ULL;
    689         timer->wrap_flag = 0;
    690     }
    691 
    692     qemu_set_irq(s->pit_enabled, 1);
    693     s->hpet_counter = 0ULL;
    694     s->hpet_offset = 0ULL;
    695     s->config = 0ULL;
    696     hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
    697     hpet_cfg.hpet[s->hpet_id].address = sbd->mmio[0].addr;
    698 
    699     /* to document that the RTC lowers its output on reset as well */
    700     s->rtc_irq_level = 0;
    701 }
    702 
    703 static void hpet_handle_legacy_irq(void *opaque, int n, int level)
    704 {
    705     HPETState *s = HPET(opaque);
    706 
    707     if (n == HPET_LEGACY_PIT_INT) {
    708         if (!hpet_in_legacy_mode(s)) {
    709             qemu_set_irq(s->irqs[0], level);
    710         }
    711     } else {
    712         s->rtc_irq_level = level;
    713         if (!hpet_in_legacy_mode(s)) {
    714             qemu_set_irq(s->irqs[RTC_ISA_IRQ], level);
    715         }
    716     }
    717 }
    718 
    719 static void hpet_init(Object *obj)
    720 {
    721     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    722     HPETState *s = HPET(obj);
    723 
    724     /* HPET Area */
    725     memory_region_init_io(&s->iomem, obj, &hpet_ram_ops, s, "hpet", HPET_LEN);
    726     sysbus_init_mmio(sbd, &s->iomem);
    727 }
    728 
    729 static void hpet_realize(DeviceState *dev, Error **errp)
    730 {
    731     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
    732     HPETState *s = HPET(dev);
    733     int i;
    734     HPETTimer *timer;
    735 
    736     if (!s->intcap) {
    737         warn_report("Hpet's intcap not initialized");
    738     }
    739     if (hpet_cfg.count == UINT8_MAX) {
    740         /* first instance */
    741         hpet_cfg.count = 0;
    742     }
    743 
    744     if (hpet_cfg.count == 8) {
    745         error_setg(errp, "Only 8 instances of HPET is allowed");
    746         return;
    747     }
    748 
    749     s->hpet_id = hpet_cfg.count++;
    750 
    751     for (i = 0; i < HPET_NUM_IRQ_ROUTES; i++) {
    752         sysbus_init_irq(sbd, &s->irqs[i]);
    753     }
    754 
    755     if (s->num_timers < HPET_MIN_TIMERS) {
    756         s->num_timers = HPET_MIN_TIMERS;
    757     } else if (s->num_timers > HPET_MAX_TIMERS) {
    758         s->num_timers = HPET_MAX_TIMERS;
    759     }
    760     for (i = 0; i < HPET_MAX_TIMERS; i++) {
    761         timer = &s->timer[i];
    762         timer->qemu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hpet_timer, timer);
    763         timer->tn = i;
    764         timer->state = s;
    765     }
    766 
    767     /* 64-bit main counter; LegacyReplacementRoute. */
    768     s->capability = 0x8086a001ULL;
    769     s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
    770     s->capability |= ((uint64_t)(HPET_CLK_PERIOD * FS_PER_NS) << 32);
    771 
    772     qdev_init_gpio_in(dev, hpet_handle_legacy_irq, 2);
    773     qdev_init_gpio_out(dev, &s->pit_enabled, 1);
    774 }
    775 
    776 static Property hpet_device_properties[] = {
    777     DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS),
    778     DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false),
    779     DEFINE_PROP_UINT32(HPET_INTCAP, HPETState, intcap, 0),
    780     DEFINE_PROP_BOOL("hpet-offset-saved", HPETState, hpet_offset_saved, true),
    781     DEFINE_PROP_END_OF_LIST(),
    782 };
    783 
    784 static void hpet_device_class_init(ObjectClass *klass, void *data)
    785 {
    786     DeviceClass *dc = DEVICE_CLASS(klass);
    787 
    788     dc->realize = hpet_realize;
    789     dc->reset = hpet_reset;
    790     dc->vmsd = &vmstate_hpet;
    791     device_class_set_props(dc, hpet_device_properties);
    792 }
    793 
    794 static const TypeInfo hpet_device_info = {
    795     .name          = TYPE_HPET,
    796     .parent        = TYPE_SYS_BUS_DEVICE,
    797     .instance_size = sizeof(HPETState),
    798     .instance_init = hpet_init,
    799     .class_init    = hpet_device_class_init,
    800 };
    801 
    802 static void hpet_register_types(void)
    803 {
    804     type_register_static(&hpet_device_info);
    805 }
    806 
    807 type_init(hpet_register_types)