apic: Open-code timer save/restore
To enable migration between accelerated and non-accelerated APIC models, we will need to handle the timer saving and restoring specially and can no longer rely on the automatics of VMSTATE_TIMER. Specifically, accelerated model will not start any QEMUTimer. This patch therefore factors out the generic bits into apic_next_timer and use a post-load callback to implemented model-specific logic. Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
This commit is contained in:
parent
dae0168528
commit
7a380ca350
30
hw/apic.c
30
hw/apic.c
@ -521,25 +521,9 @@ static uint32_t apic_get_current_count(APICCommonState *s)
|
||||
|
||||
static void apic_timer_update(APICCommonState *s, int64_t current_time)
|
||||
{
|
||||
int64_t next_time, d;
|
||||
|
||||
if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) {
|
||||
d = (current_time - s->initial_count_load_time) >>
|
||||
s->count_shift;
|
||||
if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
|
||||
if (!s->initial_count)
|
||||
goto no_timer;
|
||||
d = ((d / ((uint64_t)s->initial_count + 1)) + 1) * ((uint64_t)s->initial_count + 1);
|
||||
} else {
|
||||
if (d >= s->initial_count)
|
||||
goto no_timer;
|
||||
d = (uint64_t)s->initial_count + 1;
|
||||
}
|
||||
next_time = s->initial_count_load_time + (d << s->count_shift);
|
||||
qemu_mod_timer(s->timer, next_time);
|
||||
s->next_time = next_time;
|
||||
if (apic_next_timer(s, current_time)) {
|
||||
qemu_mod_timer(s->timer, s->next_time);
|
||||
} else {
|
||||
no_timer:
|
||||
qemu_del_timer(s->timer);
|
||||
}
|
||||
}
|
||||
@ -753,6 +737,15 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
}
|
||||
}
|
||||
|
||||
static void apic_post_load(APICCommonState *s)
|
||||
{
|
||||
if (s->timer_expiry != -1) {
|
||||
qemu_mod_timer(s->timer, s->timer_expiry);
|
||||
} else {
|
||||
qemu_del_timer(s->timer);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps apic_io_ops = {
|
||||
.old_mmio = {
|
||||
.read = { apic_mem_readb, apic_mem_readw, apic_mem_readl, },
|
||||
@ -776,6 +769,7 @@ static APICCommonInfo apic_info = {
|
||||
.set_base = apic_set_base,
|
||||
.set_tpr = apic_set_tpr,
|
||||
.external_nmi = apic_external_nmi,
|
||||
.post_load = apic_post_load,
|
||||
};
|
||||
|
||||
static void apic_register_devices(void)
|
||||
|
@ -93,6 +93,39 @@ void apic_deliver_nmi(DeviceState *d)
|
||||
info->external_nmi(s);
|
||||
}
|
||||
|
||||
bool apic_next_timer(APICCommonState *s, int64_t current_time)
|
||||
{
|
||||
int64_t d;
|
||||
|
||||
/* We need to store the timer state separately to support APIC
|
||||
* implementations that maintain a non-QEMU timer, e.g. inside the
|
||||
* host kernel. This open-coded state allows us to migrate between
|
||||
* both models. */
|
||||
s->timer_expiry = -1;
|
||||
|
||||
if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d = (current_time - s->initial_count_load_time) >> s->count_shift;
|
||||
|
||||
if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
|
||||
if (!s->initial_count) {
|
||||
return false;
|
||||
}
|
||||
d = ((d / ((uint64_t)s->initial_count + 1)) + 1) *
|
||||
((uint64_t)s->initial_count + 1);
|
||||
} else {
|
||||
if (d >= s->initial_count) {
|
||||
return false;
|
||||
}
|
||||
d = (uint64_t)s->initial_count + 1;
|
||||
}
|
||||
s->next_time = s->initial_count_load_time + (d << s->count_shift);
|
||||
s->timer_expiry = s->next_time;
|
||||
return true;
|
||||
}
|
||||
|
||||
void apic_init_reset(DeviceState *d)
|
||||
{
|
||||
APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d);
|
||||
@ -120,7 +153,10 @@ void apic_init_reset(DeviceState *d)
|
||||
s->next_time = 0;
|
||||
s->wait_for_sipi = 1;
|
||||
|
||||
qemu_del_timer(s->timer);
|
||||
if (s->timer) {
|
||||
qemu_del_timer(s->timer);
|
||||
}
|
||||
s->timer_expiry = -1;
|
||||
}
|
||||
|
||||
static void apic_reset_common(DeviceState *d)
|
||||
@ -203,12 +239,25 @@ static int apic_init_common(SysBusDevice *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apic_dispatch_post_load(void *opaque, int version_id)
|
||||
{
|
||||
APICCommonState *s = opaque;
|
||||
APICCommonInfo *info =
|
||||
DO_UPCAST(APICCommonInfo, busdev.qdev, s->busdev.qdev.info);
|
||||
|
||||
if (info->post_load) {
|
||||
info->post_load(s);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_apic_common = {
|
||||
.name = "apic",
|
||||
.version_id = 3,
|
||||
.minimum_version_id = 3,
|
||||
.minimum_version_id_old = 1,
|
||||
.load_state_old = apic_load_old,
|
||||
.post_load = apic_dispatch_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(apicbase, APICCommonState),
|
||||
VMSTATE_UINT8(id, APICCommonState),
|
||||
@ -228,7 +277,8 @@ static const VMStateDescription vmstate_apic_common = {
|
||||
VMSTATE_UINT32(initial_count, APICCommonState),
|
||||
VMSTATE_INT64(initial_count_load_time, APICCommonState),
|
||||
VMSTATE_INT64(next_time, APICCommonState),
|
||||
VMSTATE_TIMER(timer, APICCommonState),
|
||||
VMSTATE_INT64(timer_expiry,
|
||||
APICCommonState), /* open-coded timer state */
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
@ -92,6 +92,7 @@ struct APICCommonState {
|
||||
int64_t next_time;
|
||||
int idx;
|
||||
QEMUTimer *timer;
|
||||
int64_t timer_expiry;
|
||||
int sipi_vector;
|
||||
int wait_for_sipi;
|
||||
};
|
||||
@ -104,9 +105,11 @@ struct APICCommonInfo {
|
||||
void (*set_base)(APICCommonState *s, uint64_t val);
|
||||
void (*set_tpr)(APICCommonState *s, uint8_t val);
|
||||
void (*external_nmi)(APICCommonState *s);
|
||||
void (*post_load)(APICCommonState *s);
|
||||
};
|
||||
|
||||
void apic_report_irq_delivered(int delivered);
|
||||
void apic_qdev_register(APICCommonInfo *info);
|
||||
bool apic_next_timer(APICCommonState *s, int64_t current_time);
|
||||
|
||||
#endif /* !QEMU_APIC_INTERNAL_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user