summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/hpet.c53
-rw-r--r--hw/hpet_emul.h6
2 files changed, 45 insertions, 14 deletions
diff --git a/hw/hpet.c b/hw/hpet.c
index f24b227e7e..6187e5e980 100644
--- a/hw/hpet.c
+++ b/hw/hpet.c
@@ -60,7 +60,8 @@ typedef struct HPETState {
uint64_t hpet_offset;
qemu_irq irqs[HPET_NUM_IRQ_ROUTES];
uint8_t rtc_irq_level;
- HPETTimer timer[HPET_NUM_TIMERS];
+ uint8_t num_timers;
+ HPETTimer timer[HPET_MAX_TIMERS];
/* Memory-mapped, software visible registers */
uint64_t capability; /* capabilities */
@@ -196,12 +197,25 @@ static void hpet_pre_save(void *opaque)
s->hpet_counter = hpet_get_ticks(s);
}
+static int hpet_pre_load(void *opaque)
+{
+ HPETState *s = opaque;
+
+ /* version 1 only supports 3, later versions will load the actual value */
+ s->num_timers = HPET_MIN_TIMERS;
+ return 0;
+}
+
static int hpet_post_load(void *opaque, int version_id)
{
HPETState *s = opaque;
/* Recalculate the offset between the main counter and guest time */
s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_get_clock(vm_clock);
+
+ /* Push number of timers into capability returned via HPET_ID */
+ s->capability &= ~HPET_ID_NUM_TIM_MASK;
+ s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
return 0;
}
@@ -224,17 +238,19 @@ static const VMStateDescription vmstate_hpet_timer = {
static const VMStateDescription vmstate_hpet = {
.name = "hpet",
- .version_id = 1,
+ .version_id = 2,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.pre_save = hpet_pre_save,
+ .pre_load = hpet_pre_load,
.post_load = hpet_post_load,
.fields = (VMStateField []) {
VMSTATE_UINT64(config, HPETState),
VMSTATE_UINT64(isr, HPETState),
VMSTATE_UINT64(hpet_counter, HPETState),
- VMSTATE_STRUCT_ARRAY(timer, HPETState, HPET_NUM_TIMERS, 0,
- vmstate_hpet_timer, HPETTimer),
+ VMSTATE_UINT8_V(num_timers, HPETState, 2),
+ VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0,
+ vmstate_hpet_timer, HPETTimer),
VMSTATE_END_OF_LIST()
}
};
@@ -330,7 +346,7 @@ static uint32_t hpet_ram_readl(void *opaque, target_phys_addr_t addr)
uint8_t timer_id = (addr - 0x100) / 0x20;
HPETTimer *timer = &s->timer[timer_id];
- if (timer_id > HPET_NUM_TIMERS - 1) {
+ if (timer_id > s->num_timers) {
DPRINTF("qemu: timer id out of range\n");
return 0;
}
@@ -421,7 +437,7 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr,
HPETTimer *timer = &s->timer[timer_id];
DPRINTF("qemu: hpet_ram_writel timer_id = %#x \n", timer_id);
- if (timer_id > HPET_NUM_TIMERS - 1) {
+ if (timer_id > s->num_timers) {
DPRINTF("qemu: timer id out of range\n");
return;
}
@@ -504,7 +520,7 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr,
/* Enable main counter and interrupt generation. */
s->hpet_offset =
ticks_to_ns(s->hpet_counter) - qemu_get_clock(vm_clock);
- for (i = 0; i < HPET_NUM_TIMERS; i++) {
+ for (i = 0; i < s->num_timers; i++) {
if ((&s->timer[i])->cmp != ~0ULL) {
hpet_set_timer(&s->timer[i]);
}
@@ -512,7 +528,7 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr,
} else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
/* Halt main counter and disable interrupt generation. */
s->hpet_counter = hpet_get_ticks(s);
- for (i = 0; i < HPET_NUM_TIMERS; i++) {
+ for (i = 0; i < s->num_timers; i++) {
hpet_del_timer(&s->timer[i]);
}
}
@@ -530,7 +546,7 @@ static void hpet_ram_writel(void *opaque, target_phys_addr_t addr,
break;
case HPET_STATUS:
val = new_val & s->isr;
- for (i = 0; i < HPET_NUM_TIMERS; i++) {
+ for (i = 0; i < s->num_timers; i++) {
if (val & (1 << i)) {
update_irq(&s->timer[i], 0);
}
@@ -589,7 +605,7 @@ static void hpet_reset(DeviceState *d)
int i;
static int count = 0;
- for (i = 0; i < HPET_NUM_TIMERS; i++) {
+ for (i = 0; i < s->num_timers; i++) {
HPETTimer *timer = &s->timer[i];
hpet_del_timer(timer);
@@ -603,8 +619,9 @@ static void hpet_reset(DeviceState *d)
s->hpet_counter = 0ULL;
s->hpet_offset = 0ULL;
- /* 64-bit main counter; 3 timers supported; LegacyReplacementRoute. */
- s->capability = 0x8086a201ULL;
+ /* 64-bit main counter; LegacyReplacementRoute. */
+ s->capability = 0x8086a001ULL;
+ s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
s->capability |= ((HPET_CLK_PERIOD) << 32);
s->config = 0ULL;
if (count > 0) {
@@ -637,7 +654,13 @@ static int hpet_init(SysBusDevice *dev)
for (i = 0; i < HPET_NUM_IRQ_ROUTES; i++) {
sysbus_init_irq(dev, &s->irqs[i]);
}
- for (i = 0; i < HPET_NUM_TIMERS; i++) {
+
+ if (s->num_timers < HPET_MIN_TIMERS) {
+ s->num_timers = HPET_MIN_TIMERS;
+ } else if (s->num_timers > HPET_MAX_TIMERS) {
+ s->num_timers = HPET_MAX_TIMERS;
+ }
+ for (i = 0; i < HPET_MAX_TIMERS; i++) {
timer = &s->timer[i];
timer->qemu_timer = qemu_new_timer(vm_clock, hpet_timer, timer);
timer->tn = i;
@@ -661,6 +684,10 @@ static SysBusDeviceInfo hpet_device_info = {
.qdev.vmsd = &vmstate_hpet,
.qdev.reset = hpet_reset,
.init = hpet_init,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS),
+ DEFINE_PROP_END_OF_LIST(),
+ },
};
static void hpet_register_device(void)
diff --git a/hw/hpet_emul.h b/hw/hpet_emul.h
index 9c268ccae4..e8b794c7fa 100644
--- a/hw/hpet_emul.h
+++ b/hw/hpet_emul.h
@@ -17,7 +17,8 @@
#define HPET_CLK_PERIOD 10000000ULL /* 10000000 femtoseconds == 10ns*/
#define FS_PER_NS 1000000
-#define HPET_NUM_TIMERS 3
+#define HPET_MIN_TIMERS 3
+#define HPET_MAX_TIMERS 32
#define HPET_NUM_IRQ_ROUTES 32
@@ -34,6 +35,9 @@
#define HPET_TN_ROUTE 0x010
#define HPET_CFG_WRITE_MASK 0x3
+#define HPET_ID_NUM_TIM_SHIFT 8
+#define HPET_ID_NUM_TIM_MASK 0x1f00
+
#define HPET_TN_TYPE_LEVEL 0x002
#define HPET_TN_ENABLE 0x004
#define HPET_TN_PERIODIC 0x008