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