|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.