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