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