18d7f2e76SPhilippe Mathieu-Daudé /*
28d7f2e76SPhilippe Mathieu-Daudé * RTC configuration and clock read
38d7f2e76SPhilippe Mathieu-Daudé *
48d7f2e76SPhilippe Mathieu-Daudé * Copyright (c) 2003-2020 QEMU contributors
58d7f2e76SPhilippe Mathieu-Daudé *
68d7f2e76SPhilippe Mathieu-Daudé * Permission is hereby granted, free of charge, to any person obtaining a copy
78d7f2e76SPhilippe Mathieu-Daudé * of this software and associated documentation files (the "Software"), to deal
88d7f2e76SPhilippe Mathieu-Daudé * in the Software without restriction, including without limitation the rights
98d7f2e76SPhilippe Mathieu-Daudé * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
108d7f2e76SPhilippe Mathieu-Daudé * copies of the Software, and to permit persons to whom the Software is
118d7f2e76SPhilippe Mathieu-Daudé * furnished to do so, subject to the following conditions:
128d7f2e76SPhilippe Mathieu-Daudé *
138d7f2e76SPhilippe Mathieu-Daudé * The above copyright notice and this permission notice shall be included in
148d7f2e76SPhilippe Mathieu-Daudé * all copies or substantial portions of the Software.
158d7f2e76SPhilippe Mathieu-Daudé *
168d7f2e76SPhilippe Mathieu-Daudé * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
178d7f2e76SPhilippe Mathieu-Daudé * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
188d7f2e76SPhilippe Mathieu-Daudé * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
198d7f2e76SPhilippe Mathieu-Daudé * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
208d7f2e76SPhilippe Mathieu-Daudé * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
218d7f2e76SPhilippe Mathieu-Daudé * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
228d7f2e76SPhilippe Mathieu-Daudé * THE SOFTWARE.
238d7f2e76SPhilippe Mathieu-Daudé */
248d7f2e76SPhilippe Mathieu-Daudé
258d7f2e76SPhilippe Mathieu-Daudé #include "qemu/osdep.h"
268d7f2e76SPhilippe Mathieu-Daudé #include "qemu/cutils.h"
278d7f2e76SPhilippe Mathieu-Daudé #include "qapi/error.h"
288d7f2e76SPhilippe Mathieu-Daudé #include "qemu/error-report.h"
298d7f2e76SPhilippe Mathieu-Daudé #include "qemu/option.h"
308d7f2e76SPhilippe Mathieu-Daudé #include "qemu/timer.h"
318d7f2e76SPhilippe Mathieu-Daudé #include "qom/object.h"
328d7f2e76SPhilippe Mathieu-Daudé #include "sysemu/replay.h"
338d7f2e76SPhilippe Mathieu-Daudé #include "sysemu/sysemu.h"
348d7f2e76SPhilippe Mathieu-Daudé #include "sysemu/rtc.h"
358d7f2e76SPhilippe Mathieu-Daudé #include "hw/rtc/mc146818rtc.h"
368d7f2e76SPhilippe Mathieu-Daudé
378d7f2e76SPhilippe Mathieu-Daudé static enum {
388d7f2e76SPhilippe Mathieu-Daudé RTC_BASE_UTC,
398d7f2e76SPhilippe Mathieu-Daudé RTC_BASE_LOCALTIME,
408d7f2e76SPhilippe Mathieu-Daudé RTC_BASE_DATETIME,
418d7f2e76SPhilippe Mathieu-Daudé } rtc_base_type = RTC_BASE_UTC;
428d7f2e76SPhilippe Mathieu-Daudé static time_t rtc_ref_start_datetime;
438d7f2e76SPhilippe Mathieu-Daudé static int rtc_realtime_clock_offset; /* used only with QEMU_CLOCK_REALTIME */
448d7f2e76SPhilippe Mathieu-Daudé static int rtc_host_datetime_offset = -1; /* valid & used only with
458d7f2e76SPhilippe Mathieu-Daudé RTC_BASE_DATETIME */
468d7f2e76SPhilippe Mathieu-Daudé QEMUClockType rtc_clock;
478d7f2e76SPhilippe Mathieu-Daudé /***********************************************************/
488d7f2e76SPhilippe Mathieu-Daudé /* RTC reference time/date access */
qemu_ref_timedate(QEMUClockType clock)498d7f2e76SPhilippe Mathieu-Daudé static time_t qemu_ref_timedate(QEMUClockType clock)
508d7f2e76SPhilippe Mathieu-Daudé {
518d7f2e76SPhilippe Mathieu-Daudé time_t value = qemu_clock_get_ms(clock) / 1000;
528d7f2e76SPhilippe Mathieu-Daudé switch (clock) {
538d7f2e76SPhilippe Mathieu-Daudé case QEMU_CLOCK_REALTIME:
548d7f2e76SPhilippe Mathieu-Daudé value -= rtc_realtime_clock_offset;
558d7f2e76SPhilippe Mathieu-Daudé /* fall through */
568d7f2e76SPhilippe Mathieu-Daudé case QEMU_CLOCK_VIRTUAL:
578d7f2e76SPhilippe Mathieu-Daudé value += rtc_ref_start_datetime;
588d7f2e76SPhilippe Mathieu-Daudé break;
598d7f2e76SPhilippe Mathieu-Daudé case QEMU_CLOCK_HOST:
608d7f2e76SPhilippe Mathieu-Daudé if (rtc_base_type == RTC_BASE_DATETIME) {
618d7f2e76SPhilippe Mathieu-Daudé value -= rtc_host_datetime_offset;
628d7f2e76SPhilippe Mathieu-Daudé }
638d7f2e76SPhilippe Mathieu-Daudé break;
648d7f2e76SPhilippe Mathieu-Daudé default:
65*6e7d8c5fSPierrick Bouvier g_assert_not_reached();
668d7f2e76SPhilippe Mathieu-Daudé }
678d7f2e76SPhilippe Mathieu-Daudé return value;
688d7f2e76SPhilippe Mathieu-Daudé }
698d7f2e76SPhilippe Mathieu-Daudé
qemu_get_timedate(struct tm * tm,time_t offset)708d7f2e76SPhilippe Mathieu-Daudé void qemu_get_timedate(struct tm *tm, time_t offset)
718d7f2e76SPhilippe Mathieu-Daudé {
728d7f2e76SPhilippe Mathieu-Daudé time_t ti = qemu_ref_timedate(rtc_clock);
738d7f2e76SPhilippe Mathieu-Daudé
748d7f2e76SPhilippe Mathieu-Daudé ti += offset;
758d7f2e76SPhilippe Mathieu-Daudé
768d7f2e76SPhilippe Mathieu-Daudé switch (rtc_base_type) {
778d7f2e76SPhilippe Mathieu-Daudé case RTC_BASE_DATETIME:
788d7f2e76SPhilippe Mathieu-Daudé case RTC_BASE_UTC:
798d7f2e76SPhilippe Mathieu-Daudé gmtime_r(&ti, tm);
808d7f2e76SPhilippe Mathieu-Daudé break;
818d7f2e76SPhilippe Mathieu-Daudé case RTC_BASE_LOCALTIME:
828d7f2e76SPhilippe Mathieu-Daudé localtime_r(&ti, tm);
838d7f2e76SPhilippe Mathieu-Daudé break;
848d7f2e76SPhilippe Mathieu-Daudé }
858d7f2e76SPhilippe Mathieu-Daudé }
868d7f2e76SPhilippe Mathieu-Daudé
qemu_timedate_diff(struct tm * tm)878d7f2e76SPhilippe Mathieu-Daudé time_t qemu_timedate_diff(struct tm *tm)
888d7f2e76SPhilippe Mathieu-Daudé {
898d7f2e76SPhilippe Mathieu-Daudé time_t seconds;
908d7f2e76SPhilippe Mathieu-Daudé
918d7f2e76SPhilippe Mathieu-Daudé switch (rtc_base_type) {
928d7f2e76SPhilippe Mathieu-Daudé case RTC_BASE_DATETIME:
938d7f2e76SPhilippe Mathieu-Daudé case RTC_BASE_UTC:
948d7f2e76SPhilippe Mathieu-Daudé seconds = mktimegm(tm);
958d7f2e76SPhilippe Mathieu-Daudé break;
968d7f2e76SPhilippe Mathieu-Daudé case RTC_BASE_LOCALTIME:
978d7f2e76SPhilippe Mathieu-Daudé {
988d7f2e76SPhilippe Mathieu-Daudé struct tm tmp = *tm;
998d7f2e76SPhilippe Mathieu-Daudé tmp.tm_isdst = -1; /* use timezone to figure it out */
1008d7f2e76SPhilippe Mathieu-Daudé seconds = mktime(&tmp);
1018d7f2e76SPhilippe Mathieu-Daudé break;
1028d7f2e76SPhilippe Mathieu-Daudé }
1038d7f2e76SPhilippe Mathieu-Daudé default:
1048d7f2e76SPhilippe Mathieu-Daudé abort();
1058d7f2e76SPhilippe Mathieu-Daudé }
1068d7f2e76SPhilippe Mathieu-Daudé
1078d7f2e76SPhilippe Mathieu-Daudé return seconds - qemu_ref_timedate(QEMU_CLOCK_HOST);
1088d7f2e76SPhilippe Mathieu-Daudé }
1098d7f2e76SPhilippe Mathieu-Daudé
configure_rtc_base_datetime(const char * startdate)1108d7f2e76SPhilippe Mathieu-Daudé static void configure_rtc_base_datetime(const char *startdate)
1118d7f2e76SPhilippe Mathieu-Daudé {
1128d7f2e76SPhilippe Mathieu-Daudé time_t rtc_start_datetime;
1138d7f2e76SPhilippe Mathieu-Daudé struct tm tm;
1148d7f2e76SPhilippe Mathieu-Daudé
1158d7f2e76SPhilippe Mathieu-Daudé if (sscanf(startdate, "%d-%d-%dT%d:%d:%d", &tm.tm_year, &tm.tm_mon,
1168d7f2e76SPhilippe Mathieu-Daudé &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6) {
1178d7f2e76SPhilippe Mathieu-Daudé /* OK */
1188d7f2e76SPhilippe Mathieu-Daudé } else if (sscanf(startdate, "%d-%d-%d",
1198d7f2e76SPhilippe Mathieu-Daudé &tm.tm_year, &tm.tm_mon, &tm.tm_mday) == 3) {
1208d7f2e76SPhilippe Mathieu-Daudé tm.tm_hour = 0;
1218d7f2e76SPhilippe Mathieu-Daudé tm.tm_min = 0;
1228d7f2e76SPhilippe Mathieu-Daudé tm.tm_sec = 0;
1238d7f2e76SPhilippe Mathieu-Daudé } else {
1248d7f2e76SPhilippe Mathieu-Daudé goto date_fail;
1258d7f2e76SPhilippe Mathieu-Daudé }
1268d7f2e76SPhilippe Mathieu-Daudé tm.tm_year -= 1900;
1278d7f2e76SPhilippe Mathieu-Daudé tm.tm_mon--;
1288d7f2e76SPhilippe Mathieu-Daudé rtc_start_datetime = mktimegm(&tm);
1298d7f2e76SPhilippe Mathieu-Daudé if (rtc_start_datetime == -1) {
1308d7f2e76SPhilippe Mathieu-Daudé date_fail:
1318d7f2e76SPhilippe Mathieu-Daudé error_report("invalid datetime format");
1328d7f2e76SPhilippe Mathieu-Daudé error_printf("valid formats: "
1338d7f2e76SPhilippe Mathieu-Daudé "'2006-06-17T16:01:21' or '2006-06-17'\n");
1348d7f2e76SPhilippe Mathieu-Daudé exit(1);
1358d7f2e76SPhilippe Mathieu-Daudé }
1368d7f2e76SPhilippe Mathieu-Daudé rtc_host_datetime_offset = rtc_ref_start_datetime - rtc_start_datetime;
1378d7f2e76SPhilippe Mathieu-Daudé rtc_ref_start_datetime = rtc_start_datetime;
1388d7f2e76SPhilippe Mathieu-Daudé }
1398d7f2e76SPhilippe Mathieu-Daudé
configure_rtc(QemuOpts * opts)1408d7f2e76SPhilippe Mathieu-Daudé void configure_rtc(QemuOpts *opts)
1418d7f2e76SPhilippe Mathieu-Daudé {
1428d7f2e76SPhilippe Mathieu-Daudé const char *value;
1438d7f2e76SPhilippe Mathieu-Daudé
1448d7f2e76SPhilippe Mathieu-Daudé /* Set defaults */
1458d7f2e76SPhilippe Mathieu-Daudé rtc_clock = QEMU_CLOCK_HOST;
1468d7f2e76SPhilippe Mathieu-Daudé rtc_ref_start_datetime = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
1478d7f2e76SPhilippe Mathieu-Daudé rtc_realtime_clock_offset = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000;
1488d7f2e76SPhilippe Mathieu-Daudé
1498d7f2e76SPhilippe Mathieu-Daudé value = qemu_opt_get(opts, "base");
1508d7f2e76SPhilippe Mathieu-Daudé if (value) {
1518d7f2e76SPhilippe Mathieu-Daudé if (!strcmp(value, "utc")) {
1528d7f2e76SPhilippe Mathieu-Daudé rtc_base_type = RTC_BASE_UTC;
1538d7f2e76SPhilippe Mathieu-Daudé } else if (!strcmp(value, "localtime")) {
1548d7f2e76SPhilippe Mathieu-Daudé rtc_base_type = RTC_BASE_LOCALTIME;
1558d7f2e76SPhilippe Mathieu-Daudé replay_add_blocker("-rtc base=localtime");
1568d7f2e76SPhilippe Mathieu-Daudé } else {
1578d7f2e76SPhilippe Mathieu-Daudé rtc_base_type = RTC_BASE_DATETIME;
1588d7f2e76SPhilippe Mathieu-Daudé configure_rtc_base_datetime(value);
1598d7f2e76SPhilippe Mathieu-Daudé }
1608d7f2e76SPhilippe Mathieu-Daudé }
1618d7f2e76SPhilippe Mathieu-Daudé value = qemu_opt_get(opts, "clock");
1628d7f2e76SPhilippe Mathieu-Daudé if (value) {
1638d7f2e76SPhilippe Mathieu-Daudé if (!strcmp(value, "host")) {
1648d7f2e76SPhilippe Mathieu-Daudé rtc_clock = QEMU_CLOCK_HOST;
1658d7f2e76SPhilippe Mathieu-Daudé } else if (!strcmp(value, "rt")) {
1668d7f2e76SPhilippe Mathieu-Daudé rtc_clock = QEMU_CLOCK_REALTIME;
1678d7f2e76SPhilippe Mathieu-Daudé } else if (!strcmp(value, "vm")) {
1688d7f2e76SPhilippe Mathieu-Daudé rtc_clock = QEMU_CLOCK_VIRTUAL;
1698d7f2e76SPhilippe Mathieu-Daudé } else {
1708d7f2e76SPhilippe Mathieu-Daudé error_report("invalid option value '%s'", value);
1718d7f2e76SPhilippe Mathieu-Daudé exit(1);
1728d7f2e76SPhilippe Mathieu-Daudé }
1738d7f2e76SPhilippe Mathieu-Daudé }
1748d7f2e76SPhilippe Mathieu-Daudé value = qemu_opt_get(opts, "driftfix");
1758d7f2e76SPhilippe Mathieu-Daudé if (value) {
1768d7f2e76SPhilippe Mathieu-Daudé if (!strcmp(value, "slew")) {
1778d7f2e76SPhilippe Mathieu-Daudé object_register_sugar_prop(TYPE_MC146818_RTC,
1788d7f2e76SPhilippe Mathieu-Daudé "lost_tick_policy",
1798d7f2e76SPhilippe Mathieu-Daudé "slew",
1808d7f2e76SPhilippe Mathieu-Daudé false);
1818d7f2e76SPhilippe Mathieu-Daudé if (!object_class_by_name(TYPE_MC146818_RTC)) {
1828d7f2e76SPhilippe Mathieu-Daudé warn_report("driftfix 'slew' is not available with this machine");
1838d7f2e76SPhilippe Mathieu-Daudé }
1848d7f2e76SPhilippe Mathieu-Daudé } else if (!strcmp(value, "none")) {
1858d7f2e76SPhilippe Mathieu-Daudé /* discard is default */
1868d7f2e76SPhilippe Mathieu-Daudé } else {
1878d7f2e76SPhilippe Mathieu-Daudé error_report("invalid option value '%s'", value);
1888d7f2e76SPhilippe Mathieu-Daudé exit(1);
1898d7f2e76SPhilippe Mathieu-Daudé }
1908d7f2e76SPhilippe Mathieu-Daudé }
1918d7f2e76SPhilippe Mathieu-Daudé }
192