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-common.h"
  27#include "qemu/cutils.h"
  28#include "qapi/error.h"
  29#include "qapi/qmp/qerror.h"
  30#include "qemu/error-report.h"
  31#include "qemu/option.h"
  32#include "qemu/timer.h"
  33#include "qom/object.h"
  34#include "sysemu/replay.h"
  35#include "sysemu/sysemu.h"
  36
  37static enum {
  38    RTC_BASE_UTC,
  39    RTC_BASE_LOCALTIME,
  40    RTC_BASE_DATETIME,
  41} rtc_base_type = RTC_BASE_UTC;
  42static time_t rtc_ref_start_datetime;
  43static int rtc_realtime_clock_offset; /* used only with QEMU_CLOCK_REALTIME */
  44static int rtc_host_datetime_offset = -1; /* valid & used only with
  45                                             RTC_BASE_DATETIME */
  46QEMUClockType rtc_clock;
  47/***********************************************************/
  48/* RTC reference time/date access */
  49static time_t qemu_ref_timedate(QEMUClockType clock)
  50{
  51    time_t value = qemu_clock_get_ms(clock) / 1000;
  52    switch (clock) {
  53    case QEMU_CLOCK_REALTIME:
  54        value -= rtc_realtime_clock_offset;
  55        /* fall through */
  56    case QEMU_CLOCK_VIRTUAL:
  57        value += rtc_ref_start_datetime;
  58        break;
  59    case QEMU_CLOCK_HOST:
  60        if (rtc_base_type == RTC_BASE_DATETIME) {
  61            value -= rtc_host_datetime_offset;
  62        }
  63        break;
  64    default:
  65        assert(0);
  66    }
  67    return value;
  68}
  69
  70void qemu_get_timedate(struct tm *tm, int offset)
  71{
  72    time_t ti = qemu_ref_timedate(rtc_clock);
  73
  74    ti += offset;
  75
  76    switch (rtc_base_type) {
  77    case RTC_BASE_DATETIME:
  78    case RTC_BASE_UTC:
  79        gmtime_r(&ti, tm);
  80        break;
  81    case RTC_BASE_LOCALTIME:
  82        localtime_r(&ti, tm);
  83        break;
  84    }
  85}
  86
  87int qemu_timedate_diff(struct tm *tm)
  88{
  89    time_t seconds;
  90
  91    switch (rtc_base_type) {
  92    case RTC_BASE_DATETIME:
  93    case RTC_BASE_UTC:
  94        seconds = mktimegm(tm);
  95        break;
  96    case RTC_BASE_LOCALTIME:
  97    {
  98        struct tm tmp = *tm;
  99        tmp.tm_isdst = -1; /* use timezone to figure it out */
 100        seconds = mktime(&tmp);
 101        break;
 102    }
 103    default:
 104        abort();
 105    }
 106
 107    return seconds - qemu_ref_timedate(QEMU_CLOCK_HOST);
 108}
 109
 110static void configure_rtc_base_datetime(const char *startdate)
 111{
 112    time_t rtc_start_datetime;
 113    struct tm tm;
 114
 115    if (sscanf(startdate, "%d-%d-%dT%d:%d:%d", &tm.tm_year, &tm.tm_mon,
 116               &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6) {
 117        /* OK */
 118    } else if (sscanf(startdate, "%d-%d-%d",
 119                      &tm.tm_year, &tm.tm_mon, &tm.tm_mday) == 3) {
 120        tm.tm_hour = 0;
 121        tm.tm_min = 0;
 122        tm.tm_sec = 0;
 123    } else {
 124        goto date_fail;
 125    }
 126    tm.tm_year -= 1900;
 127    tm.tm_mon--;
 128    rtc_start_datetime = mktimegm(&tm);
 129    if (rtc_start_datetime == -1) {
 130    date_fail:
 131        error_report("invalid datetime format");
 132        error_printf("valid formats: "
 133                     "'2006-06-17T16:01:21' or '2006-06-17'\n");
 134        exit(1);
 135    }
 136    rtc_host_datetime_offset = rtc_ref_start_datetime - rtc_start_datetime;
 137    rtc_ref_start_datetime = rtc_start_datetime;
 138}
 139
 140void configure_rtc(QemuOpts *opts)
 141{
 142    const char *value;
 143
 144    /* Set defaults */
 145    rtc_clock = QEMU_CLOCK_HOST;
 146    rtc_ref_start_datetime = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
 147    rtc_realtime_clock_offset = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000;
 148
 149    value = qemu_opt_get(opts, "base");
 150    if (value) {
 151        if (!strcmp(value, "utc")) {
 152            rtc_base_type = RTC_BASE_UTC;
 153        } else if (!strcmp(value, "localtime")) {
 154            Error *blocker = NULL;
 155            rtc_base_type = RTC_BASE_LOCALTIME;
 156            error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED,
 157                      "-rtc base=localtime");
 158            replay_add_blocker(blocker);
 159        } else {
 160            rtc_base_type = RTC_BASE_DATETIME;
 161            configure_rtc_base_datetime(value);
 162        }
 163    }
 164    value = qemu_opt_get(opts, "clock");
 165    if (value) {
 166        if (!strcmp(value, "host")) {
 167            rtc_clock = QEMU_CLOCK_HOST;
 168        } else if (!strcmp(value, "rt")) {
 169            rtc_clock = QEMU_CLOCK_REALTIME;
 170        } else if (!strcmp(value, "vm")) {
 171            rtc_clock = QEMU_CLOCK_VIRTUAL;
 172        } else {
 173            error_report("invalid option value '%s'", value);
 174            exit(1);
 175        }
 176    }
 177    value = qemu_opt_get(opts, "driftfix");
 178    if (value) {
 179        if (!strcmp(value, "slew")) {
 180            object_register_sugar_prop("mc146818rtc",
 181                                       "lost_tick_policy",
 182                                       "slew",
 183                                       false);
 184        } else if (!strcmp(value, "none")) {
 185            /* discard is default */
 186        } else {
 187            error_report("invalid option value '%s'", value);
 188            exit(1);
 189        }
 190    }
 191}
 192