|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: // PROM (LUNA-I, LUNA88K 共通)
1.1.1.8 root 8: //
9: // IODevice
10: // |
11: // +-- ROMDevice (LoadROM()、ウェイト、マスク等を持つ)
12: // | |
13: // | | +------------+
14: // | +--| PROMDevice | (LUNA* の PROM)
15: // | | +------------+
16: // | |
17: // | +-- IPLROM1Device (X680x0 の IPLROM 後半)
18: // | +-- IPLROM2Device (X680x0 の IPLROM 前半)
19: // | +-- CGROMDevice (X680x0 の CGROM)
20: // | +-- LunaPROMEmuDevice (LUNA PROM エミュレーションの共通部分)
21: // | |
22: // | +-- Luna1PROMEmuDevice (LUNA1 の PROM エミュレーション)
23: // | +-- Luna88kPROMEmuDevice (LUNA88K の PROM エミュレーション)
24: // |
25: // | +-------------+
26: // +-- | PROM0Device | (LUNA* のブートページ切り替え用プロキシ)
27: // | +-------------+
28: // |
29: // +-- IPLROM0Device (X680x0 のブートページ切り替え用プロキシ)
1.1 root 30:
31: #include "prom.h"
32: #include "config.h"
1.1.1.8 root 33: #include "ethernet.h"
1.1 root 34: #include "mainapp.h"
1.1.1.5 root 35: #include "scheduler.h"
1.1 root 36:
1.1.1.8 root 37: std::unique_ptr<PROM0Device> gPROM0;
38: std::unique_ptr<IODevice> gPROM;
39:
40: //
41: // PROM0
42: //
43: // PROM0 は PROM のブートページ担当。
44: // これ自身はメモリ領域を持たず PROM へのプロキシとして動作する。
45: //
46: // リセット後最初の ROM 領域への書き込みアクセスでブートページを RAM に
47: // 切り替える。
48: // LUNA のブートページのアドレス範囲は不明なので、とりあえず devtable の
49: // 1区画である 16MB ($0000'0000..$00ff'ffff) とする。実害はない。
50:
51: // コンストラクタ
52: PROM0Device::PROM0Device()
53: : inherited("PROM(0)")
54: {
55: ClearAlias();
56: AddAlias("PROM0");
57: }
58:
59: // デストラクタ
60: PROM0Device::~PROM0Device()
61: {
62: }
63:
64: bool
65: PROM0Device::Init()
66: {
67: prom = gPROM.get();
68: return true;
69: }
70:
71: void
72: PROM0Device::ResetHard()
73: {
74: gVM->SwitchBootPageToROM(this);
75: }
76:
77: // アドレスデコーダ
78: /*static*/ inline uint32
79: PROM0Device::Decoder(uint32 addr)
80: {
81: // ここはブートページか ROM かの比較だけで十分 (それ以外はここに来ない)
82: if (addr < 0x0100'0000) {
83: addr += 0x4100'0000;
84: }
85: return addr;
86: }
87:
88: uint64
89: PROM0Device::Read8(uint32 addr)
90: {
91: addr = Decoder(addr);
92: return prom->Read8(addr);
93: }
94:
95: uint64
96: PROM0Device::Read16(uint32 addr)
97: {
98: addr = Decoder(addr);
99: return prom->Read16(addr);
100: }
101:
102: uint64
103: PROM0Device::Read32(uint32 addr)
104: {
105: addr = Decoder(addr);
106: return prom->Read32(addr);
107: }
108:
109: // LUNA では PROM へのライトアクセスでブートページを RAM に切り替えている。
110: // - LUNA-I では最初に $41000000.B への書き込みがあるが、
111: // それ以降も $41000000.L への書き込みが(複数回)あるようだ。
112: // - LUNA88K では $4100000c.L への書き込みがある。
113: //
114: // よってアドレスを定めず PROM 全域への書き込みで最初の1回だけ発動する
115: // ようにしておく。サイズも今の所ワード(16bit)アクセスは確認してないけど
116: // 統一的に対処しておく。
117: uint64
118: PROM0Device::Write()
119: {
120: gVM->SwitchBootPageToRAM(this);
121:
122: // バスエラーは起きない
123: return 0;
124: }
125:
126: uint64
127: PROM0Device::Write8(uint32 addr, uint32 data)
128: {
129: return Write();
130: }
131:
132: uint64
133: PROM0Device::Write16(uint32 addr, uint32 data)
134: {
135: return Write();
136: }
137:
138: uint64
139: PROM0Device::Write32(uint32 addr, uint32 data)
140: {
141: return Write();
142: }
143:
144: uint64
145: PROM0Device::Peek8(uint32 addr)
146: {
147: addr = Decoder(addr);
148: return prom->Peek8(addr);
149: }
150:
151:
152: //
153: // PROM
154: //
155:
1.1 root 156: // コンストラクタ
157: PROMDevice::PROMDevice()
1.1.1.7 root 158: : inherited("PROM")
1.1 root 159: {
160: devaddr = 0x41000000;
1.1.1.8 root 161:
162: // LUNA では ROM への書き込みは何も起きない
163: write_op = 0;
1.1 root 164: }
165:
166: // デストラクタ
167: PROMDevice::~PROMDevice()
168: {
169: }
170:
171: bool
172: PROMDevice::Init()
173: {
174: vmtype_t vmtype = gMainApp.GetVMType();
175:
176: switch (vmtype) {
177: case VMTYPE_LUNA1:
1.1.1.6 root 178: return InitLuna1();
1.1 root 179:
180: case VMTYPE_LUNA88K:
181: return InitLuna88k();
182:
183: default:
184: PANIC("unknown vmtype %d", vmtype);
185: }
186: }
187:
188: // LUNA-I の場合
189: bool
1.1.1.6 root 190: PROMDevice::InitLuna1()
1.1 root 191: {
192: devlen = 128 * 1024;
193: mask = devlen - 1;
194:
195: // ファイル名が存在してる時しかここに来ない
196: const ConfigItem& item = gConfig->Find("prom-image");
197: if (LoadROM(item.AsString().c_str(), devlen) == false) {
198: return false;
199: }
200:
1.1.1.8 root 201: // XXX アクセスウェイトは未調査。とりあえず RAM と同程度にしておく
202: wait = 1;
203:
1.1.1.2 root 204: // ROM バージョンを確認。確認されてるのは
205: // V4.02 (Wed Oct 12 17:07:11 1988)
206: // V4.22 (Thu Jul 27 11:45:42 1989)
207: // V4.25
208: //
209: // とりあえず V4.22 は +$00d390 からにタイムスタンプ文字列があるので
210: // それで判別してみる。
211: //00d390 54 68 75 20 4a 75 6c 20 32 37 20 31 31 3a 34 35 |Thu Jul 27 11:45|
212: //00d3a0 3a 34 32 20 31 39 38 39 00 00 00 00 4e 56 fd f8 |:42 1989....NV..|
213:
214: if (strcmp((const char *)&mem[0xd390], "Thu Jul 27 11:45:42 1989") == 0) {
215: // V4.22
216: putlog(1, "V4.22 detected");
217:
218: // MAC アドレスは ROM に書き込まれているので、ここで上書き
219: if (strcmp((const char *)&mem[0x1ffd8], "ENADDR") != 0) {
220: warnx("ENADDR not found; unknown ROM V4.22 variants?");
221: return false;
222: }
223:
224: // MAC アドレスは12桁の16進数文字列として書き込まれている。例えば
225: // 前半の 00:00:0A 部分は 30 30 30 30 30 41 "00000A" という感じ。
226: const macaddr_t macaddr = gEthernet->GetMacAddr();
227: memcpy(&mem[0x1ffe0], macaddr.to_string().c_str(), 12);
228:
229: // SCSI デバッグビット
230: bool scsi_debug_bit = gConfig->Find(".prom-scsi-debug").AsInt();
231: mem[0x4100981d - 0x41000000] = (scsi_debug_bit) ? 0x01 : 0;
232: } else {
233: // 他の ROM は何も分からないので何もしない。
234: putlog(0, "Unknown PROM Version");
1.1 root 235: }
236:
237: return true;
238: }
239:
240: // LUNA88K の場合
241: bool
242: PROMDevice::InitLuna88k()
243: {
244: devlen = 256 * 1024;
245: mask = devlen - 1;
246:
247: // ファイル名が存在してる時しかここに来ない
248: const ConfigItem& item = gConfig->Find("prom-image");
249: if (LoadROM(item.AsString().c_str(), devlen) == false) {
250: return false;
251: }
252:
1.1.1.8 root 253: // XXX アクセスウェイトは未調査。とりあえず RAM と同程度にしておく
254: wait = 3;
255:
1.1 root 256: return true;
257: }
258:
259: bool
260: PROMDevice::PowerOn()
261: {
262: // パフォーマンス測定に電源オン時の実時刻を記憶。
1.1.1.8 root 263: // Read8() 参照。
1.1.1.6 root 264: sw.Start();
1.1 root 265: return true;
266: }
267:
268: uint64
269: PROMDevice::Read8(uint32 addr)
270: {
271: // パフォーマンス測定のためのバックドア。
1.1.1.8 root 272: // 0x42e576f7 番地をバイト読み込みを行うと VM 電源オン時から現在までの
1.1 root 273: // 実時間をホスト (ログ) に出力する。NetBSD のマルチユーザブートが
274: // あらかた終わってログインプロンプトが出る少し前に実行される
275: // /etc/rc.local に
1.1.1.8 root 276: // dd if=/dev/mem skip=1122334455 bs=1 count=1
1.1 root 277: // と書いておくことでここまでにかかる時間を表示できる。ただしこの値は
278: // ホスト性能に依存するため、同一環境下での相対的な比較のみが可能。
279: // 実機ではこの動作では PROM のミラーが読めるだけなので副作用はないはず。
1.1.1.8 root 280: // 出来れば書き込みをバックドアにしたかったが、たぶんカーネルレベルで
281: // 弾いてあるので、読み込みを使う。
282: if (__predict_false(addr == 0x42e576f7)) {
1.1.1.6 root 283: uint64 rtime;
284: uint64 vtime;
1.1 root 285:
286: // 電源オン時からここまでの時間を表示するだけでリセット等はしない。
1.1.1.6 root 287: rtime = sw.Elapsed();
288: vtime = gMPU->GetVirtTime();
1.1 root 289: putlog(0, "電源オンから %d.%03d 秒経過 %3.3f%%",
1.1.1.6 root 290: (int)(rtime / (1000 * 1000 * 1000)),
291: (int)((rtime / (1000 * 1000)) % 1000),
292: (double)vtime / rtime * 100);
1.1 root 293: }
294:
295: // それ以外は通常動作
296: return inherited::Read8(addr);
297: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.