summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hw/ppc/spapr.c30
-rw-r--r--hw/ppc/spapr_rtc.c41
-rw-r--r--include/hw/ppc/spapr.h3
3 files changed, 66 insertions, 8 deletions
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 1908988dbc..6e8248d01e 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -1019,15 +1019,39 @@ static int spapr_vga_init(PCIBus *pci_bus)
}
}
+static int spapr_post_load(void *opaque, int version_id)
+{
+ sPAPREnvironment *spapr = (sPAPREnvironment *)opaque;
+ int err = 0;
+
+ /* In earlier versions, there was no seperate qdev for the PAPR
+ * RTC, so the RTC offset was stored directly in sPAPREnvironment.
+ * So when migrating from those versions, poke the incoming offset
+ * value into the RTC device */
+ if (version_id < 3) {
+ err = spapr_rtc_import_offset(spapr->rtc, spapr->rtc_offset);
+ }
+
+ return err;
+}
+
+static bool version_before_3(void *opaque, int version_id)
+{
+ return version_id < 3;
+}
+
static const VMStateDescription vmstate_spapr = {
.name = "spapr",
- .version_id = 2,
+ .version_id = 3,
.minimum_version_id = 1,
+ .post_load = spapr_post_load,
.fields = (VMStateField[]) {
- VMSTATE_UNUSED(4), /* used to be @next_irq */
+ /* used to be @next_irq */
+ VMSTATE_UNUSED_BUFFER(version_before_3, 0, 4),
/* RTC offset */
- VMSTATE_UINT64(rtc_offset, sPAPREnvironment),
+ VMSTATE_UINT64_TEST(rtc_offset, sPAPREnvironment, version_before_3),
+
VMSTATE_PPC_TIMEBASE_V(tb, sPAPREnvironment, 2),
VMSTATE_END_OF_LIST()
},
diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c
index b9f4704784..5ad0823d3b 100644
--- a/hw/ppc/spapr_rtc.c
+++ b/hw/ppc/spapr_rtc.c
@@ -37,6 +37,7 @@ typedef struct sPAPRRTCState sPAPRRTCState;
struct sPAPRRTCState {
/*< private >*/
SysBusDevice parent_obj;
+ int64_t ns_offset;
};
#define NSEC_PER_SEC 1000000000LL
@@ -45,20 +46,37 @@ void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns)
{
sPAPRRTCState *rtc = SPAPR_RTC(dev);
int64_t host_ns = qemu_clock_get_ns(rtc_clock);
+ int64_t guest_ns;
time_t guest_s;
assert(rtc);
- guest_s = host_ns / NSEC_PER_SEC + spapr->rtc_offset;
+ guest_ns = host_ns + rtc->ns_offset;
+ guest_s = guest_ns / NSEC_PER_SEC;
if (tm) {
gmtime_r(&guest_s, tm);
}
if (ns) {
- *ns = host_ns % NSEC_PER_SEC;
+ *ns = guest_ns;
}
}
+int spapr_rtc_import_offset(DeviceState *dev, int64_t legacy_offset)
+{
+ sPAPRRTCState *rtc;
+
+ if (!dev) {
+ return -ENODEV;
+ }
+
+ rtc = SPAPR_RTC(dev);
+
+ rtc->ns_offset = legacy_offset * NSEC_PER_SEC;
+
+ return 0;
+}
+
static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs,
target_ulong args,
@@ -94,6 +112,7 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr,
target_ulong args,
uint32_t nret, target_ulong rets)
{
+ sPAPRRTCState *rtc;
struct tm tm;
time_t new_s;
int64_t host_ns;
@@ -124,15 +143,18 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPREnvironment *spapr,
/* Generate a monitor event for the change */
qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort);
+ rtc = SPAPR_RTC(spapr->rtc);
+
host_ns = qemu_clock_get_ns(rtc_clock);
- spapr->rtc_offset = new_s - host_ns / NSEC_PER_SEC;
+ rtc->ns_offset = (new_s * NSEC_PER_SEC) - host_ns;
rtas_st(rets, 0, RTAS_OUT_SUCCESS);
}
static void spapr_rtc_realize(DeviceState *dev, Error **errp)
{
+ sPAPRRTCState *rtc = SPAPR_RTC(dev);
struct tm tm;
time_t host_s;
int64_t rtc_ns;
@@ -142,14 +164,25 @@ static void spapr_rtc_realize(DeviceState *dev, Error **errp)
qemu_get_timedate(&tm, 0);
host_s = mktimegm(&tm);
rtc_ns = qemu_clock_get_ns(rtc_clock);
- spapr->rtc_offset = host_s - rtc_ns / NSEC_PER_SEC;
+ rtc->ns_offset = host_s * NSEC_PER_SEC - rtc_ns;
}
+static const VMStateDescription vmstate_spapr_rtc = {
+ .name = "spapr/rtc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT64(ns_offset, sPAPRRTCState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
static void spapr_rtc_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = spapr_rtc_realize;
+ dc->vmsd = &vmstate_spapr_rtc;
spapr_rtas_register(RTAS_GET_TIME_OF_DAY, "get-time-of-day",
rtas_get_time_of_day);
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index 80c6e4f178..a8e6817cb8 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -27,7 +27,7 @@ typedef struct sPAPREnvironment {
void *rtas_blob;
void *fdt_skel;
target_ulong entry_point;
- uint64_t rtc_offset;
+ uint64_t rtc_offset; /* Now used only during incoming migration */
struct PPCTimebase tb;
bool has_graphics;
@@ -485,5 +485,6 @@ int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname,
#define TYPE_SPAPR_RTC "spapr-rtc"
void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns);
+int spapr_rtc_import_offset(DeviceState *dev, int64_t legacy_offset);
#endif /* !defined (__HW_SPAPR_H__) */