--- nono/vm/rtc.cpp 2026/04/29 17:05:06 1.1.1.7 +++ nono/vm/rtc.cpp 2026/04/29 17:06:01 1.1.1.14 @@ -4,60 +4,54 @@ // Licensed under nono-license.txt // +// +// RTC の共通部分 +// + #include "rtc.h" #include "config.h" -#include - -std::unique_ptr gRTC; - -RTCDevice::RTCDevice(const std::string& objname_) - : inherited(objname_) +#include "event.h" +#include "mytime.h" +#include "scheduler.h" +#include "syncer.h" + +// コンストラクタ +RTCDevice::RTCDevice() + : inherited(OBJ_RTC) { } +// デストラクタ RTCDevice::~RTCDevice() { } +// 初期化 bool RTCDevice::Init() { - struct timeval tv; - struct tm tm; + syncer = GetSyncer(); - // TODO: year_epoch, use_localtime を設定可能にするべきかどうか + // TODO: use_localtime を設定可能にするべきかどうか - // 時刻固定オプション (パフォーマンス測定用) + // 時刻固定オプション (パフォーマンス測定用)。 // パフォーマンス測定をする際に、RTC が実時間を返すと // ゲストの状態が毎回異なることになるため、 // いつ何時も同じ値を返すことで、世界から隔絶された // VM を作り、パフォーマンス測定を安定させる。 - force_fixed = gConfig->Find(".rtc-force-fixed").AsInt(); - if (force_fixed) { - putmsgn("force_fixed"); - // 固定の時刻を設定 - struct tm fixed { - .tm_sec = 16, - .tm_min = 22, - .tm_hour = 13 - 9, - .tm_mday = 27, - .tm_mon = 7 - 1, - .tm_year = 2018 - 1900, - }; - tv.tv_sec = timegm(&fixed); - tv.tv_usec = 0; - } else { - // 起動した時の現在時刻で設定する - gettimeofday(&tv, NULL); - } + force_fixed = gConfig->Find(".rtc-force-fixed").AsBool(); - if (use_localtime) { - localtime_r(&tv.tv_sec, &tm); - } else { - gmtime_r(&tv.tv_sec, &tm); - } + sync_rt = syncer->GetSyncRealtime(); - // 日付、時刻の直接指定があればここで差し替える。 + rtc_tm.tm_year = -1; + rtc_tm.tm_mon = -1; + rtc_tm.tm_mday = -1; + rtc_tm.tm_wday = -1; + rtc_tm.tm_hour = -1; + rtc_tm.tm_min = -1; + rtc_tm.tm_sec = -1; + + // 日付、時刻の直接指定 const ConfigItem item_date = gConfig->Find(".rtc-date"); std::string str_date = item_date.AsString(); if (!str_date.empty()) { @@ -66,13 +60,14 @@ RTCDevice::Init() item_date.Err(); return false; } - tm.tm_year = yy - 1900; - tm.tm_mon = mm - 1; - tm.tm_mday = dd; + rtc_tm.tm_year = yy - 1900; + rtc_tm.tm_mon = mm - 1; + rtc_tm.tm_mday = dd; if (w != -1) { - tm.tm_wday = w; + rtc_tm.tm_wday = w; } } + const ConfigItem item_time = gConfig->Find(".rtc-time"); std::string str_time = item_time.AsString(); if (!str_time.empty()) { @@ -81,13 +76,70 @@ RTCDevice::Init() item_time.Err(); return false; } - tm.tm_hour = hh; - tm.tm_min = mm; - tm.tm_sec = ss; + rtc_tm.tm_hour = hh; + rtc_tm.tm_min = mm; + rtc_tm.tm_sec = ss; + } + + auto evman = GetEventManager(); + event = evman->Regist(this, + ToEventCallback(&RTCDevice::Callback), + "RTC ClockIn"); + event->time = period; + + return true; +} + +// 時間の始まり +void +RTCDevice::StartTime() +{ + time_t now_sec; + uint64 now_usec; + struct tm tm; + + if (force_fixed) { + // 固定の時刻を設定 + struct tm fixed { + .tm_sec = 16, + .tm_min = 22, + .tm_hour = 13 - 9, + .tm_mday = 27, + .tm_mon = 7 - 1, + .tm_year = 2018 - 1900, + }; + now_sec = timegm(&fixed); + now_usec = 0; + } else { + // 起動した時の現在時刻で設定する + auto usec = get_hosttime_usec(); + now_sec = usec / 1000'000U; + now_usec = usec % 1000'000U; + } + + if (use_localtime) { + localtime_r(&now_sec, &tm); + } else { + gmtime_r(&now_sec, &tm); + } + + // 日付、時刻の直接指定があればここで差し替える。 + if (rtc_tm.tm_year != -1) { + tm.tm_year = rtc_tm.tm_year; + tm.tm_mon = rtc_tm.tm_mon; + tm.tm_mday = rtc_tm.tm_mday; + if (rtc_tm.tm_wday != -1) { + tm.tm_wday = rtc_tm.tm_wday; + } + } + if (rtc_tm.tm_hour != -1) { + tm.tm_hour = rtc_tm.tm_hour; + tm.tm_min = rtc_tm.tm_min; + tm.tm_sec = rtc_tm.tm_sec; } // 秒未満の初期値もそれなりの精度にしておく (1000*1000 / 32 = 31250) - cnt = tv.tv_usec / 31250; + cnt = now_usec / 31250; SetSec(tm.tm_sec); SetMin(tm.tm_min); SetHour(tm.tm_hour); @@ -99,7 +151,9 @@ RTCDevice::Init() SetYear(tm.tm_year + 1900); SetLeap(tm.tm_year & 3); - return true; + stime = syncer->GetRealTime(); + + scheduler->StartEvent(event); } // デバッグオプションの日付文字列を解析する。 @@ -179,8 +233,46 @@ RTCDevice::ParseTime(const std::string& return true; } +// イベントハンドラ +void +RTCDevice::Callback(Event *ev) +{ + stime += period; + ClockIn(); + if (sync_rt) { + scheduler->StartRealtimeEvent(ev, stime, period); + } else { + scheduler->StartEvent(ev); + } +} + +uint64 +RTCDevice::ClockIn_PowerOff() +{ + stime += period; + + ClockIn(); + + // XXX RealTime ではなく HostTime? + uint64 rt = syncer->GetRealTime(); + if (rt > stime) { + // RTC が現実より遅れているので早回し + return 0; + } else { + // clock-sync=virtual のときには stime が現実よりも多く進んで + // いるため、実時間との差をスケジューラが usleep で待つところで + // 長時間スリープしてしまう。 + // たちまち、period * 2 で制限しておく。 + uint64 dt = stime - rt; + if (dt > period * 2) { + dt = period * 2; + } + return dt; + } +} + // 32Hz のクロック入力。 -// スケジューラから呼び出される。 +// イベントから呼び出される。 // 本来の RTC は 32768Hz が入力クロックだが、 // それを 1024 分周したものを基準クロック入力として考える。 void @@ -200,17 +292,30 @@ RTCDevice::ClockIn() } } +// 32Hz で呼び出される関数。 +void +RTCDevice::Tick32Hz() +{ +} + +// 2Hz で呼び出される関数。 +void +RTCDevice::Tick2Hz() +{ +} + +// 1Hz で呼び出される関数。 +void +RTCDevice::Tick1Hz() +{ +} + // 秒をカウントアップし、繰り上がりを処理。 void RTCDevice::CountUpSec() { uint v; - if (__predict_false(force_fixed)) { - // 時刻固定モードならカウントアップしない - return; - } - // BCD の桁ごとにキャリー処理をしてるはずだけど妥協。 // たぶん内部キャリーはビット途中のキャリーで作られていると // 思うんだけど、ソフトでやるのは大変なので妥協。 @@ -266,11 +371,11 @@ RTCDevice::CountUpMin() } // 現在の月の最終日を返す (月の繰り上がり処理用) -int +uint RTCDevice::GetLastMday() const { // 下記の理由により 0 月が必要。値は適当。 - static const int mdays[] = { + static const uint mdays[] = { 30, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; @@ -279,8 +384,8 @@ RTCDevice::GetLastMday() const // 範囲外の値に対するロジックは違うだろうが規定されていないので // こっち都合で実装しておく。 // m は 0..12 - int m = GetMon() % 13; - int lastday = mdays[m]; + uint m = GetMon() % 13; + uint lastday = mdays[m]; if (m == 2 && GetLeap() == 0) { lastday++; }