|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2024 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // Goldfish TTY
9: //
10:
11: // レジスタ仕様は次の通り。
12: // +$00.L PUT_CHAR -W: 1バイト出力
13: // +$04.L BYTES_READY R-: 受信キューにあるバイト数を取得
14: // +$08.L COMMAND -W: コマンド
15: // +$0c.L -
16: // +$10.L DATA_PTR -W: DMA 開始アドレス
17: // +$14.L DATA_LEN -W: DMA データ長
18: // +$18.L DATA_PTR_HIGH W: DMA 開始アドレスの上位 32 ビット
19: // +$1c.L -
20: // +$20.L VERSION R-: このデバイスのバージョン ($1)
21: //
22: // コマンドは
23: // $00 INT_DISABLE
24: // $01 INT_ENABLE
25: // $02 WRITE_BUFFER
26: // $03 READ_BUFFER
27: //
28: // 受信キューにデータがあれば割り込みを上げる。
29: // 受信キューが空になると割り込みを下げる。
30: //
31: // WRITE_BUFFER, READ_BUFFER はたぶんバスマスタ的転送。
32: // 読み出し時 (READ_BUFFER) は、DATA_PTR で指定したアドレスにたぶん最大
33: // DATA_LEN バイトを GFTTY デバイスが書き込む。
34: // 書き込み時 (WRITE_BUFFER) は、DATA_PTR で指定したアドレスから DATA_LEN
35: // バイトを GFTTY デバイスが読み込む。
36: //
37: // nono では DATA_PTR_HIGH は実装しない。
38:
39: #include "goldfish_tty.h"
40: #include "goldfish_pic.h"
41: #include "hostcom.h"
42: #include "mainbus.h"
1.1.1.2 root 43: #include "mpu.h"
1.1 root 44: #include "scheduler.h"
1.1.1.4 ! root 45: #include "syncer.h"
1.1 root 46:
47: // コンストラクタ
48: GFTTYDevice::GFTTYDevice()
49: : inherited(OBJ_GFTTY)
50: {
51: }
52:
53: // デストラクタ
54: GFTTYDevice::~GFTTYDevice()
55: {
56: if ((bool)hostcom) {
57: hostcom->SetRxCallback(NULL);
58: }
59: }
60:
61: bool
62: GFTTYDevice::Create()
63: {
64: // 対応するホストドライバを作成
1.1.1.4 ! root 65: try {
! 66: hostcom.reset(new HostCOMDevice(this, "HostCOM"));
! 67: } catch (...) { }
1.1 root 68: if ((bool)hostcom == false) {
1.1.1.4 ! root 69: warnx("Failed to initialize HostCOMDevice at %s", __method__);
1.1 root 70: return false;
71: }
72: hostcom->SetRxCallback(ToDeviceCallback(&GFTTYDevice::HostRxCallback));
73:
74: return true;
75: }
76:
77: void
78: GFTTYDevice::SetLogLevel(int loglevel_)
79: {
80: inherited::SetLogLevel(loglevel_);
81:
1.1.1.4 ! root 82: if ((bool)hostcom) {
! 83: hostcom->SetLogLevel(loglevel_);
! 84: }
1.1 root 85: }
86:
87: // 初期化
88: bool
89: GFTTYDevice::Init()
90: {
91: mainbus = GetMainbusDevice();
92:
93: gfpic = GetGFPICDevice(1);
94: gfpic->RegistIRQ(32, "GFTTY", this);
95:
96: scheduler->ConnectMessage(MessageID::HOSTCOM_RX, this,
97: ToMessageCallback(&GFTTYDevice::RxMessage));
98: return true;
99: }
100:
101: // 電源オン/リセット
102: void
103: GFTTYDevice::ResetHard(bool poweron)
104: {
1.1.1.4 ! root 105: sesame_idx = 0;
1.1 root 106: rxq.clear();
107: intr_enable = false;
108: ChangeInterrupt();
109: }
110:
111: busdata
1.1.1.2 root 112: GFTTYDevice::ReadPort(uint32 offset)
1.1 root 113: {
114: busdata data;
115:
116: switch (offset) {
117: case BYTES_READY:
1.1.1.2 root 118: data = GetREADY();
119: putlog(2, "BYTES_READY -> $%08x", data.Data());
1.1 root 120: break;
121:
122: case VERSION:
1.1.1.2 root 123: data = GFTTYVersion;
1.1 root 124: putlog(2, "VERSION -> $%08x", data.Data());
125: break;
126:
127: default:
1.1.1.2 root 128: putlog(1, "Read $%08x.L", mpu->GetPaddr());
1.1 root 129: data.SetBusErr();
130: break;
131: }
132:
1.1.1.2 root 133: data |= BusData::Size4;
1.1 root 134: return data;
135: }
136:
137: busdata
1.1.1.2 root 138: GFTTYDevice::WritePort(uint32 offset, uint32 data)
1.1 root 139: {
140: busdata r;
141:
142: switch (offset) {
143: case PUT_CHAR:
144: putlog(2, "PUT_CHAR <- $%02x", data);
1.1.1.4 ! root 145: Tx(data);
1.1 root 146: break;
147:
148: case COMMAND:
149: Command(data);
150: break;
151:
152: case DATA_PTR:
153: putlog(2, "DATA_PTR <- $%08x", data);
154: dma_ptr = data;
155: break;
156:
157: case DATA_LEN:
158: putlog(2, "DATA_LEN <- $%08x", data);
159: dma_len = data;
160: break;
161:
162: default:
1.1.1.2 root 163: putlog(1, "Write $%08x.L", mpu->GetPaddr());
1.1 root 164: r.SetBusErr();
165: break;
166: }
1.1.1.2 root 167:
168: r |= BusData::Size4;
1.1 root 169: return r;
170: }
171:
172: busdata
1.1.1.2 root 173: GFTTYDevice::PeekPort(uint32 offset)
174: {
175: switch (offset) {
176: case BYTES_READY:
177: return GetREADY();
178: case VERSION:
179: return GFTTYVersion;
180: default:
181: return BusData::BusErr;
182: }
183: }
184:
185: // 着信しているバイト数を返す。
186: uint32
187: GFTTYDevice::GetREADY() const
1.1 root 188: {
1.1.1.2 root 189: return rxq.size();
1.1 root 190: }
191:
192: void
193: GFTTYDevice::Command(uint32 cmd)
194: {
195: switch (cmd) {
196: case CMD_INT_DISABLE:
197: putlog(1, "CMD_INT_DISABLE");
198: intr_enable = false;
199: ChangeInterrupt();
200: break;
201:
202: case CMD_INT_ENABLE:
203: putlog(1, "CMD_INT_ENABLE");
204: intr_enable = true;
205: ChangeInterrupt();
206: break;
207:
208: case CMD_WRITE_BUFFER:
209: {
210: // ゲストからの書き込み
211: putlog(1, "CMD_WRITE_BUFFER($%08x, $%08x)", dma_ptr, dma_len);
212: uint32 addr = dma_ptr;
213: uint32 end = addr + dma_len;
214: for (; addr < end; addr++) {
1.1.1.2 root 215: busdata data = mainbus->HVRead1(addr);
1.1 root 216: if (data.IsBusErr()) {
217: putlog(1, "WRITE_BUFFER: BusError at $%08x", addr);
218: break;
219: }
1.1.1.4 ! root 220: Tx(data);
1.1 root 221: }
222: break;
223: }
224:
225: case CMD_READ_BUFFER:
226: {
227: // ゲストからの読み込み (ゲスト空間へ書き出し)
228: putlog(1, "CMD_READ_BUFFER($%08x, $%08x)", dma_ptr, dma_len);
229: uint32 addr = dma_ptr;
230: uint32 len = std::min(dma_len, (uint32)rxq.size());
231: for (uint32 end = addr + len; addr < end; addr++) {
232: uint32 data = rxq.front();
233: rxq.pop_front();
1.1.1.2 root 234: busdata bd = mainbus->HVWrite1(addr, data);
1.1 root 235: if (bd.IsBusErr()) {
236: putlog(1, "READ_BUFFER: BusError at $%08x", addr);
237: break;
238: }
239: }
240: ChangeInterrupt();
241: break;
242: }
243:
244: default:
245: putlog(1, "Unknown command $%08x", cmd);
246: break;
247: }
248: }
249:
1.1.1.4 ! root 250: // 1バイト送信。
! 251: void
! 252: GFTTYDevice::Tx(uint32 data)
! 253: {
! 254: // パフォーマンス測定用のギミック。
! 255: // GFTTY に特定の文字列を書き込むと、
! 256: // VM 電源オン時から現在までの実時間をログに出力する。NetBSD の
! 257: // マルチユーザブートがあらかた終わってログインプロンプトが出る手前の
! 258: // /etc/rc.local に
! 259: //
! 260: // printf '\e[?68008h\e[?68008l'
! 261: //
! 262: // と書いておくことで、ここまでにかかる時間を表示できる。
! 263: // この値はホスト性能に依存するため、同一環境下での相対比較のみ可能。
! 264: //
! 265: // このエスケープシーケンスは DEC/xterm 拡張の設定と解除を続けて行う
! 266: // もので、68008 は空いてそうな適当な値。知らない番号の拡張機能については
! 267: // おそらく黙って何もしないだけだと思うので、どの端末エミュレータを通過
! 268: // しても副作用は少ないと思われる。
! 269: static const char sesame[] = "\x1b[?68008h\x1b[?68008l";
! 270: if (__predict_false(data == sesame[sesame_idx])) {
! 271: sesame_idx++;
! 272: if (__predict_false(sesame_idx >= strlen(sesame))) {
! 273: GetSyncer()->PutlogRealtime();
! 274: sesame_idx = 0;
! 275: }
! 276: } else {
! 277: sesame_idx = 0;
! 278: }
! 279:
! 280: // 1バイト送信。
! 281: hostcom->Tx(data);
! 282: }
! 283:
1.1 root 284: void
285: GFTTYDevice::ChangeInterrupt()
286: {
287: bool irq = (intr_enable && rxq.empty() == false);
288: gfpic->ChangeINT(this, irq);
289: }
290:
291: // ホストスレッドからの受信通知
292: // (HostCOM スレッドで呼ばれる)
293: void
294: GFTTYDevice::HostRxCallback()
295: {
296: scheduler->SendMessage(MessageID::HOSTCOM_RX);
297: }
298:
299: // ホストシリアルからの1バイト(以上)受信メッセージ
300: // (VM スレッドで呼ばれる)
301: void
302: GFTTYDevice::RxMessage(MessageID msgid, uint32 arg)
303: {
304: bool recv = false;
305:
306: for (;;) {
307: uint32 data = hostcom->Rx();
308: if ((int32)data < 0) {
309: break;
310: }
311: rxq.push_back(data);
312: recv = true;
313: }
314:
315: if (recv) {
316: ChangeInterrupt();
317: }
318: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.