|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
1.1.1.10 root 7: //
1.1.1.12 root 8: // PROM (LUNA-I, LUNA-88K 共通)
1.1.1.8 root 9: //
1.1.1.10 root 10:
1.1.1.8 root 11: // IODevice
1.1.1.15 root 12: // |
13: // +- ROMDevice (LoadROM()、ウェイト、マスク等を持つ)
14: // | |
15: // | | +------------+
16: // | +-| PROMDevice | (LUNA* の PROM)
17: // | | +------------+
18: // | |
19: // | +- IPLROM1Device (X680x0 の IPLROM 後半)
20: // | +- IPLROM2Device (X680x0 の IPLROM 前半)
21: // | +- CGROMDevice (X680x0 の CGROM)
22: // | |
23: // | +- ROMEmuDevice
24: // | +- LunaPROMEmuDevice (LUNA PROM エミュレーションの共通部分)
25: // | | +- Luna1PROMEmuDevice (LUNA-I の PROM エミュレーション)
26: // | | +- Luna88kPROMEmuDevice (LUNA-88K の PROM エミュレーション)
27: // | +- NewsROMEmuDevice (NEWS の ROM エミュレーション)
28: // | +- ROM30EmuDevice (X68030 の ROM30 エミュレーション)
29: // | +- Virt68kROMEmuDevice (virt-m68k の IPLROM 相当の何か)
30: // |
31: // +- PROM0Device (LUNA* のブートページ切り替え用プロキシ)
32: // +- IPLROM0Device (X680x0 のブートページ切り替え用プロキシ)
1.1 root 33:
34: #include "prom.h"
35: #include "config.h"
1.1.1.8 root 36: #include "ethernet.h"
1.1 root 37: #include "mainapp.h"
1.1.1.14 root 38: #include "mainbus.h"
39: #include "mpu.h"
1.1 root 40:
1.1.1.8 root 41: //
42: // PROM0
43: //
44: // PROM0 は PROM のブートページ担当。
45: // これ自身はメモリ領域を持たず PROM へのプロキシとして動作する。
46: //
47: // リセット後最初の ROM 領域への書き込みアクセスでブートページを RAM に
48: // 切り替える。
49: // LUNA のブートページのアドレス範囲は不明なので、とりあえず devtable の
50: // 1区画である 16MB ($0000'0000..$00ff'ffff) とする。実害はない。
51:
52: // コンストラクタ
53: PROM0Device::PROM0Device()
1.1.1.12 root 54: : inherited(OBJ_PROM_BOOT)
1.1.1.8 root 55: {
1.1.1.12 root 56: // 通常ここからログを出すことはない
1.1.1.8 root 57: ClearAlias();
58: }
59:
60: // デストラクタ
61: PROM0Device::~PROM0Device()
62: {
63: }
64:
1.1.1.12 root 65: // 初期化
1.1.1.8 root 66: bool
67: PROM0Device::Init()
68: {
1.1.1.12 root 69: prom = GetPROMDevice();
70:
1.1.1.8 root 71: return true;
72: }
73:
1.1.1.10 root 74: // リセット
1.1.1.8 root 75: void
1.1.1.10 root 76: PROM0Device::ResetHard(bool poweron)
1.1.1.8 root 77: {
1.1.1.14 root 78: auto mainbus = GetMainbusDevice();
79: mainbus->SwitchBootPageToROM();
1.1.1.8 root 80: }
81:
82: // アドレスデコーダ
83: /*static*/ inline uint32
84: PROM0Device::Decoder(uint32 addr)
85: {
86: // ここはブートページか ROM かの比較だけで十分 (それ以外はここに来ない)
87: if (addr < 0x0100'0000) {
88: addr += 0x4100'0000;
89: }
90: return addr;
91: }
92:
1.1.1.14 root 93: busdata
1.1.1.15 root 94: PROM0Device::Read(busaddr addr)
1.1.1.8 root 95: {
1.1.1.15 root 96: addr.ChangeAddr(Decoder(addr.Addr()));
97: return prom->Read(addr);
1.1.1.8 root 98: }
99:
100: // LUNA では PROM へのライトアクセスでブートページを RAM に切り替えている。
101: // - LUNA-I では最初に $41000000.B への書き込みがあるが、
102: // それ以降も $41000000.L への書き込みが(複数回)あるようだ。
1.1.1.12 root 103: // - LUNA-88K では $4100000c.L への書き込みがある。
1.1.1.8 root 104: //
105: // よってアドレスを定めず PROM 全域への書き込みで最初の1回だけ発動する
106: // ようにしておく。サイズも今の所ワード(16bit)アクセスは確認してないけど
107: // 統一的に対処しておく。
1.1.1.14 root 108: busdata
1.1.1.15 root 109: PROM0Device::Write(busaddr addr, uint32 data)
1.1.1.8 root 110: {
1.1.1.14 root 111: auto mainbus = GetMainbusDevice();
112: mainbus->SwitchBootPageToRAM();
1.1.1.8 root 113:
114: // バスエラーは起きない
1.1.1.15 root 115: busdata r = 0;
116: r |= busdata::Size(addr.GetSize());
117: return r;
1.1.1.8 root 118: }
119:
1.1.1.14 root 120: busdata
1.1.1.15 root 121: PROM0Device::Peek1(uint32 addr)
1.1.1.8 root 122: {
123: addr = Decoder(addr);
1.1.1.15 root 124: return prom->Peek1(addr);
1.1.1.8 root 125: }
126:
127:
128: //
129: // PROM
130: //
131:
1.1 root 132: // コンストラクタ
133: PROMDevice::PROMDevice()
1.1.1.12 root 134: : inherited(OBJ_PROM)
1.1 root 135: {
1.1.1.8 root 136: // LUNA では ROM への書き込みは何も起きない
137: write_op = 0;
1.1 root 138: }
139:
140: // デストラクタ
141: PROMDevice::~PROMDevice()
142: {
143: }
144:
1.1.1.12 root 145: // 初期化
1.1 root 146: bool
147: PROMDevice::Init()
148: {
1.1.1.11 root 149: VMType vmtype = gMainApp.GetVMType();
1.1 root 150:
151: switch (vmtype) {
1.1.1.11 root 152: case VMType::LUNA1:
1.1.1.12 root 153: if (gMainApp.msxdos_mode) {
154: return true;
155: }
1.1.1.17 root 156: return InitLuna1();
1.1 root 157:
1.1.1.11 root 158: case VMType::LUNA88K:
1.1 root 159: return InitLuna88k();
160:
161: default:
1.1.1.15 root 162: PANIC("unknown vmtype %u", (uint)vmtype);
1.1 root 163: }
164: }
165:
166: // LUNA-I の場合
167: bool
1.1.1.6 root 168: PROMDevice::InitLuna1()
1.1 root 169: {
1.1.1.12 root 170: uint devlen = 128 * 1024;
1.1 root 171: mask = devlen - 1;
172:
173: // ファイル名が存在してる時しかここに来ない
174: const ConfigItem& item = gConfig->Find("prom-image");
175: if (LoadROM(item.AsString().c_str(), devlen) == false) {
176: return false;
177: }
178:
1.1.1.8 root 179: // XXX アクセスウェイトは未調査。とりあえず RAM と同程度にしておく
1.1.1.19! root 180: wait = busdata::Wait(1 * mpu->GetClock_tsec());
1.1.1.8 root 181:
1.1.1.2 root 182: // ROM バージョンを確認。確認されてるのは
183: // V4.02 (Wed Oct 12 17:07:11 1988)
184: // V4.22 (Thu Jul 27 11:45:42 1989)
185: // V4.25
186: //
187: // とりあえず V4.22 は +$00d390 からにタイムスタンプ文字列があるので
188: // それで判別してみる。
189: //00d390 54 68 75 20 4a 75 6c 20 32 37 20 31 31 3a 34 35 |Thu Jul 27 11:45|
190: //00d3a0 3a 34 32 20 31 39 38 39 00 00 00 00 4e 56 fd f8 |:42 1989....NV..|
191:
192: if (strcmp((const char *)&mem[0xd390], "Thu Jul 27 11:45:42 1989") == 0) {
1.1.1.12 root 193: romver = 422;
194: }
195:
196: if (romver == 0) {
197: putmsg(0, "Unknown PROM version");
198: } else {
1.1.1.15 root 199: putmsg(1, "v%u.%02u detected", romver / 100, romver % 100);
1.1.1.12 root 200: }
201:
202: if (romver == 422) {
203: // V4.22。他の ROM は何も分からないので何もしない。
1.1.1.2 root 204:
205: // MAC アドレスは ROM に書き込まれているので、ここで上書き
206: if (strcmp((const char *)&mem[0x1ffd8], "ENADDR") != 0) {
207: warnx("ENADDR not found; unknown ROM V4.22 variants?");
208: return false;
209: }
210:
211: // MAC アドレスは12桁の16進数文字列として書き込まれている。例えば
212: // 前半の 00:00:0A 部分は 30 30 30 30 30 41 "00000A" という感じ。
1.1.1.16 root 213: MacAddr macaddr {};
1.1.1.13 root 214: if (EthernetDevice::GetConfigMacAddr(0, &macaddr, true) == false) {
1.1.1.12 root 215: // エラーメッセージは表示済み
216: return false;
217: }
218:
219: const char *msg;
220: if (macaddr.Empty()) {
221: // "rom" なら元イメージのままにする。
222: msg = "ROM";
223: } else {
224: // 指定されたら上書き。
225: memcpy(&imagebuf[0x1ffe0], macaddr.ToString().c_str(), 12);
226: msg = "config";
227: }
228: putmsg(1, "macaddr=%c%c:%c%c:%c%c:%c%c:%c%c:%c%c (from %s)",
229: std::tolower(imagebuf[0x1ffe0]),
230: std::tolower(imagebuf[0x1ffe1]),
231: std::tolower(imagebuf[0x1ffe2]),
232: std::tolower(imagebuf[0x1ffe3]),
233: std::tolower(imagebuf[0x1ffe4]),
234: std::tolower(imagebuf[0x1ffe5]),
235: std::tolower(imagebuf[0x1ffe6]),
236: std::tolower(imagebuf[0x1ffe7]),
237: std::tolower(imagebuf[0x1ffe8]),
238: std::tolower(imagebuf[0x1ffe9]),
239: std::tolower(imagebuf[0x1ffea]),
240: std::tolower(imagebuf[0x1ffeb]),
241: msg);
1.1.1.2 root 242:
243: // SCSI デバッグビット
1.1.1.19! root 244: bool scsi_debug_bit = gConfig->Find(".prom-scsi-debug").AsBool();
1.1.1.11 root 245: imagebuf[0x4100981d - 0x41000000] = (scsi_debug_bit) ? 0x01 : 0;
1.1 root 246: }
247:
248: return true;
249: }
250:
1.1.1.12 root 251: // LUNA-88K の場合
1.1 root 252: bool
253: PROMDevice::InitLuna88k()
254: {
1.1.1.12 root 255: uint devlen = 256 * 1024;
1.1 root 256: mask = devlen - 1;
257:
258: // ファイル名が存在してる時しかここに来ない
259: const ConfigItem& item = gConfig->Find("prom-image");
260: if (LoadROM(item.AsString().c_str(), devlen) == false) {
261: return false;
262: }
263:
1.1.1.8 root 264: // XXX アクセスウェイトは未調査。とりあえず RAM と同程度にしておく
1.1.1.19! root 265: wait = busdata::Wait(3 * mpu->GetClock_tsec());
1.1.1.8 root 266:
1.1.1.12 root 267: // ROM バージョンを照合。どこかに日付が埋まってるっぽい。
268: //
269: // 1.20:
270: //02c190 00 00 04 b0 28 0d 2f e1 28 0d 2f e1 54 68 75 20 |....(./.(./.Thu |
271: //02c1a0 41 70 72 20 31 38 20 31 34 3a 33 34 3a 32 35 20 |Apr 18 14:34:25 |
272: //02c1b0 31 39 39 31 00 00 00 00 41 02 c1 9c 00 00 00 00 |1991....A.......|
273: //
274: // 1.31:
275: //03c940 00 00 05 1e 2a 0f 28 78 2a 0f 28 78 54 75 65 20 |....*.(x*.(xTue |
276: //03c950 4d 61 79 20 31 32 20 31 31 3a 30 37 3a 35 32 20 |May 12 11:07:52 |
277: //03c960 31 39 39 32 00 00 00 00 41 03 c9 4c 00 00 00 00 |1992....A..L....|
278: //
279: // 1.37:
280: //03cf30 00 00 05 5a 2b 5a 4a 7d 2b 5a 4a 7d 4d 6f 6e 20 |...Z+ZJ}+ZJ}Mon |
281: //03cf40 4a 61 6e 20 31 38 20 31 35 3a 31 33 3a 31 37 20 |Jan 18 15:13:17 |
282: //03cf50 31 39 39 33 00 00 00 00 41 03 cf 3c 00 00 00 00 |1993....A..<....|
283:
284: if (strcmp((const char *)&mem[0x2c19c], "Thu Apr 18 14:34:25 1991") == 0) {
285: romver = 120;
286: } else
287: if (strcmp((const char *)&mem[0x3c94c], "Tue May 12 11:07:52 1992") == 0) {
288: romver = 131;
289: } else
290: if (strcmp((const char *)&mem[0x3cf3c], "Mon Jan 18 15:13:17 1993") == 0) {
291: romver = 137;
292: }
293:
294: if (romver == 0) {
295: putmsg(0, "Unknown PROM version");
296: } else {
1.1.1.15 root 297: putmsg(1, "v%u.%02u detected", romver / 100, romver % 100);
1.1.1.12 root 298: }
299:
1.1.1.18 root 300: if (romver == 120) {
301: // m88100 コアのパイプラインが未実装なのをごまかすハック。
302: //
303: // PROM 1.20 のプローブルーチン(かな?) はこうなっている。
304: // 410005f8: f5421400 ld r10,r2, r0
305: // 410005fc: f4405800 or r2, r0, r0
306: // 41000600: c0000002 br 0x41000608
307: // 41000604: 58400001 or r2, r0, #0x0001
308: // 41000608: f1a0e800 tcnd ne0,r0, #0x000
309: // :
310: // ここで 410005f8 の ld 命令がバスエラーになると以下の例外ハンドラに
311: // 飛ぶ。
312: // 0000102c: 80008060 stcr r0, ssbr ; Clear SSBR
313: // 00001030: 80204080 ldcr r1, sxip
314: // 00001034: 800180c1 stcr r1, sfip ; sfip <- sxip
315: // 00001038: f0218021 clr r1, r1, 1<1> ; &= ~VALID
316: // 0000103c: 80018081 stcr r1, sxip ; ただし sxip は readonly
317: // 00001040: 800080a0 stcr r0, snip ; snip <- 0 (Invalidate)
318: // 00001044: 80204260 ldcr r1, sr2
319: // 00001048: f400fc00 rte
320: //
321: // 例外ハンドラは特に何もせず sxip の位置から実行を再開しようとする。
322: // そのまま読むと例外発生前に実行した命令(ld 命令)を再実行するように
323: // 読めるが、ld 命令直後の OR 命令は整数ユニット担当であり更に ld
324: // 命令の rD と OR 命令の rD, rS は依存関係もないため、データユニット
325: // が ld 命令のバス応答を待っている間に整数ユニットが OR 命令を実行
326: // できる。もっと言えばバスの応答時間次第で少なくとも同期命令である
327: // tcnd 命令の直前までは進むことが出来る。
328: // この例外ハンドラはおそらくこれを踏まえて、例外発生時の sxip が
329: // 少なくとも ld 命令よりも進んでいることを前提にしているようだ。
330: //
331: // ところがうちではこのパイプラインを実装しておらず、xip が ld 命令を
332: // 指したまま例外が発生するため、このままでは rte 命令で再び ld 命令に
333: // 戻って行ってしまい無限ループになる。
334: //
335: // この DAE ハンドラは OpenBSD のブートローダからも呼ばれるので、
336: // MPU 側で呼び出し元を特定して動作を書き換えるハックでは十分でない。
337: // そのため PROM 1.20 であれば ROM のコードを変更して snip から
338: // 再実行するようにする。
339: //
340: // OpenBSD (カーネルのほう) でも例外発生時の sxip が実機よりも遅い
341: // ところを指している (可能性がある) という現象は変わらないが、
342: // これによる齟齬は発生しないはず。
343: // ld/st 命令中の例外なら、xip がどこを指していても xip は実行済み
344: // で snip から再実行することには変わりないので問題は起きないはず。
345: // xmem 命令中の例外なら、xmem は同期命令なので xip はここに留まる
346: // ためおそらく実機と同じ動作になってるはず。
347: //
348: // ROM上 RAMコピー後
349: // 410016c0: 00001030: 80204080 ldcr r1,sxip
350: // ↓
351: // 410016c0: 00001030: 802040c0 ldcr r1,snip
352: if (be32toh(*(const uint32 *)&imagebuf[0x16c0]) == 0x80204080) {
353: imagebuf[0x16c3] = 0xa0;
354: }
355: }
356:
1.1 root 357: return true;
358: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.