m48t59-test.c (6598B)
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 "libqos/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 28static uint32_t base; 29static uint16_t reg_base = 0x1ff0; /* 0x7f0 for m48t02 */ 30static int base_year; 31static const char *base_machine; 32static bool use_mmio; 33 34static 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 39static 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 46static 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 52static 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 58static 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 67static 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 76static int bcd2dec(int value) 77{ 78 return (((value >> 4) & 0x0F) * 10) + (value & 0x0F); 79} 80 81static 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 102static 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 110static 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#ifndef __sun__ 141 date->tm_gmtoff = 0; 142#endif 143 144 ts = mktime(date); 145} 146 147static QTestState *m48t59_qtest_start(void) 148{ 149 return qtest_initf("-M %s -rtc clock=vm", base_machine); 150} 151 152static 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 *s = 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(s, &date[0]); 177 cmos_get_date_time(s, &date[1]); 178 cmos_get_date_time(s, &date[2]); 179 cmos_get_date_time(s, &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 t, s; 196 197 start.tm_isdst = datep->tm_isdst; 198 199 t = (long)mktime(datep); 200 s = (long)mktime(&start); 201 if (t < s) { 202 g_test_message("RTC is %ld second(s) behind wall-clock", (s - t)); 203 } else { 204 g_test_message("RTC is %ld second(s) ahead of wall-clock", (t - s)); 205 } 206 207 g_assert_cmpint(ABS(t - s), <=, wiggle); 208 } 209 210 qtest_quit(s); 211} 212 213/* success if no crash or abort */ 214static void fuzz_registers(void) 215{ 216 unsigned int i; 217 QTestState *s = m48t59_qtest_start(); 218 219 for (i = 0; i < 1000; i++) { 220 uint8_t reg, val; 221 222 reg = (uint8_t)g_test_rand_int_range(0, 16); 223 val = (uint8_t)g_test_rand_int_range(0, 256); 224 225 if (reg == 7) { 226 /* watchdog setup register, may trigger system reset, skip */ 227 continue; 228 } 229 230 cmos_write(s, reg, val); 231 cmos_read(s, reg); 232 } 233 234 qtest_quit(s); 235} 236 237static void base_setup(void) 238{ 239 const char *arch = qtest_get_arch(); 240 241 if (g_str_equal(arch, "sparc")) { 242 /* Note: For sparc64, we'd need to map-in the PCI bridge memory first */ 243 base = 0x71200000; 244 base_year = 1968; 245 base_machine = "SS-5"; 246 use_mmio = true; 247 } else if (g_str_equal(arch, "ppc") || g_str_equal(arch, "ppc64")) { 248 base = 0xF0000000; 249 base_year = 1968; 250 base_machine = "ref405ep"; 251 use_mmio = true; 252 } else { 253 g_assert_not_reached(); 254 } 255} 256 257int main(int argc, char **argv) 258{ 259 base_setup(); 260 261 g_test_init(&argc, &argv, NULL); 262 263 if (g_test_slow()) { 264 /* Do not run this in timing-sensitive environments */ 265 qtest_add_func("/rtc/bcd-check-time", bcd_check_time); 266 } 267 qtest_add_func("/rtc/fuzz-registers", fuzz_registers); 268 return g_test_run(); 269}