Annotation of nono/vm/goldfish_timer.cpp, revision 1.1

1.1     ! root        1: //
        !             2: // nono
        !             3: // Copyright (C) 2024 nono project
        !             4: // Licensed under nono-license.txt
        !             5: //
        !             6: 
        !             7: //
        !             8: // Goldfish (Qemu) RTC+Timer (の Timer のほう)
        !             9: //
        !            10: 
        !            11: // Google Goldfish RTC/Timer 仕様と QEMU 実装の仕様には互換性がない。
        !            12: // オリジナルもまあまあ首をかしげる仕様だったので、それを QEMU が跡形なく
        !            13: // 変更して使っているのかも知れない。知らんけど。
        !            14: // ここで実装しているのは QEMU 仕様のほう。
        !            15: //
        !            16: // デバイスには 2 種類あり、Timer と RTC という名前で区別する。
        !            17: // で、これはどこの仕様なのか QEMU の実装なのか分からないが、何故か
        !            18: // RTC デバイスのベースアドレスは Timer デバイスのベースアドレス + 0x1000 の
        !            19: // 位置らしい。
        !            20: // どうして別デバイスにして BootInfo で別々に渡すよう設計しなかったのか…。
        !            21: //
        !            22: // 元仕様書にエンディアンのエの字もないので、たぶん何の考察もしてないのだと
        !            23: // 思うが、ここではゲストエンディアン (ビッグエンディアン) デバイスに見せる。
        !            24: // NetBSD/virt68k もそのように動いているようなので。
        !            25: 
        !            26: // Timer デバイスのレジスタは次の通り。
        !            27: //
        !            28: //  +$00.L TIME_LOW            R-: ホスト時刻 [nsec] の下位 32 ビット
        !            29: //  +$04.L TIME_HIGH   R-: ホスト時刻 [nsec] の上位 32 ビット
        !            30: //  +$08.L ALARM_LOW   -W: アラーム時刻 [nsec] の下位 32 ビットとアラーム開始
        !            31: //  +$0c.L ALARM_HIGH  -W: アラーム時刻 [nsec] の上位 32 ビット
        !            32: //  +$10.L INTR_STATUS RW: 割り込み許可禁止の取得(R)/設定(W)
        !            33: //  +$14.L ALARM_CLEAR -W: 動作中のアラームクリア
        !            34: //  +$18.L ALARM_STATUS        R-: アラーム動作中なら $1、そうでなければ $0
        !            35: //  +$1c.L INTR_CLEAR  -W: 割り込みクリア
        !            36: //
        !            37: // TIME_LOW、TIME_HIGH はホスト時刻を [nsec] で返す。
        !            38: // TIME_LOW 読み込み時に 64 ビットをラッチするので必ず TIME_LOW、HIGH の順で
        !            39: // 読み出すこと。ここでいうホスト時刻は、ホストのある時点 (大抵は VM の開始
        !            40: // 時点) からの相対時刻。
        !            41: //
        !            42: // ALARM_LOW、ALARM_HIGH はアラーム時刻 [nsec] の設定。
        !            43: // 現時刻からの間隔ではなくこのホスト時間軸上の時刻。
        !            44: // 過去の時刻だったらどうなるとかの定義は一切ない…。
        !            45: // ALARM_LOW がアラーム開始を兼ねているので必ず HIGH、LOW の順で書き込むこと。
        !            46: //
        !            47: // INTR_STATUS は書き込み時は最下位ビットが %1 なら割り込み許可、%0 なら禁止。
        !            48: // 読み込み時は割り込み許可なら $1、禁止なら $0。
        !            49: 
        !            50: #include "goldfish_timer.h"
        !            51: #include "goldfish_pic.h"
        !            52: #include "goldfish_rtc.h"
        !            53: #include "scheduler.h"
        !            54: #include "syncer.h"
        !            55: 
        !            56: //
        !            57: // GFTimer
        !            58: //
        !            59: 
        !            60: // コンストラクタ
        !            61: GFTimerDevice::GFTimerDevice()
        !            62:        : inherited(OBJ_GFTIMER)
        !            63: {
        !            64:        // func は Init() で設定する
        !            65:        event.code = 0;
        !            66:        event.Regist("Goldfish Timer");
        !            67: 
        !            68:        // モニタは GFRTC + GFTimer で共有。
        !            69:        monitor.func = ToMonitorCallback(&GFTimerDevice::MonitorUpdate);
        !            70:        monitor.SetSize(38, 7);
        !            71:        monitor.Regist(ID_MONITOR_RTC);
        !            72: }
        !            73: 
        !            74: // デストラクタ
        !            75: GFTimerDevice::~GFTimerDevice()
        !            76: {
        !            77: }
        !            78: 
        !            79: // 初期化
        !            80: bool
        !            81: GFTimerDevice::Init()
        !            82: {
        !            83:        if (inherited::Init() == false) {
        !            84:                return false;
        !            85:        }
        !            86: 
        !            87:        // 割り込み信号線の接続先
        !            88:        auto gfpic = GetGFPICDevice(6);
        !            89:        gfpic->RegistIRQ(1, "GFTimer", this);
        !            90:        interrupt = gfpic;
        !            91: 
        !            92:        gfrtc = dynamic_cast<GFRTCDevice *>(GetRTCDevice());
        !            93:        syncer = GetSyncer();
        !            94: 
        !            95:        // クロックの同期方法
        !            96:        sync_rt = GetRTCDevice()->GetSyncRealtime();
        !            97:        // それによってイベントを分けてある。(time はどちらも都度セットする)
        !            98:        if (sync_rt) {
        !            99:                event.func = ToEventCallback(&GFTimerDevice::CallbackRT);
        !           100:        } else {
        !           101:                event.func = ToEventCallback(&GFTimerDevice::CallbackVT);
        !           102:        }
        !           103: 
        !           104:        return true;
        !           105: }
        !           106: 
        !           107: // 電源オン/リセット
        !           108: void
        !           109: GFTimerDevice::ResetHard(bool poweron)
        !           110: {
        !           111:        scheduler->StopEvent(event);
        !           112:        elapsed = 0;
        !           113:        alarm = 0;
        !           114:        mtime = 0;
        !           115:        mtime_epoch = 0;
        !           116: 
        !           117:        intr_enable = false;
        !           118:        intr_assert = false;
        !           119:        ChangeInterrupt();
        !           120: }
        !           121: 
        !           122: busdata
        !           123: GFTimerDevice::Read32(uint32 addr)
        !           124: {
        !           125:        uint32 offset = addr & 0xfff;
        !           126:        busdata data;
        !           127: 
        !           128:        switch (offset) {
        !           129:         case GFRTC::TIME_LOW:
        !           130:                elapsed = Now();
        !           131:                data = (uint32)elapsed;
        !           132:                if (__predict_false(loglevel >= 2)) {
        !           133:                        if (loglevel >= 3) {
        !           134:                                putlogn("TIME_LOW  -> $%08x", data.Data());
        !           135:                        } else {
        !           136:                                putlogn("TIME -> $%08x'%08x",
        !           137:                                        (uint32)(elapsed >> 32), data.Data());
        !           138:                        }
        !           139:                }
        !           140:                break;
        !           141: 
        !           142:         case GFRTC::TIME_HIGH:
        !           143:                data = (uint32)(elapsed >> 32);
        !           144:                putlog(3, "TIME_HIGH -> $%08x", data.Data());
        !           145:                break;
        !           146: 
        !           147:         case GFRTC::INTR_STATUS:
        !           148:                data = intr_enable ? 1 : 0;
        !           149:                putlog(2, "INTR_STATUS -> $%08x", data.Data());
        !           150:                break;
        !           151: 
        !           152:         case GFRTC::ALARM_STATUS:
        !           153:                data = event.IsRunning() ? 1 : 0;
        !           154:                putlog(2, "ALARM_STATUS -> $%08x", data.Data());
        !           155:                break;
        !           156: 
        !           157:         default:
        !           158:                putlog(1, "Read unknown $%08x", addr);
        !           159:                data.SetBusErr();
        !           160:                break;
        !           161:        }
        !           162:        return data;
        !           163: }
        !           164: 
        !           165: busdata
        !           166: GFTimerDevice::Write32(uint32 addr, uint32 data)
        !           167: {
        !           168:        uint32 offset = addr & 0xfff;
        !           169:        busdata r;
        !           170: 
        !           171:        switch (offset) {
        !           172:         case GFRTC::ALARM_LOW:
        !           173:                alarm = alarm_high | data;
        !           174:                alarm_high = 0;
        !           175:                if (__predict_false(loglevel >= 2)) {
        !           176:                        if (loglevel >= 3) {
        !           177:                                putlogn("ALARM_LOW  <- $%08x", data);
        !           178:                        } else {
        !           179:                                putlogn("ALARM <- $%08x'%08x",
        !           180:                                        (uint32)(alarm >> 32), (uint32)alarm);
        !           181:                        }
        !           182:                }
        !           183:                StartAlarm();
        !           184:                break;
        !           185: 
        !           186:         case GFRTC::ALARM_HIGH:
        !           187:                putlog(3, "ALARM_HIGH <- $%08x", data);
        !           188:                alarm_high = (uint64)data << 32;
        !           189:                break;
        !           190: 
        !           191:         case GFRTC::INTR_STATUS:
        !           192:                intr_enable = data & 1;
        !           193:                if (__predict_false(loglevel >= 1)) {
        !           194:                        if (intr_enable) {
        !           195:                                putlogn("Enable interrupt");
        !           196:                        } else {
        !           197:                                putlogn("Disable interrupt");
        !           198:                        }
        !           199:                }
        !           200:                ChangeInterrupt();
        !           201:                break;
        !           202: 
        !           203:         case GFRTC::ALARM_CLEAR:
        !           204:                putlog(1, "Clear alarm");
        !           205:                alarm = 0;
        !           206:                break;
        !           207: 
        !           208:         case GFRTC::INTR_CLEAR:
        !           209:                putlog(2, "Clear interrupt");
        !           210:                intr_assert = false;
        !           211:                ChangeInterrupt();
        !           212:                break;
        !           213: 
        !           214:         default:
        !           215:                putlog(1, "Write unknown $%08x <- $%08x", addr, data);
        !           216:                r = busdata::BusErr;
        !           217:                break;
        !           218:        }
        !           219: 
        !           220:        return r;
        !           221: }
        !           222: 
        !           223: busdata
        !           224: GFTimerDevice::Peek8(uint32 addr)
        !           225: {
        !           226:        uint32 offset = addr & 0xfff;
        !           227:        switch (offset) {
        !           228:         case GFRTC::TIME_LOW + 0:
        !           229:         case GFRTC::TIME_LOW + 1:
        !           230:         case GFRTC::TIME_LOW + 2:
        !           231:         case GFRTC::TIME_LOW + 3:
        !           232:         case GFRTC::TIME_HIGH + 0:
        !           233:         case GFRTC::TIME_HIGH + 1:
        !           234:         case GFRTC::TIME_HIGH + 2:
        !           235:         case GFRTC::TIME_HIGH + 3:
        !           236:         {
        !           237:                uint64 now = Now();
        !           238:                switch (offset) {
        !           239:                 case GFRTC::TIME_LOW + 0:
        !           240:                        return (now >> 24) & 0xff;
        !           241:                 case GFRTC::TIME_LOW + 1:
        !           242:                        return (now >> 16) & 0xff;
        !           243:                 case GFRTC::TIME_LOW + 2:
        !           244:                        return (now >>  8) & 0xff;
        !           245:                 case GFRTC::TIME_LOW + 3:
        !           246:                        return now & 0xff;
        !           247: 
        !           248:                 case GFRTC::TIME_HIGH + 0:
        !           249:                        return (now >> 56) & 0xff;
        !           250:                 case GFRTC::TIME_HIGH + 1:
        !           251:                        return (now >> 48) & 0xff;
        !           252:                 case GFRTC::TIME_HIGH + 2:
        !           253:                        return (now >> 40) & 0xff;
        !           254:                 case GFRTC::TIME_HIGH + 3:
        !           255:                        return (now >> 32) & 0xff;
        !           256:                 default:
        !           257:                        __unreachable();
        !           258:                }
        !           259:         }
        !           260: 
        !           261:         case GFRTC::INTR_STATUS + 0:
        !           262:         case GFRTC::INTR_STATUS + 1:
        !           263:         case GFRTC::INTR_STATUS + 2:
        !           264:                return 0;
        !           265:         case GFRTC::INTR_STATUS + 3:
        !           266:                return intr_assert ? 1 : 0;
        !           267: 
        !           268:         case GFRTC::ALARM_STATUS + 0:
        !           269:         case GFRTC::ALARM_STATUS + 1:
        !           270:         case GFRTC::ALARM_STATUS + 2:
        !           271:                return 0;
        !           272:         case GFRTC::ALARM_STATUS + 3:
        !           273:                return event.IsRunning() ? 1 : 0;
        !           274: 
        !           275:         default:
        !           276:                return busdata::BusErr;
        !           277:        }
        !           278: }
        !           279: 
        !           280: void
        !           281: GFTimerDevice::MonitorUpdate(Monitor *, TextScreen& screen)
        !           282: {
        !           283:        int y = 0;
        !           284: 
        !           285:        screen.Clear();
        !           286: 
        !           287:        uint64 now = Now();
        !           288: 
        !           289:        screen.Puts(0, y++, "<Timer>");
        !           290:        screen.Print(0, y++, "Time:  %26s", TimeToStr(now).c_str());
        !           291:        screen.Print(0, y++, "Alarm: %26s", TimeToStr(alarm).c_str());
        !           292:        y++;
        !           293: 
        !           294:        gfrtc->MonitorUpdateRTC(screen, y);
        !           295: }
        !           296: 
        !           297: // 現在時刻を返す
        !           298: uint64
        !           299: GFTimerDevice::Now() const
        !           300: {
        !           301:        uint64 now;
        !           302: 
        !           303:        if (sync_rt) {
        !           304:                now = syncer->GetRealTime();
        !           305:        } else {
        !           306:                now = scheduler->GetVirtTime();
        !           307:        }
        !           308:        return now;
        !           309: }
        !           310: 
        !           311: // アラーム開始。(ALARM_LOW の書き込みで呼ばれる)
        !           312: void
        !           313: GFTimerDevice::StartAlarm()
        !           314: {
        !           315:        if (__predict_false(mtime_epoch == 0)) {
        !           316:                // 最初のアラーム書き込み時を修正実時間軸の基準点にする。
        !           317:                mtime_epoch = syncer->GetRealTime();
        !           318:                mtime = mtime_epoch;
        !           319:        }
        !           320: 
        !           321:        // 仮想時間モードなら ALARM 時刻に発火するイベントをセットするだけ。
        !           322:        // 実時間モードならひとまず仮に本来の仮想時刻にイベントは起こすが、
        !           323:        // 割り込みを上げるかどうかは向こうで調整する。
        !           324:        // event.code は VT モードの時には不要だが気にせず立てる。
        !           325:        event.time = alarm - Now();
        !           326:        event.code = 1;
        !           327:        scheduler->RestartEvent(event);
        !           328: }
        !           329: 
        !           330: // 実時間モードでのコールバック。
        !           331: void
        !           332: GFTimerDevice::CallbackRT(Event& ev)
        !           333: {
        !           334:        // アラーム開始後最初の1回だけ自分の時間軸をアラーム分だけ進める。
        !           335:        if (event.code) {
        !           336:                mtime += ev.time;
        !           337:                event.code = 0;
        !           338:        }
        !           339: 
        !           340:        uint64 mnow = syncer->GetRealTime() - mtime_epoch;
        !           341:        if (mtime >= mnow) {
        !           342:                // システムの実時間軸を追い越していたら一回休んで
        !           343:                // もう一度このイベントに来る。
        !           344:                scheduler->RestartEvent(event);
        !           345:        } else {
        !           346:                // システムの実時間軸に追い付いてなければ(通常動作)、アラーム発砲。
        !           347:                intr_assert = true;
        !           348:                ChangeInterrupt();
        !           349:        }
        !           350: }
        !           351: 
        !           352: // 仮想時間モードでのコールバック。
        !           353: void
        !           354: GFTimerDevice::CallbackVT(Event& ev)
        !           355: {
        !           356:        // 仮想時間モードなら、イベントが起きたここがゴール。
        !           357:        // アラームは単発イベントなのでリスタート不要。
        !           358:        intr_assert = true;
        !           359:        ChangeInterrupt();
        !           360: }
        !           361: 
        !           362: void
        !           363: GFTimerDevice::ChangeInterrupt()
        !           364: {
        !           365:        bool irq = (intr_enable && intr_assert);
        !           366:        interrupt->ChangeINT(this, irq);
        !           367: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.