qemu/softmmu/rtc.c
<<
>>
Prefs
   1/*
   2 * RTC configuration and clock read
   3 *
   4 * Copyright (c) 2003-2020 QEMU contributors
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24
  25#include "qemu/osdep.h"
  26#include "qemu/cutils.h"
  27#include "qapi/error.h"
  28#include "qapi/qmp/qerror.h"
  29#include "qemu/error-report.h"
  30#include "qemu/option.h"
  31#include "qemu/timer.h"
  32#include "qom/object.h"
  33#include "sysemu/replay.h"
  34#include "sysemu/sysemu.h"
  35#include "sysemu/rtc.h"
  36#include "hw/rtc/mc146818rtc.h"
  37
  38static enum {
  39    RTC_BASE_UTC,
  40    RTC_BASE_LOCALTIME,
  41    RTC_BASE_DATETIME,
  42} rtc_base_type = RTC_BASE_UTC;
  43static time_t rtc_ref_start_datetime;
  44static int rtc_realtime_clock_offset; /* used only with QEMU_CLOCK_REALTIME */
  45static int rtc_host_datetime_offset = -1; /* valid & used only with
  46                                             RTC_BASE_DATETIME */
  47QEMUClockType rtc_clock;
  48/***********************************************************/
  49/* RTC reference time/date access */
  50static time_t qemu_ref_timedate(QEMUClockType clock)
  51{
  52    time_t value = qemu_clock_get_ms(clock) / 1000;
  53    switch (clock) {
  54    case QEMU_CLOCK_REALTIME:
  55        value -= rtc_realtime_clock_offset;
  56        /* fall through */
  57    case QEMU_CLOCK_VIRTUAL:
  58        value += rtc_ref_start_datetime;
  59        break;
  60    case QEMU_CLOCK_HOST:
  61        if (rtc_base_type == RTC_BASE_DATETIME) {
  62            value -= rtc_host_datetime_offset;
  63        }
  64        break;
  65    default:
  66        assert(0);
  67    }
  68    return value;
  69}
  70
  71void qemu_get_timedate(struct tm *tm, int offset)
  72{
  73    time_t ti = qemu_ref_timedate(rtc_clock);
  74
  75    ti += offset;
  76
  77    switch (rtc_base_type) {
  78    case RTC_BASE_DATETIME:
  79    case RTC_BASE_UTC:
  80        gmtime_r(&ti, tm);
  81        break;
  82    case RTC_BASE_LOCALTIME:
  83        localtime_r(&ti, tm);
  84        break;
  85    }
  86}
  87
  88int qemu_timedate_diff(struct tm *tm)
  89{
  90    time_t seconds;
  91
  92    switch (rtc_base_type) {
  93    case RTC_BASE_DATETIME:
  94    case RTC_BASE_UTC:
  95        seconds = mktimegm(tm);
  96        break;
  97    case RTC_BASE_LOCALTIME:
  98    {
  99        struct tm tmp = *tm;
 100        tmp.tm_isdst = -1; /* use timezone to figure it out */
 101        seconds = mktime(&tmp);
 102        break;
 103    }
 104    default:
 105        abort();
 106    }
 107
 108    return seconds - qemu_ref_timedate(QEMU_CLOCK_HOST);
 109}
 110
 111static void configure_rtc_base_datetime(const char *startdate)
 112{
 113    time_t rtc_start_datetime;
 114    struct tm tm;
 115
 116    if (sscanf(startdate, "%d-%d-%dT%d:%d:%d", &tm.tm_year, &tm.tm_mon,
 117               &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6) {
 118        /* OK */
 119    } else if (sscanf(startdate, "%d-%d-%d",
 120                      &tm.tm_year, &tm.tm_mon, &tm.tm_mday) == 3) {
 121        tm.tm_hour = 0;
 122        tm.tm_min = 0;
 123        tm.tm_sec = 0;
 124    } else {
 125        goto date_fail;
 126    }
 127    tm.tm_year -= 1900;
 128    tm.tm_mon--;
 129    rtc_start_datetime = mktimegm(&tm);
 130    if (rtc_start_datetime == -1) {
 131    date_fail:
 132        error_report("invalid datetime format");
 133        error_printf("valid formats: "
 134                     "'2006-06-17T16:01:21' or '2006-06-17'\n");
 135        exit(1);
 136    }
 137    rtc_host_datetime_offset = rtc_ref_start_datetime - rtc_start_datetime;
 138    rtc_ref_start_datetime = rtc_start_datetime;
 139}
 140
 141void configure_rtc(QemuOpts *opts)
 142{
 143    const char *value;
 144
 145    /* Set defaults */
 146    rtc_clock = QEMU_CLOCK_HOST;
 147    rtc_ref_start_datetime = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
 148    rtc_realtime_clock_offset = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000;
 149
 150    value = qemu_opt_get(opts, "base");
 151    if (value) {
 152        if (!strcmp(value, "utc")) {
 153            rtc_base_type = RTC_BASE_UTC;
 154        } else if (!strcmp(value, "localtime")) {
 155            rtc_base_type = RTC_BASE_LOCALTIME;
 156            replay_add_blocker("-rtc base=localtime");
 157        } else {
 158            rtc_base_type = RTC_BASE_DATETIME;
 159            configure_rtc_base_datetime(value);
 160        }
 161    }
 162    value = qemu_opt_get(opts, "clock");
 163    if (value) {
 164        if (!strcmp(value, "host")) {
 165            rtc_clock = QEMU_CLOCK_HOST;
 166        } else if (!strcmp(value, "rt")) {
 167            rtc_clock = QEMU_CLOCK_REALTIME;
 168        } else if (!strcmp(value, "vm")) {
 169            rtc_clock = QEMU_CLOCK_VIRTUAL;
 170        } else {
 171            error_report("invalid option value '%s'", value);
 172            exit(1);
 173        }
 174    }
 175    value = qemu_opt_get(opts, "driftfix");
 176    if (value) {
 177        if (!strcmp(value, "slew")) {
 178            object_register_sugar_prop(TYPE_MC146818_RTC,
 179                                       "lost_tick_policy",
 180                                       "slew",
 181                                       false);
 182            if (!object_class_by_name(TYPE_MC146818_RTC)) {
 183                warn_report("driftfix 'slew' is not available with this machine");
 184            }
 185        } else if (!strcmp(value, "none")) {
 186            /* discard is default */
 187        } else {
 188            error_report("invalid option value '%s'", value);
 189            exit(1);
 190        }
 191    }
 192}
 193