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 <aliguori@us.ibm.com> 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 15 #include "qemu/osdep.h" 16 17 #include "libqtest.h" 18 19 #define RTC_SECONDS 0x9 20 #define RTC_MINUTES 0xa 21 #define RTC_HOURS 0xb 22 23 #define RTC_DAY_OF_WEEK 0xc 24 #define RTC_DAY_OF_MONTH 0xd 25 #define RTC_MONTH 0xe 26 #define RTC_YEAR 0xf 27 28 static uint32_t base; 29 static uint16_t reg_base = 0x1ff0; /* 0x7f0 for m48t02 */ 30 static int base_year; 31 static const char *base_machine; 32 static bool use_mmio; 33 34 static uint8_t cmos_read_mmio(QTestState *s, uint8_t reg) 35 { 36 return qtest_readb(s, base + (uint32_t)reg_base + (uint32_t)reg); 37 } 38 39 static void cmos_write_mmio(QTestState *s, uint8_t reg, uint8_t val) 40 { 41 uint8_t data = val; 42 43 qtest_writeb(s, base + (uint32_t)reg_base + (uint32_t)reg, data); 44 } 45 46 static uint8_t cmos_read_ioio(QTestState *s, uint8_t reg) 47 { 48 qtest_outw(s, base + 0, reg_base + (uint16_t)reg); 49 return qtest_inb(s, base + 3); 50 } 51 52 static void cmos_write_ioio(QTestState *s, uint8_t reg, uint8_t val) 53 { 54 qtest_outw(s, base + 0, reg_base + (uint16_t)reg); 55 qtest_outb(s, base + 3, val); 56 } 57 58 static uint8_t cmos_read(QTestState *s, uint8_t reg) 59 { 60 if (use_mmio) { 61 return cmos_read_mmio(s, reg); 62 } else { 63 return cmos_read_ioio(s, reg); 64 } 65 } 66 67 static void cmos_write(QTestState *s, uint8_t reg, uint8_t val) 68 { 69 if (use_mmio) { 70 cmos_write_mmio(s, reg, val); 71 } else { 72 cmos_write_ioio(s, reg, val); 73 } 74 } 75 76 static int bcd2dec(int value) 77 { 78 return (((value >> 4) & 0x0F) * 10) + (value & 0x0F); 79 } 80 81 static int tm_cmp(struct tm *lhs, struct tm *rhs) 82 { 83 time_t a, b; 84 struct tm d1, d2; 85 86 memcpy(&d1, lhs, sizeof(d1)); 87 memcpy(&d2, rhs, sizeof(d2)); 88 89 a = mktime(&d1); 90 b = mktime(&d2); 91 92 if (a < b) { 93 return -1; 94 } else if (a > b) { 95 return 1; 96 } 97 98 return 0; 99 } 100 101 #if 0 102 static void print_tm(struct tm *tm) 103 { 104 printf("%04d-%02d-%02d %02d:%02d:%02d %+02ld\n", 105 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, 106 tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_gmtoff); 107 } 108 #endif 109 110 static void cmos_get_date_time(QTestState *s, struct tm *date) 111 { 112 int sec, min, hour, mday, mon, year; 113 time_t ts; 114 struct tm dummy; 115 116 sec = cmos_read(s, RTC_SECONDS); 117 min = cmos_read(s, RTC_MINUTES); 118 hour = cmos_read(s, RTC_HOURS); 119 mday = cmos_read(s, RTC_DAY_OF_MONTH); 120 mon = cmos_read(s, RTC_MONTH); 121 year = cmos_read(s, RTC_YEAR); 122 123 sec = bcd2dec(sec); 124 min = bcd2dec(min); 125 hour = bcd2dec(hour); 126 mday = bcd2dec(mday); 127 mon = bcd2dec(mon); 128 year = bcd2dec(year); 129 130 ts = time(NULL); 131 localtime_r(&ts, &dummy); 132 133 date->tm_isdst = dummy.tm_isdst; 134 date->tm_sec = sec; 135 date->tm_min = min; 136 date->tm_hour = hour; 137 date->tm_mday = mday; 138 date->tm_mon = mon - 1; 139 date->tm_year = base_year + year - 1900; 140 #if !defined(__sun__) && !defined(_WIN32) 141 date->tm_gmtoff = 0; 142 #endif 143 144 ts = mktime(date); 145 } 146 147 static QTestState *m48t59_qtest_start(void) 148 { 149 return qtest_initf("-M %s -rtc clock=vm", base_machine); 150 } 151 152 static void bcd_check_time(void) 153 { 154 struct tm start, date[4], end; 155 struct tm *datep; 156 time_t ts; 157 const int wiggle = 2; 158 QTestState *qts = m48t59_qtest_start(); 159 160 /* 161 * This check assumes a few things. First, we cannot guarantee that we get 162 * a consistent reading from the wall clock because we may hit an edge of 163 * the clock while reading. To work around this, we read four clock readings 164 * such that at least two of them should match. We need to assume that one 165 * reading is corrupt so we need four readings to ensure that we have at 166 * least two consecutive identical readings 167 * 168 * It's also possible that we'll cross an edge reading the host clock so 169 * simply check to make sure that the clock reading is within the period of 170 * when we expect it to be. 171 */ 172 173 ts = time(NULL); 174 gmtime_r(&ts, &start); 175 176 cmos_get_date_time(qts, &date[0]); 177 cmos_get_date_time(qts, &date[1]); 178 cmos_get_date_time(qts, &date[2]); 179 cmos_get_date_time(qts, &date[3]); 180 181 ts = time(NULL); 182 gmtime_r(&ts, &end); 183 184 if (tm_cmp(&date[0], &date[1]) == 0) { 185 datep = &date[0]; 186 } else if (tm_cmp(&date[1], &date[2]) == 0) { 187 datep = &date[1]; 188 } else if (tm_cmp(&date[2], &date[3]) == 0) { 189 datep = &date[2]; 190 } else { 191 g_assert_not_reached(); 192 } 193 194 if (!(tm_cmp(&start, datep) <= 0 && tm_cmp(datep, &end) <= 0)) { 195 long date_s, start_s; 196 unsigned long diff; 197 198 start.tm_isdst = datep->tm_isdst; 199 200 date_s = (long)mktime(datep); 201 start_s = (long)mktime(&start); 202 if (date_s < start_s) { 203 diff = start_s - date_s; 204 g_test_message("RTC is %ld second(s) behind wall-clock", diff); 205 } else { 206 diff = date_s - start_s; 207 g_test_message("RTC is %ld second(s) ahead of wall-clock", diff); 208 } 209 210 g_assert_cmpint(diff, <=, wiggle); 211 } 212 213 qtest_quit(qts); 214 } 215 216 /* success if no crash or abort */ 217 static void fuzz_registers(void) 218 { 219 unsigned int i; 220 QTestState *s = m48t59_qtest_start(); 221 222 for (i = 0; i < 1000; i++) { 223 uint8_t reg, val; 224 225 reg = (uint8_t)g_test_rand_int_range(0, 16); 226 val = (uint8_t)g_test_rand_int_range(0, 256); 227 228 if (reg == 7) { 229 /* watchdog setup register, may trigger system reset, skip */ 230 continue; 231 } 232 233 cmos_write(s, reg, val); 234 cmos_read(s, reg); 235 } 236 237 qtest_quit(s); 238 } 239 240 static void base_setup(void) 241 { 242 const char *arch = qtest_get_arch(); 243 244 if (g_str_equal(arch, "sparc")) { 245 /* Note: For sparc64, we'd need to map-in the PCI bridge memory first */ 246 base = 0x71200000; 247 base_year = 1968; 248 base_machine = "SS-5"; 249 use_mmio = true; 250 } else if (g_str_equal(arch, "ppc") || g_str_equal(arch, "ppc64")) { 251 base = 0xF0000000; 252 base_year = 1968; 253 base_machine = "ref405ep"; 254 use_mmio = true; 255 } else { 256 g_assert_not_reached(); 257 } 258 } 259 260 int main(int argc, char **argv) 261 { 262 base_setup(); 263 264 g_test_init(&argc, &argv, NULL); 265 if (qtest_has_machine(base_machine)) { 266 if (g_test_slow()) { 267 /* Do not run this in timing-sensitive environments */ 268 qtest_add_func("/rtc/bcd-check-time", bcd_check_time); 269 } 270 qtest_add_func("/rtc/fuzz-registers", fuzz_registers); 271 } 272 return g_test_run(); 273 } 274