xref: /openbmc/qemu/system/rtc.c (revision ea9cdbcf3a0b8d5497cddf87990f1b39d8f3bb0a)
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