]> Git Repo - qemu.git/blob - tests/m48t59-test.c
Merge remote-tracking branch 'kraxel/pixman.v8' into staging
[qemu.git] / tests / m48t59-test.c
1 /*
2  * QTest testcase for the M48T59 and M48T08 real-time clocks
3  *
4  * Based on MC146818 RTC test:
5  * Copyright IBM, Corp. 2012
6  *
7  * Authors:
8  *  Anthony Liguori   <[email protected]>
9  *
10  * This work is licensed under the terms of the GNU GPL, version 2 or later.
11  * See the COPYING file in the top-level directory.
12  *
13  */
14 #include "libqtest.h"
15
16 #include <glib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21
22 #define RTC_SECONDS             0x9
23 #define RTC_MINUTES             0xa
24 #define RTC_HOURS               0xb
25
26 #define RTC_DAY_OF_WEEK         0xc
27 #define RTC_DAY_OF_MONTH        0xd
28 #define RTC_MONTH               0xe
29 #define RTC_YEAR                0xf
30
31 static uint32_t base;
32 static uint16_t reg_base = 0x1ff0; /* 0x7f0 for m48t02 */
33 static int base_year;
34 static bool use_mmio;
35
36 static uint8_t cmos_read_mmio(uint8_t reg)
37 {
38     return readb(base + (uint32_t)reg_base + (uint32_t)reg);
39 }
40
41 static void cmos_write_mmio(uint8_t reg, uint8_t val)
42 {
43     uint8_t data = val;
44
45     writeb(base + (uint32_t)reg_base + (uint32_t)reg, data);
46 }
47
48 static uint8_t cmos_read_ioio(uint8_t reg)
49 {
50     outw(base + 0, reg_base + (uint16_t)reg);
51     return inb(base + 3);
52 }
53
54 static void cmos_write_ioio(uint8_t reg, uint8_t val)
55 {
56     outw(base + 0, reg_base + (uint16_t)reg);
57     outb(base + 3, val);
58 }
59
60 static uint8_t cmos_read(uint8_t reg)
61 {
62     if (use_mmio) {
63         return cmos_read_mmio(reg);
64     } else {
65         return cmos_read_ioio(reg);
66     }
67 }
68
69 static void cmos_write(uint8_t reg, uint8_t val)
70 {
71     if (use_mmio) {
72         cmos_write_mmio(reg, val);
73     } else {
74         cmos_write_ioio(reg, val);
75     }
76 }
77
78 static int bcd2dec(int value)
79 {
80     return (((value >> 4) & 0x0F) * 10) + (value & 0x0F);
81 }
82
83 static int tm_cmp(struct tm *lhs, struct tm *rhs)
84 {
85     time_t a, b;
86     struct tm d1, d2;
87
88     memcpy(&d1, lhs, sizeof(d1));
89     memcpy(&d2, rhs, sizeof(d2));
90
91     a = mktime(&d1);
92     b = mktime(&d2);
93
94     if (a < b) {
95         return -1;
96     } else if (a > b) {
97         return 1;
98     }
99
100     return 0;
101 }
102
103 #if 0
104 static void print_tm(struct tm *tm)
105 {
106     printf("%04d-%02d-%02d %02d:%02d:%02d %+02ld\n",
107            tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
108            tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_gmtoff);
109 }
110 #endif
111
112 static void cmos_get_date_time(struct tm *date)
113 {
114     int sec, min, hour, mday, mon, year;
115     time_t ts;
116     struct tm dummy;
117
118     sec = cmos_read(RTC_SECONDS);
119     min = cmos_read(RTC_MINUTES);
120     hour = cmos_read(RTC_HOURS);
121     mday = cmos_read(RTC_DAY_OF_MONTH);
122     mon = cmos_read(RTC_MONTH);
123     year = cmos_read(RTC_YEAR);
124
125     sec = bcd2dec(sec);
126     min = bcd2dec(min);
127     hour = bcd2dec(hour);
128     mday = bcd2dec(mday);
129     mon = bcd2dec(mon);
130     year = bcd2dec(year);
131
132     ts = time(NULL);
133     localtime_r(&ts, &dummy);
134
135     date->tm_isdst = dummy.tm_isdst;
136     date->tm_sec = sec;
137     date->tm_min = min;
138     date->tm_hour = hour;
139     date->tm_mday = mday;
140     date->tm_mon = mon - 1;
141     date->tm_year = base_year + year - 1900;
142 #ifndef __sun__
143     date->tm_gmtoff = 0;
144 #endif
145
146     ts = mktime(date);
147 }
148
149 static void check_time(int wiggle)
150 {
151     struct tm start, date[4], end;
152     struct tm *datep;
153     time_t ts;
154
155     /*
156      * This check assumes a few things.  First, we cannot guarantee that we get
157      * a consistent reading from the wall clock because we may hit an edge of
158      * the clock while reading.  To work around this, we read four clock readings
159      * such that at least two of them should match.  We need to assume that one
160      * reading is corrupt so we need four readings to ensure that we have at
161      * least two consecutive identical readings
162      *
163      * It's also possible that we'll cross an edge reading the host clock so
164      * simply check to make sure that the clock reading is within the period of
165      * when we expect it to be.
166      */
167
168     ts = time(NULL);
169     gmtime_r(&ts, &start);
170
171     cmos_get_date_time(&date[0]);
172     cmos_get_date_time(&date[1]);
173     cmos_get_date_time(&date[2]);
174     cmos_get_date_time(&date[3]);
175
176     ts = time(NULL);
177     gmtime_r(&ts, &end);
178
179     if (tm_cmp(&date[0], &date[1]) == 0) {
180         datep = &date[0];
181     } else if (tm_cmp(&date[1], &date[2]) == 0) {
182         datep = &date[1];
183     } else if (tm_cmp(&date[2], &date[3]) == 0) {
184         datep = &date[2];
185     } else {
186         g_assert_not_reached();
187     }
188
189     if (!(tm_cmp(&start, datep) <= 0 && tm_cmp(datep, &end) <= 0)) {
190         long t, s;
191
192         start.tm_isdst = datep->tm_isdst;
193
194         t = (long)mktime(datep);
195         s = (long)mktime(&start);
196         if (t < s) {
197             g_test_message("RTC is %ld second(s) behind wall-clock\n", (s - t));
198         } else {
199             g_test_message("RTC is %ld second(s) ahead of wall-clock\n", (t - s));
200         }
201
202         g_assert_cmpint(ABS(t - s), <=, wiggle);
203     }
204 }
205
206 static int wiggle = 2;
207
208 static void bcd_check_time(void)
209 {
210     if (strcmp(qtest_get_arch(), "sparc64") == 0) {
211         base = 0x74;
212         base_year = 1900;
213         use_mmio = false;
214     } else if (strcmp(qtest_get_arch(), "sparc") == 0) {
215         base = 0x71200000;
216         base_year = 1968;
217         use_mmio = true;
218     } else { /* PPC: need to map macio in PCI */
219         g_assert_not_reached();
220     }
221     check_time(wiggle);
222 }
223
224 /* success if no crash or abort */
225 static void fuzz_registers(void)
226 {
227     unsigned int i;
228
229     for (i = 0; i < 1000; i++) {
230         uint8_t reg, val;
231
232         reg = (uint8_t)g_test_rand_int_range(0, 16);
233         val = (uint8_t)g_test_rand_int_range(0, 256);
234
235         if (reg == 7) {
236             /* watchdog setup register, may trigger system reset, skip */
237             continue;
238         }
239
240         cmos_write(reg, val);
241         cmos_read(reg);
242     }
243 }
244
245 int main(int argc, char **argv)
246 {
247     QTestState *s = NULL;
248     int ret;
249
250     g_test_init(&argc, &argv, NULL);
251
252     s = qtest_start("-display none -rtc clock=vm");
253
254     qtest_add_func("/rtc/bcd/check-time", bcd_check_time);
255     qtest_add_func("/rtc/fuzz-registers", fuzz_registers);
256     ret = g_test_run();
257
258     if (s) {
259         qtest_quit(s);
260     }
261
262     return ret;
263 }
This page took 0.03662 seconds and 4 git commands to generate.