cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

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
     51OBJECT_DECLARE_SIMPLE_TYPE(HPETState, HPET)
     52
     53struct HPETState;
     54typedef 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
     69struct 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
     93static uint32_t hpet_in_legacy_mode(HPETState *s)
     94{
     95    return s->config & HPET_CFG_LEGACY;
     96}
     97
     98static 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
    103static uint32_t timer_fsb_route(HPETTimer *t)
    104{
    105    return t->config & HPET_TN_FSB_ENABLE;
    106}
    107
    108static uint32_t hpet_enabled(HPETState *s)
    109{
    110    return s->config & HPET_CFG_ENABLE;
    111}
    112
    113static uint32_t timer_is_periodic(HPETTimer *t)
    114{
    115    return t->config & HPET_TN_PERIODIC;
    116}
    117
    118static uint32_t timer_enabled(HPETTimer *t)
    119{
    120    return t->config & HPET_TN_ENABLE;
    121}
    122
    123static uint32_t hpet_time_after(uint64_t a, uint64_t b)
    124{
    125    return ((int32_t)(b - a) < 0);
    126}
    127
    128static uint32_t hpet_time_after64(uint64_t a, uint64_t b)
    129{
    130    return ((int64_t)(b - a) < 0);
    131}
    132
    133static uint64_t ticks_to_ns(uint64_t value)
    134{
    135    return value * HPET_CLK_PERIOD;
    136}
    137
    138static uint64_t ns_to_ticks(uint64_t value)
    139{
    140    return value / HPET_CLK_PERIOD;
    141}
    142
    143static 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
    150static int activating_bit(uint64_t old, uint64_t new, uint64_t mask)
    151{
    152    return (!(old & mask) && (new & mask));
    153}
    154
    155static int deactivating_bit(uint64_t old, uint64_t new, uint64_t mask)
    156{
    157    return ((old & mask) && !(new & mask));
    158}
    159
    160static 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 */
    168static 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
    188static 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
    223static 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
    235static 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
    244static 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
    256static 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
    279static bool hpet_offset_needed(void *opaque)
    280{
    281    HPETState *s = opaque;
    282
    283    return hpet_enabled(s) && s->hpet_offset_saved;
    284}
    285
    286static bool hpet_rtc_irq_level_needed(void *opaque)
    287{
    288    HPETState *s = opaque;
    289
    290    return s->rtc_irq_level != 0;
    291}
    292
    293static 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
    304static 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
    315static 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
    331static 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 */
    358static 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
    390static 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
    414static void hpet_del_timer(HPETTimer *t)
    415{
    416    timer_del(t->qemu_timer);
    417    update_irq(t, 0);
    418}
    419
    420static 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
    492static 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
    661static 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
    671static 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
    703static 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
    719static 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
    729static 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
    776static 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
    784static 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
    794static 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
    802static void hpet_register_types(void)
    803{
    804    type_register_static(&hpet_device_info);
    805}
    806
    807type_init(hpet_register_types)