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

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"
1.1.1.6   root       51: #include "event.h"
1.1       root       52: #include "goldfish_pic.h"
                     53: #include "goldfish_rtc.h"
1.1.1.7 ! root       54: #include "mainbus.h"
1.1.1.3   root       55: #include "monitor.h"
1.1       root       56: #include "scheduler.h"
                     57: #include "syncer.h"
                     58: 
                     59: //
                     60: // GFTimer
                     61: //
                     62: 
                     63: // コンストラクタ
                     64: GFTimerDevice::GFTimerDevice()
                     65:        : inherited(OBJ_GFTIMER)
                     66: {
                     67:        // モニタは GFRTC + GFTimer で共有。
1.1.1.3   root       68:        monitor = gMonitorManager->Regist(ID_MONITOR_RTC, this);
1.1.1.7 ! root       69:        monitor->SetCallback(&GFTimerDevice::MonitorScreen);
1.1.1.3   root       70:        monitor->SetSize(38, 7);
1.1       root       71: }
                     72: 
                     73: // デストラクタ
                     74: GFTimerDevice::~GFTimerDevice()
                     75: {
                     76: }
                     77: 
                     78: // 初期化
                     79: bool
                     80: GFTimerDevice::Init()
                     81: {
                     82:        // 割り込み信号線の接続先
                     83:        auto gfpic = GetGFPICDevice(6);
                     84:        gfpic->RegistIRQ(1, "GFTimer", this);
                     85:        interrupt = gfpic;
                     86: 
                     87:        gfrtc = dynamic_cast<GFRTCDevice *>(GetRTCDevice());
                     88:        syncer = GetSyncer();
                     89: 
1.1.1.6   root       90:        auto evman = GetEventManager();
                     91:        event = evman->Regist(this, NULL, "Goldfish Timer");
                     92: 
1.1       root       93:        // クロックの同期方法
1.1.1.5   root       94:        sync_rt = syncer->GetSyncRealtime();
1.1.1.2   root       95: 
1.1       root       96:        // それによってイベントを分けてある。(time はどちらも都度セットする)
                     97:        if (sync_rt) {
1.1.1.7 ! root       98:                event->SetCallback(&GFTimerDevice::CallbackRT);
1.1       root       99:        } else {
1.1.1.7 ! root      100:                event->SetCallback(&GFTimerDevice::CallbackVT);
1.1       root      101:        }
                    102: 
                    103:        return true;
                    104: }
                    105: 
                    106: // 電源オン/リセット
                    107: void
                    108: GFTimerDevice::ResetHard(bool poweron)
                    109: {
                    110:        scheduler->StopEvent(event);
1.1.1.7 ! root      111:        elapsed.q = 0;
        !           112:        alarm.q = 0;
1.1       root      113:        mtime = 0;
                    114:        mtime_epoch = 0;
                    115: 
                    116:        intr_enable = false;
                    117:        intr_assert = false;
                    118:        ChangeInterrupt();
                    119: }
                    120: 
                    121: busdata
1.1.1.2   root      122: GFTimerDevice::ReadPort(uint32 offset)
1.1       root      123: {
                    124:        busdata data;
                    125: 
                    126:        switch (offset) {
                    127:         case GFRTC::TIME_LOW:
1.1.1.7 ! root      128:                elapsed.q = Now();
        !           129:                data = elapsed.l;
1.1       root      130:                if (__predict_false(loglevel >= 2)) {
                    131:                        if (loglevel >= 3) {
                    132:                                putlogn("TIME_LOW  -> $%08x", data.Data());
                    133:                        } else {
1.1.1.7 ! root      134:                                putlogn("TIME -> $%08x'%08x", elapsed.h, data.Data());
1.1       root      135:                        }
                    136:                }
                    137:                break;
                    138: 
                    139:         case GFRTC::TIME_HIGH:
1.1.1.7 ! root      140:                data = elapsed.h;
1.1       root      141:                putlog(3, "TIME_HIGH -> $%08x", data.Data());
                    142:                break;
                    143: 
                    144:         case GFRTC::INTR_STATUS:
                    145:                data = intr_enable ? 1 : 0;
                    146:                putlog(2, "INTR_STATUS -> $%08x", data.Data());
                    147:                break;
                    148: 
                    149:         case GFRTC::ALARM_STATUS:
1.1.1.6   root      150:                data = event->IsRunning() ? 1 : 0;
1.1       root      151:                putlog(2, "ALARM_STATUS -> $%08x", data.Data());
                    152:                break;
                    153: 
                    154:         default:
1.1.1.7 ! root      155:                putlog(1, "Read unknown $%08x", GetMainbusDevice()->GetPaddr());
1.1       root      156:                data.SetBusErr();
                    157:                break;
                    158:        }
1.1.1.2   root      159: 
                    160:        data |= BusData::Size4;
1.1       root      161:        return data;
                    162: }
                    163: 
                    164: busdata
1.1.1.2   root      165: GFTimerDevice::WritePort(uint32 offset, uint32 data)
1.1       root      166: {
                    167:        busdata r;
                    168: 
                    169:        switch (offset) {
                    170:         case GFRTC::ALARM_LOW:
1.1.1.7 ! root      171:                alarm.h = alarm_high;
        !           172:                alarm.l = data;
1.1       root      173:                alarm_high = 0;
                    174:                if (__predict_false(loglevel >= 2)) {
                    175:                        if (loglevel >= 3) {
                    176:                                putlogn("ALARM_LOW  <- $%08x", data);
                    177:                        } else {
1.1.1.7 ! root      178:                                putlogn("ALARM <- $%08x'%08x", alarm.h, alarm.l);
1.1       root      179:                        }
                    180:                }
                    181:                StartAlarm();
                    182:                break;
                    183: 
                    184:         case GFRTC::ALARM_HIGH:
                    185:                putlog(3, "ALARM_HIGH <- $%08x", data);
1.1.1.7 ! root      186:                alarm_high = data;
1.1       root      187:                break;
                    188: 
                    189:         case GFRTC::INTR_STATUS:
                    190:                intr_enable = data & 1;
                    191:                if (__predict_false(loglevel >= 1)) {
                    192:                        if (intr_enable) {
                    193:                                putlogn("Enable interrupt");
                    194:                        } else {
                    195:                                putlogn("Disable interrupt");
                    196:                        }
                    197:                }
                    198:                ChangeInterrupt();
                    199:                break;
                    200: 
                    201:         case GFRTC::ALARM_CLEAR:
                    202:                putlog(1, "Clear alarm");
1.1.1.7 ! root      203:                alarm.q = 0;
1.1       root      204:                break;
                    205: 
                    206:         case GFRTC::INTR_CLEAR:
                    207:                putlog(2, "Clear interrupt");
                    208:                intr_assert = false;
                    209:                ChangeInterrupt();
                    210:                break;
                    211: 
                    212:         default:
1.1.1.7 ! root      213:                putlog(1, "Write unknown $%08x <- $%08x",
        !           214:                        GetMainbusDevice()->GetPaddr(), data);
1.1.1.2   root      215:                r = BusData::BusErr;
1.1       root      216:                break;
                    217:        }
                    218: 
1.1.1.2   root      219:        r |= BusData::Size4;
1.1       root      220:        return r;
                    221: }
                    222: 
                    223: busdata
1.1.1.2   root      224: GFTimerDevice::PeekPort(uint32 offset)
1.1       root      225: {
                    226:        switch (offset) {
1.1.1.2   root      227:         case GFRTC::TIME_LOW:
                    228:                return Now() & 0xffffffffU;
                    229:         case GFRTC::TIME_HIGH:
                    230:                return Now() >> 32;
                    231:         case GFRTC::INTR_STATUS:
1.1       root      232:                return intr_assert ? 1 : 0;
1.1.1.2   root      233:         case GFRTC::ALARM_STATUS:
1.1.1.6   root      234:                return event->IsRunning() ? 1 : 0;
1.1       root      235:         default:
1.1.1.2   root      236:                return BusData::BusErr;
1.1       root      237:        }
                    238: }
                    239: 
                    240: void
1.1.1.7 ! root      241: GFTimerDevice::MonitorScreen(Monitor *, TextScreen& screen)
1.1       root      242: {
                    243:        int y = 0;
                    244: 
                    245:        screen.Clear();
                    246: 
                    247:        uint64 now = Now();
                    248: 
                    249:        screen.Puts(0, y++, "<Timer>");
                    250:        screen.Print(0, y++, "Time:  %26s", TimeToStr(now).c_str());
1.1.1.7 ! root      251:        screen.Print(0, y++, "Alarm: %26s", TimeToStr(alarm.q).c_str());
1.1       root      252:        y++;
                    253: 
1.1.1.7 ! root      254:        gfrtc->MonitorScreenRTC(screen, y);
1.1       root      255: }
                    256: 
                    257: // 現在時刻を返す
                    258: uint64
                    259: GFTimerDevice::Now() const
                    260: {
                    261:        uint64 now;
                    262: 
                    263:        if (sync_rt) {
                    264:                now = syncer->GetRealTime();
                    265:        } else {
                    266:                now = scheduler->GetVirtTime();
                    267:        }
                    268:        return now;
                    269: }
                    270: 
                    271: // アラーム開始。(ALARM_LOW の書き込みで呼ばれる)
                    272: void
                    273: GFTimerDevice::StartAlarm()
                    274: {
1.1.1.3   root      275:        uint64 now = Now();
                    276: 
1.1       root      277:        if (__predict_false(mtime_epoch == 0)) {
                    278:                // 最初のアラーム書き込み時を修正実時間軸の基準点にする。
1.1.1.3   root      279:                mtime_epoch = now;
                    280:                mtime = 0;
1.1       root      281:        }
                    282: 
                    283:        // 仮想時間モードなら ALARM 時刻に発火するイベントをセットするだけ。
                    284:        // 実時間モードならひとまず仮に本来の仮想時刻にイベントは起こすが、
                    285:        // 割り込みを上げるかどうかは向こうで調整する。
1.1.1.6   root      286:        // event->code は VT モードの時には不要だが気にせず立てる。
1.1.1.2   root      287:        // 指定されたアラームがすでに過去の時刻なら QEMU は即発火するようだ。
1.1.1.7 ! root      288:        if (__predict_true(alarm.q > now)) {
        !           289:                event->time = alarm.q - now;
1.1.1.2   root      290:        } else {
                    291:                if (__predict_false(loglevel >= 1)) {
1.1.1.7 ! root      292:                        uint64 delta = now - alarm.q;
1.1.1.2   root      293:                        putlogn("ALARM: past alarm -%s", TimeToStr(delta).c_str());
                    294:                }
1.1.1.6   root      295:                event->time = 0;
1.1.1.2   root      296:        }
1.1.1.6   root      297:        event->code = 1;
1.1       root      298:        scheduler->RestartEvent(event);
                    299: }
                    300: 
                    301: // 実時間モードでのコールバック。
                    302: void
1.1.1.6   root      303: GFTimerDevice::CallbackRT(Event *ev)
1.1       root      304: {
                    305:        // アラーム開始後最初の1回だけ自分の時間軸をアラーム分だけ進める。
1.1.1.6   root      306:        if (event->code) {
                    307:                mtime += event->time;
                    308:                event->code = 0;
1.1       root      309:        }
                    310: 
                    311:        uint64 mnow = syncer->GetRealTime() - mtime_epoch;
                    312:        if (mtime >= mnow) {
                    313:                // システムの実時間軸を追い越していたら一回休んで
                    314:                // もう一度このイベントに来る。
                    315:                scheduler->RestartEvent(event);
                    316:        } else {
1.1.1.2   root      317:                // システムの実時間軸に追い付いてなければ(通常動作)、アラーム発報。
1.1       root      318:                intr_assert = true;
                    319:                ChangeInterrupt();
                    320:        }
                    321: }
                    322: 
                    323: // 仮想時間モードでのコールバック。
                    324: void
1.1.1.6   root      325: GFTimerDevice::CallbackVT(Event *ev)
1.1       root      326: {
                    327:        // 仮想時間モードなら、イベントが起きたここがゴール。
                    328:        // アラームは単発イベントなのでリスタート不要。
                    329:        intr_assert = true;
                    330:        ChangeInterrupt();
                    331: }
                    332: 
                    333: void
                    334: GFTimerDevice::ChangeInterrupt()
                    335: {
                    336:        bool irq = (intr_enable && intr_assert);
                    337:        interrupt->ChangeINT(this, irq);
                    338: }

unix.superglobalmegacorp.com

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