Annotation of nono/vm/goldfish_timer.cpp, revision 1.1.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.