Skip to content

Commit 8380b3a

Browse files
jrtc27alistair23
authored andcommitted
goldfish_rtc: Fix non-atomic read behaviour of TIME_LOW/TIME_HIGH
The specification says: 0x00 TIME_LOW R: Get current time, then return low-order 32-bits. 0x04 TIME_HIGH R: Return high 32-bits from previous TIME_LOW read. ... To read the value, the kernel must perform an IO_READ(TIME_LOW), which returns an unsigned 32-bit value, before an IO_READ(TIME_HIGH), which returns a signed 32-bit value, corresponding to the higher half of the full value. However, we were just returning the current time for both. If the guest is unlucky enough to read TIME_LOW and TIME_HIGH either side of an overflow of the lower half, it will see time be in the future, before jumping backwards on the next read, and Linux currently relies on the atomicity guaranteed by the spec so is affected by this. Fix this violation of the spec by caching the correct value for TIME_HIGH whenever TIME_LOW is read, and returning that value for any TIME_HIGH read. Signed-off-by: Jessica Clarke <[email protected]> Reviewed-by: Peter Maydell <[email protected]> Reviewed-by: Richard Henderson <[email protected]> Message-Id: <[email protected]> Signed-off-by: Alistair Francis <[email protected]>
1 parent 3cbc897 commit 8380b3a

File tree

2 files changed

+15
-3
lines changed

2 files changed

+15
-3
lines changed

hw/rtc/goldfish_rtc.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,22 @@ static uint64_t goldfish_rtc_read(void *opaque, hwaddr offset,
9494
GoldfishRTCState *s = opaque;
9595
uint64_t r = 0;
9696

97+
/*
98+
* From the documentation linked at the top of the file:
99+
*
100+
* To read the value, the kernel must perform an IO_READ(TIME_LOW), which
101+
* returns an unsigned 32-bit value, before an IO_READ(TIME_HIGH), which
102+
* returns a signed 32-bit value, corresponding to the higher half of the
103+
* full value.
104+
*/
97105
switch (offset) {
98106
case RTC_TIME_LOW:
99-
r = goldfish_rtc_get_count(s) & 0xffffffff;
107+
r = goldfish_rtc_get_count(s);
108+
s->time_high = r >> 32;
109+
r &= 0xffffffff;
100110
break;
101111
case RTC_TIME_HIGH:
102-
r = goldfish_rtc_get_count(s) >> 32;
112+
r = s->time_high;
103113
break;
104114
case RTC_ALARM_LOW:
105115
r = s->alarm_next & 0xffffffff;
@@ -216,7 +226,7 @@ static const MemoryRegionOps goldfish_rtc_ops = {
216226

217227
static const VMStateDescription goldfish_rtc_vmstate = {
218228
.name = TYPE_GOLDFISH_RTC,
219-
.version_id = 1,
229+
.version_id = 2,
220230
.pre_save = goldfish_rtc_pre_save,
221231
.post_load = goldfish_rtc_post_load,
222232
.fields = (VMStateField[]) {
@@ -225,6 +235,7 @@ static const VMStateDescription goldfish_rtc_vmstate = {
225235
VMSTATE_UINT32(alarm_running, GoldfishRTCState),
226236
VMSTATE_UINT32(irq_pending, GoldfishRTCState),
227237
VMSTATE_UINT32(irq_enabled, GoldfishRTCState),
238+
VMSTATE_UINT32(time_high, GoldfishRTCState),
228239
VMSTATE_END_OF_LIST()
229240
}
230241
};

include/hw/rtc/goldfish_rtc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ typedef struct GoldfishRTCState {
4141
uint32_t alarm_running;
4242
uint32_t irq_pending;
4343
uint32_t irq_enabled;
44+
uint32_t time_high;
4445
} GoldfishRTCState;
4546

4647
#endif

0 commit comments

Comments
 (0)