|
|
1.1 root 1: //
2: // nono
1.1.1.2 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.2 root 7: // LUNA-I の ROM エミュレーション
8:
9: // IODevice
10: // |
11: // +----------+----------+
12: // v v
13: // ROMDevice LunaPROMEmuDeivce (ROM エミュレーション)
14: // |
15: // v
16: // PROMDevice (実ROM)
17:
18: #include "romemu_luna.h"
1.1 root 19: #include "aout.h"
20: #include "bitmap.h"
21: #include "bt454.h"
1.1.1.2 root 22: #include "ethernet.h"
1.1 root 23: #include "ffs_dinode.h"
24: #include "ffs_dir.h"
25: #include "ffs_fs.h"
26: #include "mainapp.h"
27: #include "memorystream.h"
28: #include "mk48t02.h"
1.1.1.2 root 29: #include "mpu680x0.h"
1.1 root 30: #include "pio.h"
31: #include "ram.h"
32: #include "sio.h"
33: #include "spc.h"
34:
35: // ROM エミュレーションの状態遷移
36: enum {
37: STATE_CLOSED = 0, // バックドアは塞がれている
38: STATE_RESET, // リセット後、アクセス前
39: STATE_INIT, // プロンプト初期化中
40: STATE_PROMPT, // プロンプト実行中
41: };
42:
43: // コンストラクタ
44: LunaPROMEmuDevice::LunaPROMEmuDevice()
45: {
1.1.1.6 ! root 46: logname = "prom";
! 47: devname = "PROM";
! 48:
1.1.1.2 root 49: // LUNA-I の ROM の特定アドレスを読み出す人がいるので、
50: // サイズは実 ROM と同じにしておく必要がある。
51: devlen = 128 * 1024;
52: romdata.reset(new uint8 [devlen]);
1.1 root 53: }
54:
55: // デストラクタ
56: LunaPROMEmuDevice::~LunaPROMEmuDevice()
57: {
58: }
59:
60: bool
61: LunaPROMEmuDevice::Init()
62: {
63: // 都度都度 dynamic_cast は面倒なので、ここで一度代入しとく
64: nvram = dynamic_cast<MK48T02Device*>(gRTC.get());
65:
1.1.1.2 root 66: MemoryStreamBE ms(romdata.get());
1.1 root 67: ms.Write32(0x00002000); // +00: リセットベクタ SP
68: ms.Write32(0x41000400); // +04: リセットベクタ PC
69: ms.Write32(0); // +08
70: ms.Write32(0x41000500); // +0c: メモリサイズ格納先
71:
72: // リセット時に実行する命令
1.1.1.2 root 73: ms.SetPtr(&romdata[0x400]);
74: ms.Write16(0x2039); // move.l (ROMIO_INIT),d0
75: ms.Write32(ROMIO_INIT);
76: ms.Write16(0x660c); // bne _go ; 起動先あればGO
1.1 root 77: //_prompt:
1.1.1.2 root 78: ms.Write32(0x4e722500); // stop #0x2500 ; キー入力を待つ
1.1.1.6 ! root 79: ms.Write16(0x2039); // move.l (ROMIO_ENTRY),d0
! 80: ms.Write32(ROMIO_ENTRY); // ; キー入力処理
1.1.1.2 root 81: ms.Write16(0x67f4); // beq _prompt ; 起動先なければループ
1.1 root 82: //_go:
83: ms.Write16(0x2040); // move.l d0,a0 ; ジャンプ先を a0 にセット
1.1.1.2 root 84: ms.Write16(0x2039); // move.l (ROMIO_CLOSE),d0
85: ms.Write32(ROMIO_CLOSE); // ; 後始末
1.1 root 86: ms.Write16(0x4ed0); // jmp (a0) ; ジャンプ
87:
88: // Lv6 (SIO) 割り込みハンドラ
1.1.1.6 ! root 89: // キーボード割り込みが上がったら KEYIN をコールする
1.1.1.2 root 90: ms.SetPtr(&romdata[0x600]);
1.1.1.6 ! root 91: ms.Write16(0x4ab9); // tst.l (ROMIO_KEYIN)
! 92: ms.Write32(ROMIO_KEYIN); // ; キー入力処理
1.1 root 93: ms.Write16(0x4e73); // rte
94:
1.1.1.2 root 95: // MAC アドレス
96: const macaddr_t macaddr = gEthernet->GetMacAddr();
97: strcpy((char *)&romdata[0x1ffd8], "ENADDR");
98: memcpy(&romdata[0x1ffe0], macaddr.to_string().c_str(), 12);
1.1 root 99:
100: // RAM にベクタを書き込む
101: // XXX VBR を 41000000 に向けるのとどっちが楽かはある
102: gRAM->Write32(0x78, 0x41000600); // +78: Lv6 割り込み
103:
104: return true;
105: }
106:
107: void
108: LunaPROMEmuDevice::ResetHard()
109: {
1.1.1.2 root 110: is_closed = false;
1.1 root 111: }
112:
113: // アドレスデコーダ
114: inline uint64
115: LunaPROMEmuDevice::Decoder(uint32 addr) const
116: {
1.1.1.2 root 117: // 折り返しなし
1.1 root 118: return addr - baseaddr;
119: }
120:
121: uint64
1.1.1.2 root 122: LunaPROMEmuDevice::Read8(uint32 addr)
123: {
124: uint64 offset = Decoder(addr);
125:
126: // 謎の IO 空間はロングワードアクセスのみ
127: return romdata[offset & (devlen - 1)];
128: }
129:
130: uint64
131: LunaPROMEmuDevice::Read16(uint32 addr)
132: {
133: uint64 offset = Decoder(addr);
134:
135: // 謎の IO 空間はロングワードアクセスのみ
136: return be16toh(*(uint16 *)&romdata[offset & (devlen - 1)]);
137: }
138:
139: uint64
1.1 root 140: LunaPROMEmuDevice::Read32(uint32 addr)
141: {
142: uint64 offset = Decoder(addr);
143:
144: // ロングワードアクセスでだけ謎の I/O 空間が見える
1.1.1.2 root 145: if (!is_closed) {
1.1 root 146: switch (offset) {
1.1.1.2 root 147: case ROMIO_INIT - baseaddr:
148: return ROM_Init();
1.1 root 149:
1.1.1.2 root 150: case ROMIO_KEYIN - baseaddr:
151: return ROM_Keyin();
1.1 root 152:
1.1.1.2 root 153: case ROMIO_CLOSE - baseaddr:
154: return ROM_Close();
1.1 root 155:
1.1.1.6 ! root 156: case ROMIO_ENTRY - baseaddr:
! 157: return entrypoint;
! 158:
1.1 root 159: default:
160: break;
161: }
162: }
1.1.1.2 root 163: return be32toh(*(uint32 *)&romdata[offset & (devlen - 1)]);
164: }
165:
166: uint64
167: LunaPROMEmuDevice::Write8(uint32 addr, uint32 data)
168: {
169: return 0;
170: }
171:
172: uint64
173: LunaPROMEmuDevice::Write16(uint32 addr, uint32 data)
174: {
175: return 0;
176: }
177:
178: uint64
179: LunaPROMEmuDevice::Write32(uint32 addr, uint32 data)
180: {
181: return 0;
182: }
183:
184: uint64
185: LunaPROMEmuDevice::Peek8(uint32 addr)
186: {
187: uint64 offset = Decoder(addr);
188:
189: return romdata[offset & (devlen - 1)];
1.1 root 190: }
191:
192: // 起動時の処理を行う。
193: // 次段実行ファイルの読み込みに成功すればそのエントリポイントを返す。
194: // そうでなければ 0 を返すので、プロンプトに処理を移すこと。
195: uint32
1.1.1.2 root 196: LunaPROMEmuDevice::ROM_Init()
1.1 root 197: {
198: MemoryStreamBE ms;
199: uint32 entry;
200:
201: entrypoint = 0;
202:
203: // ROM 0x41000000.L リセットベクタ(SP,PC)もカーネルが参照している
204: // (これは Init() が用意している)
205: // ROM 0x4100000c.L メモリサイズ(バイト数)格納先アドレス
206: // ROM 0x410000b8.L プレーンマスク格納先アドレス
207:
208: // メモリサイズはとりあえず 0x41000500 に格納
1.1.1.2 root 209: ms.SetPtr(&romdata[0x500]);
1.1 root 210: ms.Write32(::ram_size);
211:
212: // プレーン数(1..4)をプレーンマスク(0x01..0x0f)に
213: int nplane = gBitmap->GetPlaneCount();
214: int planemask = (1 << nplane) - 1;
215: // 実際は RAM 上を指しているのだが、とりあえずすぐ隣に置いておく。
1.1.1.2 root 216: ms.SetPtr(&romdata[0xb8]);
1.1 root 217: ms.Write32(0x410000bc);
218: ms.Write32(planemask);
219:
220: // 初期パレット
1.1.1.4 root 221: // 偶数番は白(R/G/B=15/15/15)、奇数番は黒(0/0/0)。
222: uint8 val = 0xf0;
223: gBT454->Write8(0, 0); // select palette
224: for (int i = 0; i < 16; i++) {
225: gBT454->Write8(1, val); // R
226: gBT454->Write8(1, val); // G
227: gBT454->Write8(1, val); // B
228: val ^= 0xf0;
229: }
1.1 root 230:
231: // SCSI
232: // BDID の書き込みによってホストデバイスがバスにアタッチされる構造なので。
233: gSPC->Write8(SPC::BDID, 7);
234:
1.1.1.2 root 235: // 内蔵 ROM: キーボード(chB)を初期化
236: gSIO->Write8(0x51000006, 0x01); // CR1
237: gSIO->Write8(0x51000006, 0x10); // RX Int(all char)
238: gSIO->Write8(0x51000006, 0x03); // CR3
239: gSIO->Write8(0x51000006, 0xc1); // RX Enable, 8bit
240: gSIO->Write8(0x51000006, 0x04); // CR4
241: gSIO->Write8(0x51000006, 0x44); // Stop 1bit
242: gSIO->Write8(0x51000006, 0x05); // CR5
243: gSIO->Write8(0x51000006, 0x68); // TX Enable, 8bit
244:
245: // 内蔵 ROM: スクリーンを初期化
1.1 root 246: text.Init(80, 32);
247: gBitmap->EmuConsoleOpen(&text);
1.1.1.2 root 248: inputbuf.clear();
249: inputpos = 0;
250: is_shift = false;
1.1.1.3 root 251: mousecnt = 0;
1.1.1.2 root 252: prompt_idx = 0;
253: prompt = default_prompt;
1.1 root 254:
255: text.Print(0, 0, "NONO %d.%d.%d Emulated ROM Monitor",
256: NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER);
257: text.Locate(0, 2);
258:
259: // 表示環境が整ったところで NVRAM を照合
260: if (VerifyNVRAM() == false) {
261: InitNVRAM();
262: text.Print("** NVRAM Initialized.\n");
263: text.Print("\n");
264: return 0;
265: }
266:
1.1.1.5 root 267: // 実際の画面更新は ConsoleUpdate() を呼ぶことで行われるので、
1.1 root 268: // タイトルを Print してはいるが、エラーなくブートプログラムに制御を
269: // 渡せた場合にはそれすら表示せずにゲストに処理を移す。
270: // 意図して或いは意図せずプロンプトに降りる場合だけ Update が起きて
271: // 初めて画面が更新される。
272:
273: // DIPSW#1-1 が UP なら自動起動、DOWN なら ROM モニタ起動なので、
274: // これを真似する。
275: entry = 0;
276: if ((gPIO0->GetDIPSW1() & 1)) {
277: if (gMainApp.host_file) {
278: // ホストのファイルを読み込んでエントリポイントアドレスを
279: // この Read アクセスの読み出し値として返す。
280: // エラーがあればエラーメッセージは呼び出し先側で表示してある。
281: entry = LoadHostFile();
282: } else {
283: // NVRAM の設定に従ってブート先を探す。
284: errmsg.clear();
285: entry = LoadByNVRAM("");
286: if (entry == 0) {
287: text.Print("** %s\n", errmsg.c_str());
288: }
289: }
290: }
291:
1.1.1.2 root 292: if (entry == 0) {
293: ConsoleUpdate();
294: }
295:
1.1 root 296: return entry;
297: }
298:
299: // ROM 処理のクローズ。
300: // 次段プログラムにジャンプするための処理も含む。
301: uint32
1.1.1.2 root 302: LunaPROMEmuDevice::ROM_Close()
1.1 root 303: {
304: // ここでバックドアに蓋をする
1.1.1.2 root 305: is_closed = true;
1.1 root 306:
307: // コンソールクローズ
308: gBitmap->EmuConsoleClose();
309:
310: // NetBSD のブートローダがカーネルを読み込む時にはいくつかレジスタ
311: // 渡しのパラメータがあるので、ここで用意する。本当はターゲットが
312: // NetBSD カーネルの時に限定すべきのような気もするけど、とりあえず。
313: // ROM がブートローダを読み込む時にはそのような取り決め事は一切ない
314: // ので、ブートローダを読む時にも同じ値が入ってしまうが気にしない
315: // ことにする。
316: // 必要なパラメータは
317: // %d6 に bootdev、NetBSD/luna68k の場合は
318: // bootdev = $MACUPP00
319: // ||||++-- パーティション番号
320: // |||+---- SCSI ID (XXX ただしここでは 6 決め打ち)
321: // ||+----- unit (spc0 なら 0 のやつのはず)
322: // |+------ アダプタ (spc = 0、lance = 1)
323: // +------- magic ($a)
324: //
325: // %d7 に howto (0 でよい)
326: // (と %a0 にジャンプ先を入れるがこれは Init() が書き出した ROM
327: // アセンブラコード側で行っている)
328: m68kcpu *cpu = gMPU680x0->GetCPU();
329: RegD(6) = 0xa0060000;
330: RegD(7) = 0;
331:
332: // 戻り値は使わないのでなんでもいい
333: return 0;
334: }
335:
1.1.1.2 root 336: // ROM プロンプトのキー入力。
1.1 root 337: // VM 側の仮想マシンを回すため、ここでは入力待ちはしない。
338: // エントリポイントが取得できればそれを、そうでなければ 0 を返す。
339: uint32
1.1.1.2 root 340: LunaPROMEmuDevice::ROM_Keyin()
1.1 root 341: {
1.1.1.2 root 342: uint32 lunakey = gSIO->Read8(0x51000004);
1.1 root 343:
1.1.1.3 root 344: // マウス入力の 2, 3バイト目は無視
345: if (mousecnt > 0) {
346: mousecnt--;
347: return 0;
348: }
349:
1.1.1.2 root 350: // SHIFT キーだけ状態を持つので先に処理
351: if (lunakey == 0x0c || lunakey == 0x0d) { // 左右SHIFT押下
352: is_shift = true;
353: return 0;
354: }
355: if (lunakey == 0x8c || lunakey == 0x8d) { // 左右SHIFT開放
1.1 root 356: is_shift = false;
1.1.1.2 root 357: return 0;
358: }
1.1.1.3 root 359: // マウスデータの1バイト目なら、後続2バイトをスキップ
360: if ((lunakey & 0xf8) == 0x80) {
361: mousecnt = 2;
362: return 0;
363: }
364: // キー開放は無視してよい
1.1.1.2 root 365: if ((lunakey & 0x80)) {
366: return 0;
367: }
1.1 root 368:
1.1.1.2 root 369: switch (lunakey) {
370: case 0x11: // BS
371: if (inputpos > 0) {
372: inputbuf.erase(--inputpos);
1.1 root 373: }
1.1.1.2 root 374: break;
375: case 0x12: // Enter
376: {
377: // 現在位置のカーソルを消去
378: uint16 *rawbuf = text.GetRawBuf();
379: int curx = prompt.length() + inputpos;
380: rawbuf[text.GetY() * text.GetCol() + curx] &= ~TA::On;
381: // 改行
382: text.Putc('\n');
383: // コマンド処理。
384: // エントリポイントが見付かればもうここは放棄するので帰るだけ。
385: uint32 entry = Command();
386: if (entry != 0) {
387: return entry;
1.1 root 388: }
1.1.1.2 root 389: inputbuf.clear();
390: inputpos = 0;
391: break;
392: }
393: case 0x1d: // ←
394: if (inputpos > 0) {
395: inputpos--;
1.1 root 396: }
1.1.1.2 root 397: break;
398: case 0x1e: // →
399: if (inputpos < inputbuf.length()) {
1.1 root 400: inputpos++;
401: }
402: break;
1.1.1.2 root 403: default: // 通常文字
404: {
405: char asciicode;
406: if (is_shift) {
407: asciicode = lunakey2shifttable[lunakey];
408: } else {
409: asciicode = lunakey2asciitable[lunakey];
410: }
411: if (asciicode == 0) {
412: // 割り当てなし
413: return 0;
414: }
415: inputbuf.insert(inputbuf.begin() + inputpos, asciicode);
416: inputpos++;
417: break;
418: }
1.1 root 419: }
1.1.1.2 root 420: putmsg(2, "inputbuf=%zd|%s|", inputbuf.length(), inputbuf.c_str());
421: ConsoleUpdate();
422: return 0;
423: }
1.1 root 424:
1.1.1.2 root 425: // 画面を更新
426: void
427: LunaPROMEmuDevice::ConsoleUpdate()
428: {
1.1 root 429: // 入力行全体を都度更新 (面倒なので行をはみ出さないようにする)
430: std::string buf = prompt + inputbuf;
431: text.Print(0, text.GetY(), "%-79s", buf.c_str());
432: // カーソルを描画 (生バッファを触って属性を立てる)
433: uint16 *rawbuf = text.GetRawBuf();
434: int curx = prompt.length() + inputpos;
435: rawbuf[text.GetY() * text.GetCol() + curx] |= TA::On;
436: // 画面更新
437: gBitmap->EmuConsoleUpdate();
438: }
439:
440: // 入力コマンド処理
441: uint32
1.1.1.2 root 442: LunaPROMEmuDevice::Command()
1.1 root 443: {
1.1.1.2 root 444: std::vector<char> buf(inputbuf.length() + 1);
1.1 root 445: std::vector<std::string> arg;
446:
447: // 1ワードごとに分解
1.1.1.2 root 448: strcpy(&buf[0], inputbuf.c_str());
449: char *p = &buf[0];
1.1 root 450: char *s = p;
451: while (*p != '\0') {
452: if (*p == ' ') {
453: *p++ = '\0';
454: arg.push_back(std::string(s));
455: s = p;
456: } else {
457: p++;
458: }
459: }
460: if (p != s) {
461: arg.push_back(std::string(s));
462: }
463: if (loglevel >= 1) {
464: for (int i = 0; i < arg.size(); i++) {
465: putmsg("arg[%d]=|%s|", i, arg[i].c_str());
466: }
467: }
468:
469: // "k" コマンド中ならここで先に処理する
470: if (prompt_idx != 0) {
1.1.1.2 root 471: CommandK(arg);
1.1 root 472: return 0;
473: }
474:
475: // 空行
476: if (arg.size() == 0) {
477: return 0;
478: }
479:
480: // 1ワード目がコマンド
481: if (arg[0] == "h") {
482: // ヘルプメッセージ
483: text.Print("Commands\n");
484: text.Print(
485: " g [<filename>] : load program\n"
486: " gh : load host program (specified by -A option)\n"
487: " h : show this message\n"
488: " k : show/set boot constants\n"
489: " x : execute loaded program\n"
490: );
491: return 0;
492: }
493: if (arg[0] == "g") {
1.1.1.2 root 494: CommandG(arg);
1.1 root 495: return 0;
496: }
497: if (arg[0] == "gh") {
1.1.1.2 root 498: CommandGH();
1.1 root 499: return 0;
500: }
501: if (arg[0] == "k") {
1.1.1.2 root 502: CommandK(arg);
1.1 root 503: return 0;
504: }
505: if (arg[0] == "x") {
1.1.1.2 root 506: return CommandX();
1.1 root 507: }
508:
509: text.Print("** Unknown command: %s\n", arg[0].c_str());
510: return 0;
511: }
512:
513: // "g" コマンド
514: // g なら NVRAM の情報に従ってプログラムをロード。
515: // g <fname> なら NVRAM の fname だけ指定の名前を使ってロード。NVRAM には
516: // 書き込まない。
517: void
1.1.1.2 root 518: LunaPROMEmuDevice::CommandG(const std::vector<std::string>& arg)
1.1 root 519: {
520: std::string ctlr = nvram->PeekString(0x0030);
521: if (ctlr == "dk") { // harddisk
522: std::string fname {};
523:
524: switch (arg.size()) {
525: case 1:
526: break;
527: case 2:
528: fname = arg[1];
529: break;
530: default:
531: text.Print("** Invalid argument.\n");
532: return;
533: }
534: errmsg.clear();
535: entrypoint = LoadByNVRAM(fname);
536: if (entrypoint == 0) {
537: text.Print("** %s\n", errmsg.c_str());
538: }
539:
540: } else if (ctlr == "et" || // netboot
541: ctlr == "sd" || // cassette tape
542: ctlr == "fl" ) // floppy
543: {
544: text.Print("** %s boot not supported.\n", ctlr.c_str());
545: return;
546: } else {
547: // 起きないはず
548: text.Print("** Invalid boot controller name.\n");
549: return;
550: }
551:
552: if (entrypoint) {
553: text.Print("Loaded. Entry point = $%08x\n", entrypoint);
554: }
555: }
556:
557: // "gh" コマンド (勝手に拡張)
558: // -A オプションで指定されたホストプログラムをロードする。
559: void
1.1.1.2 root 560: LunaPROMEmuDevice::CommandGH()
1.1 root 561: {
562: entrypoint = 0;
563: if (gMainApp.host_file) {
564: // 指定されてたら読み込む
565: entrypoint = LoadHostFile();
566: } else {
567: text.Print("** Cannot loaded; host program is not specified.\n");
568: }
569: }
570:
571: // -A オプションで指定されたホストファイルをロードする。
572: // ロードできればエントリポイントを返す。
573: // host_file が指定されている時に呼ぶこと。
574: uint32
575: LunaPROMEmuDevice::LoadHostFile()
576: {
577: assert(gMainApp.host_file);
578:
579: uint32 entry = gRAM->LoadFile(gMainApp.host_file, AOUT_MID_M68K);
580: if (entry) {
581: text.Print("Host program loaded. Entry point = $%08x\n",
582: entry);
583: } else {
584: text.Print("** Couldn't load specified host program.\n");
585: }
586: return entry;
587: }
588:
589:
590: // k コマンドのパラメータ表
591: struct kinfo {
592: uint32 offset; // NVRAM 上のオフセット
593: const char * const name; // 表示名
594: int next; // 次インデックス
595: };
596: static const struct kinfo kinfo[] = {
597: { 0 }, // 0 は通常プロンプトを指すのでここでは使わない
598: { 0x0030, "controller", 0 }, // [1] BOOT (next は動的判定する)
599: { 0x0090, "drive unit", 3 }, // [2] DKUNIT
600: { 0x00b0, "partition ", 4 }, // [3] DKPART
601: { 0x00d0, "filename ", 0 }, // [4] FLNAME
602: { 0x0050, "hostname ", 6 }, // [5] HOST
603: { 0x0070, "servername", 7 }, // [6] SERVER
604: { 0x0190, "filename ", 8 }, // [7] ETBOOT
605: { 0x01b0, "", 0 }, // [8] ETFILE (ETBOOT と1行で処理する)
606: };
607:
608: // "k" コマンド
609: void
1.1.1.2 root 610: LunaPROMEmuDevice::CommandK(const std::vector<std::string>& arg)
1.1 root 611: {
612: uint32 offset;
613: const char *name;
614: int next;
615:
616: // 入力を処理
617: if (prompt_idx == 0) {
618: // ここは1行目表示前なので前行の入力はない。
619: next = 1;
620: } else {
621: std::string val;
622: bool rv;
623:
624: // 入力があれば更新
625: if (arg.size() != 0) {
626: rv = CommandKInput(arg[0]);
627: } else {
628: // [7] の時に入力があれば CommandKInput() の中で更新されるが、
629: // 入力がなければそこを通らないので、こっちでやらないといけない。
630: // うーん。
631: if (prompt_idx == 7) {
632: prompt_idx = kinfo[prompt_idx].next;
633: }
634: rv = true;
635: }
636:
637: // これ以降は更新後の prompt_idx を使う
638:
639: // 入力エラーがあった時はこの行を再入力するため
640: next = prompt_idx;
641:
642: if (rv == true) {
643: // 入力があっても空でも次インデックスへ。
644: if (prompt_idx == 1) {
645: // BOOT なら現在値によって次のインデックスが変わる
646: // "dk" なら [2]、"et" なら [5]。他は未対応
647: offset = kinfo[prompt_idx].offset;
648: val = nvram->PeekString(offset);
649: if (val == "dk") {
650: next = 2;
651: } else if (val == "et") {
652: next = 5;
653: } else {
654: // 起きることはないはず
655: text.Print("** Inconsistent boot parameter: %s\n",
656: val.c_str());
657: }
658: } else {
659: // それ以外はテーブルにかかれている通り
660: next = kinfo[prompt_idx].next;
661: }
662: }
663: }
664:
665: // この行のプロンプト文字列を作成
666: prompt_idx = next;
667: if (prompt_idx == 0) {
668: // 最終行なら元に戻す
669: prompt = default_prompt;
670: } else {
671: // 次のプロンプトに変更
672: name = kinfo[prompt_idx].name;
673: offset = kinfo[prompt_idx].offset;
674: std::string val = nvram->PeekString(offset);
675:
676: // [7] ETBOOT だけ1行で ETBOOT:ETFILE 形式
677: if (prompt_idx == 7) {
678: next = kinfo[prompt_idx].next; // ETFILE
679: offset = kinfo[next].offset;
680: val += ":";
681: val += nvram->PeekString(offset);
682: }
683:
684: prompt = string_format("%s: %s ?", name, val.c_str());
685: }
686: }
687:
688: // k コマンドの入力値を処理する部分。
689: // 成功すれば true を返す。
690: // 失敗すれば false を返す。
691: // 実機 ROM は入力値にエラーがあると黙って再入力を促す。
692: // エラーメッセージくらい表示してもいいかもだがとりあえず真似しておく。
693: bool
694: LunaPROMEmuDevice::CommandKInput(const std::string& val)
695: {
696: int offset;
697: bool rv = true;
698:
699: offset = kinfo[prompt_idx].offset;
700:
701: switch (prompt_idx) {
702: case 1: // BOOT
703: // 正常な入力値は "dk", "et", "sd", "fl" のみ。
704: if (val != "dk" && val != "et" && val != "sd" && val != "fl") {
705: return false;
706: }
707: rv = nvram->WriteString(offset, val);
708: if (rv == false) {
709: return false;
710: }
711: break;
712:
713: case 7: // ETBOOT
714: {
715: // こいつだけ ETBOOT:ETFILE 形式で2つのパラメータを同時に処理する
716:
717: // ':' で前後に分ける
718: int sep = val.find(':');
719: if (sep == std::string::npos) {
720: return false;
721: }
722: std::string etboot = val.substr(0, sep);
723: std::string etfile = val.substr(sep + 1, val.size() - sep + 1);
724: if (etboot.empty() || etfile.empty()) {
725: return false;
726: }
727:
728: // ETBOOT
729: rv = nvram->WriteString(offset, etboot);
730: if (rv == false) {
731: return false;
732: }
733:
734: // 次へ
735: prompt_idx = kinfo[prompt_idx].next;
736:
737: // ETFILE
738: offset = kinfo[prompt_idx].offset;
739: rv = nvram->WriteString(offset, etfile);
740: if (rv == false) {
741: return false;
742: }
743: break;
744: }
745:
746: default:
747: rv = nvram->WriteString(offset, val);
748: if (rv == false) {
749: return false;
750: }
751: break;
752: }
753:
754: WriteNVRAMCsum();
755: return rv;
756: }
757:
758: // "x" コマンド
759: // すでに読み込まれてるはずのエントリポイントにジャンプするだけ。
760: uint32
1.1.1.2 root 761: LunaPROMEmuDevice::CommandX()
1.1 root 762: {
763: if (entrypoint == 0) {
764: text.Print("** Program is not loaded.\n");
765: }
766: return entrypoint;
767: }
768:
769: // NVRAM のチェックサムを返す
770: uint8
771: LunaPROMEmuDevice::CalcNVRAMCsum()
772: {
773: uint8 eor = 0;
774: for (uint32 addr = 0x20; addr < 0x560; addr++) {
775: eor ^= nvram->Peek(addr);
776: }
777: return eor;
778: }
779:
780: // NVRAM のチェックサムを計算して書き込む
781: void
782: LunaPROMEmuDevice::WriteNVRAMCsum()
783: {
784: uint8 eor = CalcNVRAMCsum();
785:
786: nvram->Write(0x001c, eor);
787: nvram->Write(0x001d, eor);
788: nvram->Write(0x001e, eor);
789: }
790:
791: // NVRAM のマジックとチェックサムを照合する
792: bool
793: LunaPROMEmuDevice::VerifyNVRAM()
794: {
795: if (nvram->PeekString(0x0004) != "<nv>") {
796: return false;
797: }
798:
799: uint8 eor = CalcNVRAMCsum();
800: for (uint32 addr = 0x1c; addr < 0x1f; addr++) {
801: if (nvram->Peek(addr) != eor) {
802: return false;
803: }
804: }
805: return true;
806: }
807:
808: // NVRAM を初期化する
809: bool
810: LunaPROMEmuDevice::InitNVRAM()
811: {
812: // 初期値どうしたもんか
813: nvram->WriteString(0x0004, "<nv>");
814: nvram->WriteString(0x0020, "BOOT");
815: nvram->WriteString(0x0030, "dk");
816: nvram->WriteString(0x0040, "HOST");
817: nvram->WriteString(0x0050, "nono"); // 偽札的なアレ
818: nvram->WriteString(0x0060, "SERVER");
819: nvram->WriteString(0x0070, "servername");
820: nvram->WriteString(0x0080, "DKUNIT");
821: nvram->WriteString(0x0090, "0");
822: nvram->WriteString(0x00a0, "DKPART");
823: nvram->WriteString(0x00b0, "c");
824: nvram->WriteString(0x00c0, "DKFILE");
825: nvram->WriteString(0x00d0, "vmunix");
826: nvram->WriteString(0x00e0, "FLUNIT");
827: nvram->WriteString(0x00f0, "2");
828: nvram->WriteString(0x0100, "FLFILE");
829: nvram->WriteString(0x0110, "vmunix");
830: nvram->WriteString(0x0120, "STUNIT");
831: nvram->WriteString(0x0130, "0");
832: nvram->WriteString(0x0140, "STFLNO");
833: nvram->WriteString(0x0150, "1");
834: nvram->WriteString(0x0160, "STFILE");
835: nvram->WriteString(0x0170, "vmunix");
836: nvram->WriteString(0x0180, "ETBOOT");
837: nvram->WriteString(0x0190, "server");
838: nvram->WriteString(0x01a0, "ETFILE");
839: nvram->WriteString(0x01b0, "/vmunix");
840: nvram->WriteString(0x0560, "ENADDR");
841: nvram->WriteString(0x0570, ""); // どうするか
842:
843: // チェックサム再計算
844: WriteNVRAMCsum();
845:
846: return true;
847: }
848:
849: // キーコードから文字への変換
850: /*static*/ const uint8
851: LunaPROMEmuDevice::lunakey2asciitable[] = {
852: 0, 0, 0, 0, 0, 0, 0, 0,
853: 0, 0, 0, 0, 0, 0, 0, 0,
854: 0, 0, 0, 0, ' ', 0, 0, 0,
855: 0, 0, 0, 0, 0, 0, 0, 0,
856: 0, 0, '1', '2', '3', '4', '5', '6',
857: '7', '8', '9', '0', '-', '^', '\\', 0,
858: 0, 0, 'q', 'w', 'e', 'r', 't', 'y',
859: 'u', 'i', 'o', 'p', '@', '[', 0, 0,
860: 0, 0, 'a', 's', 'd', 'f', 'g', 'h',
861: 'j', 'k', 'l', ';', ':', ']', 0, 0,
862: 0, 0, 'z', 'x', 'c', 'v', 'b', 'n',
863: 'm', ',', '.', '/', 0, 0, 0, 0,
864: 0, 0, 0, 0, 0, 0, 0, 0, // XXX テンキー
865: 0, 0, 0, 0, 0, 0, 0, 0,
866: 0, 0, 0, 0, 0, 0, 0, 0,
867: 0, 0, 0, 0, 0, 0, 0, 0,
868: };
869: /*static*/ const uint8
870: LunaPROMEmuDevice::lunakey2shifttable[] = {
871: 0, 0, 0, 0, 0, 0, 0, 0,
872: 0, 0, 0, 0, 0, 0, 0, 0,
873: 0, 0, 0, 0, ' ', 0, 0, 0,
874: 0, 0, 0, 0, 0, 0, 0, 0,
875: 0, 0, '1', '2', '3', '4', '5', '6',
876: '7', '8', '9', '0', '=', '~', '|', 0,
877: 0, 0, 'Q', 'W', 'E', 'R', 'T', 'Y',
878: 'U', 'I', 'O', 'P', '`', '{', 0, 0,
879: 0, 0, 'A', 'S', 'D', 'F', 'G', 'H',
880: 'J', 'K', 'L', '+', '*', '}', 0, 0,
881: 0, 0, 'Z', 'X', 'C', 'V', 'B', 'N',
882: 'M', '<', '>', '?', '_', 0, 0, 0,
883: 0, 0, 0, 0, 0, 0, 0, 0, // XXX テンキー
884: 0, 0, 0, 0, 0, 0, 0, 0,
885: 0, 0, 0, 0, 0, 0, 0, 0,
886: 0, 0, 0, 0, 0, 0, 0, 0,
887: };
888:
889:
890: //
891: // UFS ブート
892: //
893:
894: // ディスクラベル (1セクタ目)
895: // svnweb.freebsd.org/csrg/sys/luna68k/stand/omron_disklabel.h
896: struct scd_dk_label
897: {
898: char asciilabel[128]; /* for compatibility */
899: char pad[512 - (128 + 8*8 + 11*2 + 4)];
900: uint16 badchk; /* checksum of bad track */
901: uint32 maxblk; /* # of total logical block */
902: uint16 dtype; /* disk drive type */
903: uint16 ndisk; /* # of disk drives */
904: uint16 ncyl; /* # of data cylinders */
905: uint16 acyl; /* # of altenate cylinders */
906: uint16 nhead; /* # of heads in this partition */
907: uint16 nsect; /* # of 512 byte sectors per track */
908: uint16 bhead; /* identifies proper label locations */
909: uint16 ppart; /* physical partition # */
910: struct { /* logical partitions */
911: uint32 blkno; /* starting block */
912: uint32 nblk; /* number of blocks */
913: } map[8];
914: uint16 magic; /* identifies this label format */
915: uint16 cksum; /* xor checksum of sector */
916:
917: static const uint16 DKL_MAGIC = 0xdabe;
918: };
919: static_assert(sizeof(scd_dk_label) == 512, "size must be 512");
920:
921: // オレオレ inode 情報クラス
1.1.1.2 root 922: class inodefile final
1.1 root 923: {
924: public:
925: inodefile(Device *dev) {
926: parent = dev;
927: }
1.1.1.2 root 928: ~inodefile() { }
1.1 root 929:
930: // ディレクトリエントリからファイルを検索
931: uint32 FindFile(const std::string& name);
932:
933: uint32 ino {}; // inode 番号
934: struct ufs1_dinode di {}; // disk inode 情報 (ゲストエンディアンで保持)
935: std::vector<uint8> data {}; // ファイル(またはディレクトリ)本文
936: private:
937: Device *parent {}; // 親デバイス
938: };
939:
940: // ファイルシステムハンドルっぽいもの
1.1.1.2 root 941: class Filesys final
1.1 root 942: {
943: public:
944: Filesys(Device *dev) {
945: parent = dev;
946: fs = &fs0;
947: }
1.1.1.2 root 948: ~Filesys() { }
1.1 root 949:
950: // ファイルシステムをオープン
951: bool Open(SCSIHD *hd_, uint32 start, uint32 size);
952: // inode 情報を読み込む
953: bool Readi(inodefile& inode);
954: // データを読み込む
955: bool ReadData(inodefile& inode);
956:
957: std::string errstr{}; // エラーメッセージ
958:
959: private:
960: Device *parent {}; // 親デバイス
961: SCSIHD *hd {}; // ターゲット SCSI HD
962: // パーティション情報
963: uint32 part_start {}; // パーティションの先頭セクタ
964: uint32 part_size {}; // パーティションのサイズ(セクタ数)
965: // スーパーブロック情報
966: // fs の(たぶんほとんどの)整数パラメータは読み込み時にホストエンディアン
967: // に変換してあるのでそのままアクセスしてよい。構造体とかまでは全部対応
968: // してないので、必要になったら書き足すこと。
969: struct fs *fs; // 体裁を揃えるためポインタを使いたい
970: struct fs fs0; // こっちが実体
971: };
972:
973: // NVRAM の設定に従って起動先を探す。
974: // fname が指定されればそれをファイル名に使う。empty なら NVRAM の DKFILE
975: // をファイル名に使う。
976: // 成功すればエントリポイントを返す。
977: // 失敗ならをエラーメッセージを errmsg にセットして 0 を返す。
978: uint32
979: LunaPROMEmuDevice::LoadByNVRAM(const std::string& fname)
980: {
981: // 起動先情報を NVRAM (mk48t02) に問い合わせる
982: // DKUNIT はとりあえず無視して SCSI ID=6
983: // DKPART は 'a'..'h' でパーティション。
984: // DKFILE が読み込むカーネル名 (NetBSD ではブートローダ)。
985: int dkunit = nvram->Peek(0x0090) - '0';
986: int dkpart = nvram->Peek(0x00b0) - 'a';
987: std::string dkfile_name;
988: if (!fname.empty()) {
989: dkfile_name = fname;
990: } else {
991: dkfile_name = nvram->PeekString(0x00d0);
992: }
993: putmsg(1, "%s DKUNIT=%d", __func__, dkunit);
994: putmsg(1, "%s DKPART=%c", __func__, dkpart + 'a');
995: putmsg(1, "%s DKFILE=|%s|", __func__, dkfile_name.c_str());
996:
997: // ユニット (今は SCSI ID=6 決め打ち)
998: int id = 6;
999: SCSITarget *target = gSPC->GetTarget(id);
1000: if (target == NULL) {
1001: errmsg = "No bootable disks";
1002: putmsg(0, "%s", errmsg.c_str());
1003: return 0;
1004: }
1.1.1.3 root 1005: if (target->GetDevType() != SCSI::DevType::HD) {
1.1 root 1006: errmsg = "No bootable disks";
1007: putmsg(0, "%s", errmsg.c_str());
1008: return 0;
1009: }
1010:
1011: SCSIHD *hd = dynamic_cast<SCSIHD*>(target);
1012: putmsg(2, "%s SCSIHD[%d] found", __func__, id);
1013:
1014: // +0 セクタ目(512byte)に Sun disklabel
1015: scd_dk_label dk;
1016: if (hd->PeekImage(&dk, 0, sizeof(dk)) == false) {
1017: putmsg(0, "SCSIHD[%d] ReadSector failed", id);
1018: errmsg = string_format("dk%d Read disklabel failed.", dkunit);
1019: return 0;
1020: }
1021:
1022: // +$1fc の MAGIC をチェック
1023: uint32 magic = be16toh(dk.magic);
1024: if (magic != scd_dk_label::DKL_MAGIC) {
1025: putmsg(0, "SCSIHD[%d] Bad magic 0x%04x (!= 0x%x)",
1026: id, magic, scd_dk_label::DKL_MAGIC);
1027: errmsg = string_format("dk%d Bad disklabel magic.", dkunit);
1028: return 0;
1029: }
1030:
1031: // チェックサム照合は手抜きで省略
1032:
1033: // 指定パーティションの開始位置とサイズ
1034: if (loglevel >= 2) {
1035: // デバッグ用に全部表示
1036: for (int i = 0; i < 8; i++) {
1037: putmsg("%s partition %c: { start=%8u size=%8u }", __func__,
1038: 'a' + i,
1039: be32toh(dk.map[i].blkno),
1040: be32toh(dk.map[i].nblk));
1041: }
1042: }
1043: uint32 part_start = be32toh(dk.map[dkpart].blkno);
1044: uint32 part_size = be32toh(dk.map[dkpart].nblk);
1045: putmsg(1, "%s partition=%c, start=%u size=%u", __func__,
1046: dkpart + 'a', part_start, part_size);
1047:
1048: // ファイルシステムをオープン (マウントっぽいイメージ)
1049: Filesys fsys(this);
1050: if (fsys.Open(hd, part_start, part_size) == false) {
1051: putmsg(0, "SCSIHD[%d] %c: %s", id, dkpart + 'a', fsys.errstr.c_str());
1052: errmsg = string_format("dk%d Open ffs failed.", dkunit);
1053: return 0;
1054: }
1055:
1056: // XXX 以降エラーメッセージ全部同じ
1057: errmsg = string_format("dk%d Read ffs failed.", dkunit);
1058:
1059: std::vector<std::string> dkfile_paths;
1060: int idx = 0;
1061: inodefile dir(this);
1062: inodefile dkfile(this);
1063:
1064: enum {
1065: STATE_REG, // 通常ファイル (終了条件)
1066: STATE_PATH, // パス解析
1067: STATE_DIR, // ディレクトリ読み込み
1068: STATE_FIND, // ディレクトリからファイルを検索
1069: STATE_LINK, // シンボリックリンク
1070: } state;
1071:
1072: // DKFILE の先頭に '/' がなければここで足しておく。
1073: // ファイル名を分解する際にルートディレクトリマークが必要なので。
1074: if (dkfile_name[0] != '/') {
1075: dkfile_name.insert(0, 1, '/');
1076: }
1077: state = STATE_PATH;
1078:
1079: while (state != STATE_REG) {
1080: switch (state) {
1081: case STATE_PATH:
1082: // ファイル名をパスに分解
1083: // dkfile_name にパス名をセットしてからここに来ること。
1084: putmsg(1, "%s Path dkfile_name=|%s|", __func__,
1085: dkfile_name.c_str());
1086: if (dkfile_name.empty()) {
1087: putmsg(0, "SCSIHD[%d] %c: Filename empty?", id, dkpart + 'a');
1088: return 0;
1089: }
1090: dkfile_paths.clear();
1091: for (int s = 0; s < dkfile_name.size(); ) {
1092: int p = dkfile_name.find('/', s);
1093: if (p == std::string::npos) {
1094: p = dkfile_name.size();
1095: }
1096: // '/' が連続していなければパスコンポーネント
1097: if (p != s) {
1098: dkfile_paths.push_back(dkfile_name.substr(s, p - s));
1099: }
1100: s = p + 1;
1101: }
1102: idx = 0;
1103: if (loglevel >= 1) {
1104: for (int i = 0; i < dkfile_paths.size(); i++) {
1105: putmsg("%s dkfile_path[%d]=|%s|",
1106: __func__, i, dkfile_paths[i].c_str());
1107: }
1108: }
1109: // パスが '/' から始まっていればルートディレクトリの読み込みから。
1110: // そうでなければ現在のディレクトリでファイルの検索から。
1111: if (dkfile_name[0] == '/') {
1112: // ルートディレクトリの inode 番号は伝統的に #2 と
1113: // いうことになっている。
1114: dir.ino = 2;
1115: state = STATE_DIR;
1116: } else {
1117: state = STATE_FIND;
1118: }
1119: break;
1120:
1121: case STATE_DIR:
1122: // inode 番号からディレクトリエントリを取得。
1123: // dir.ino をセットしてからここに来ること。
1124: putmsg(1, "%s Readdir ino=%d", __func__, dir.ino);
1125: if (fsys.Readi(dir) == false) {
1126: putmsg(0, "SCSIHD[%d] %c: %s",
1127: id, dkpart + 'a', fsys.errstr.c_str());
1128: return 0;
1129: }
1130: // 中身(ディレクトリエントリ) を取得
1131: if (fsys.ReadData(dir) == false) {
1132: putmsg(0, "SCSIHD[%d] %c: %s",
1133: id, dkpart + 'a', fsys.errstr.c_str());
1134: return 0;
1135: }
1136: state = STATE_FIND;
1137: break;
1138:
1139: case STATE_FIND:
1140: // ディレクトリエントリ dir.data から
1141: // ファイル名 dkfile_paths[idx] を探す。
1142: putmsg(1, "%s FindFile %s", __func__, dkfile_paths[idx].c_str());
1143: dkfile.ino = dir.FindFile(dkfile_paths[idx]);
1144: if (dkfile.ino == 0) {
1145: putmsg(0, "SCSIHD[%d] %c: %s",
1146: id, dkpart + 'a', fsys.errstr.c_str());
1147: return 0;
1148: }
1149: // ファイルの inode 情報を取得。
1150: if (fsys.Readi(dkfile) == false) {
1151: putmsg(0, "SCSIHD[%d] %c: %s",
1152: id, dkpart + 'a', fsys.errstr.c_str());
1153: return 0;
1154: }
1155: switch (be16toh(dkfile.di.di_mode) & IFMT) {
1156: case IFREG: // 通常ファイル
1157: state = STATE_REG;
1158: break;
1159:
1160: case IFLNK: // シンボリックリンク
1161: state = STATE_LINK;
1162: break;
1163:
1164: case IFDIR: // ディレクトリ
1165: dir.ino = dkfile.ino;
1166: idx++;
1167: state = STATE_DIR;
1168: break;
1169:
1170: default: // それ以外は対応してないし来ないはず
1171: putmsg(0, "SCSIHD[%d] %c:%s invalid filemode %06o",
1172: id, dkpart + 'a', dkfile_name.c_str(),
1173: be16toh(dkfile.di.di_mode));
1174: return 0;
1175: }
1176: break;
1177:
1178: case STATE_LINK:
1179: // リンク先ファイル名を取り出す
1180: putmsg(1, "%s Readlink", __func__);
1181: if (fsys.ReadData(dkfile) == false) {
1182: return 0;
1183: }
1184: dkfile_name = std::string((const char *)dkfile.data.data(),
1185: dkfile.data.size());
1186: // パス解析へ
1187: state = STATE_PATH;
1188: break;
1189:
1190: default:
1191: __unreachable();
1192: }
1193: }
1194: // 通常ファイルに到達
1195:
1196: if (idx != dkfile_paths.size() - 1) {
1197: // パスの最後のコンポーネントじゃなければエラー
1198: putmsg(0, "SCSIHD[%d] %c:%s path not found",
1199: id, dkpart + 'a', dkfile_name.c_str());
1200: return 0;
1201: }
1202: // ファイル本体を読み込み
1203: if (fsys.ReadData(dkfile) == false) {
1204: putmsg(0, "SCSIHD[%d] %c: %s", id, dkpart + 'a', fsys.errstr.c_str());
1205: return 0;
1206: }
1207:
1208: // ロード
1209: uint32 entry;
1210: std::string name = string_format("SCSIHD %d,%c:%s",
1211: id, dkpart + 'a', dkfile_name.c_str());
1212: entry = gRAM->LoadFile(name.c_str(), dkfile.data.data(),
1213: dkfile.data.size(), AOUT_MID_M68K);
1214:
1215: return entry;
1216: }
1217:
1218: // パーティションを FFS としてオープンする(マウントのようなイメージ)。
1219: // start, size はこのパーティションの開始セクタとセクタ数。
1220: bool
1221: Filesys::Open(SCSIHD *hd_, uint32 start, uint32 size)
1222: {
1223: hd = hd_;
1224: part_start = start;
1225: part_size = size;
1226:
1227: // FFS の先頭にはブートブロックがあり、続けてスーパーブロックがある。
1228: // ブートブロックはブートローダのための領域で、ここもファイルシステムの
1229: // 内側という認識のようだ。
1230: //
1231: // ブートブロックの大きさはおそらくファイルシステムごとに固定で、
1232: // FFSv1(?) なら 8KB のようだ。ここは LUNA-I の ROM が解釈できるのと
1233: // 同じファイルシステムだけ扱えればよいので、8KB 固定とする。
1234: // 続いてスーパーブロックがある。ここにこのファイルシステムのパラメータ
1235: // が書いてある。
1236: //
1237: // +-------------+ 0 <- part_start (パーティション先頭)
1238: // | BootBlock |
1239: // +-------------+ 8KB
1240: // | SuperBlock |
1241: // | fs_iblkno ---+
1242: // +-------------+ |
1243: // : : |
1244: // |
1245: // : : |
1246: // +-------------+ <-+
1247: // | InodeBlock? |
1248: //
1249: // スーパーブロックの構造は <ufs/ffs/fs.h> の struct fs 参照。
1250:
1251: // スーパーブロックを読む
1252: hd->PeekImage(&fs0, part_start * 512 + 8192, sizeof(fs0));
1253: // 読み込んだスーパーブロックはゲストエンディアン(BE) なので
1254: // 使う前に (もう面倒なのでほぼ全部) ホストエンディアンにしておく。
1255: // ヘッダに定義してあるマクロをそのまま使いたいからだけど、正気かなこれ。
1256: fs->fs_firstfield = be32toh(fs->fs_firstfield);
1257: fs->fs_sblkno = be32toh(fs->fs_sblkno);
1258: fs->fs_cblkno = be32toh(fs->fs_cblkno);
1259: fs->fs_iblkno = be32toh(fs->fs_iblkno);
1260: fs->fs_dblkno = be32toh(fs->fs_dblkno);
1261: fs->fs_old_cgoffset = be32toh(fs->fs_old_cgoffset);
1262: fs->fs_old_cgmask = be32toh(fs->fs_old_cgmask);
1263: fs->fs_old_time = be32toh(fs->fs_old_time);
1264: fs->fs_old_size = be32toh(fs->fs_old_size);
1265: fs->fs_old_dsize = be32toh(fs->fs_old_dsize);
1266: fs->fs_ncg = be32toh(fs->fs_ncg);
1267: fs->fs_bsize = be32toh(fs->fs_bsize);
1268: fs->fs_fsize = be32toh(fs->fs_fsize);
1269: fs->fs_frag = be32toh(fs->fs_frag);
1270: fs->fs_minfree = be32toh(fs->fs_minfree);
1271: fs->fs_old_rotdelay = be32toh(fs->fs_old_rotdelay);
1272: fs->fs_old_rps = be32toh(fs->fs_old_rps);
1273: fs->fs_bmask = be32toh(fs->fs_bmask);
1274: fs->fs_fmask = be32toh(fs->fs_fmask);
1275: fs->fs_bshift = be32toh(fs->fs_bshift);
1276: fs->fs_fshift = be32toh(fs->fs_fshift);
1277: fs->fs_maxcontig = be32toh(fs->fs_maxcontig);
1278: fs->fs_maxbpg = be32toh(fs->fs_maxbpg);
1279: fs->fs_fragshift = be32toh(fs->fs_fragshift);
1280: fs->fs_fsbtodb = be32toh(fs->fs_fsbtodb);
1281: fs->fs_sbsize = be32toh(fs->fs_sbsize);
1282: //fs->fs_spare1[2]
1283: fs->fs_nindir = be32toh(fs->fs_nindir);
1284: fs->fs_inopb = be32toh(fs->fs_inopb);
1285: fs->fs_old_nspf = be32toh(fs->fs_old_nspf);
1286: fs->fs_optim = be32toh(fs->fs_optim);
1287: fs->fs_old_npsect = be32toh(fs->fs_old_npsect);
1288: fs->fs_old_interleave = be32toh(fs->fs_old_interleave);
1289: fs->fs_old_trackskew = be32toh(fs->fs_old_trackskew);
1290: fs->fs_id[0] = be32toh(fs->fs_id[0]);
1291: fs->fs_id[1] = be32toh(fs->fs_id[1]);
1292: fs->fs_old_csaddr = be32toh(fs->fs_old_csaddr);
1293: fs->fs_cssize = be32toh(fs->fs_cssize);
1294: fs->fs_cgsize = be32toh(fs->fs_cgsize);
1295: fs->fs_spare2 = be32toh(fs->fs_spare2);
1296: fs->fs_old_nsect = be32toh(fs->fs_old_nsect);
1297: fs->fs_old_spc = be32toh(fs->fs_old_spc);
1298: fs->fs_old_ncyl = be32toh(fs->fs_old_ncyl);
1299: fs->fs_old_cpg = be32toh(fs->fs_old_cpg);
1300: fs->fs_ipg = be32toh(fs->fs_ipg);
1301: fs->fs_fpg = be32toh(fs->fs_fpg);
1302: //struct csum fs_old_cstotal
1303: //u_char fs_fsmnt[MAXMNTLEN]
1304: //u_char fs_volname[MAXVOLLEN]
1305: fs->fs_swuid = be64toh(fs->fs_swuid);
1306: fs->fs_pad = be32toh(fs->fs_pad);
1307: fs->fs_cgrotor = be32toh(fs->fs_cgrotor);
1308: //void *fs_ocsp[NOCSPTRS]
1309: //struct csum *fs_csp
1310: //uint32_t *fs_maxcluster
1311: //u_char *fs_active
1312: fs->fs_old_cpc = be32toh(fs->fs_old_cpc);
1313: fs->fs_maxbsize = be32toh(fs->fs_maxbsize);
1314: fs->fs_journal_flags = be32toh(fs->fs_journal_flags);
1315: //uint64_t fs->fs_journallocs[4]
1316: fs->fs_quota_magic = be32toh(fs->fs_quota_magic);
1317: //uint64_t fs->fs_quotafile[2]
1318: //uint64_t fs->fs_sparecon64[9]
1319: fs->fs_sblockloc = be64toh(fs->fs_sblockloc);
1320: //struct csum_total fs_cstotal
1321: fs->fs_time = be64toh(fs->fs_time);
1322: fs->fs_size = be64toh(fs->fs_size);
1323: fs->fs_dsize = be64toh(fs->fs_dsize);
1324: fs->fs_csaddr = be64toh(fs->fs_csaddr);
1325: fs->fs_pendingblocks = be64toh(fs->fs_pendingblocks);
1326: fs->fs_pendinginodes = be32toh(fs->fs_pendinginodes);
1327: // uint32_t fs->fs_snapinum[FSMAXSNAP]
1328: fs->fs_avgfilesize = be32toh(fs->fs_avgfilesize);
1329: fs->fs_avgfpdir = be32toh(fs->fs_avgfpdir);
1330: fs->fs_save_cgsize = be32toh(fs->fs_save_cgsize);
1331: // uint32_t fs->fs_sparecon32[26]
1332: fs->fs_flags = be32toh(fs->fs_flags);
1333: fs->fs_contigsumsize = be32toh(fs->fs_contigsumsize);
1334: fs->fs_maxsymlinklen = be32toh(fs->fs_maxsymlinklen);
1335: fs->fs_old_inodefmt = be32toh(fs->fs_old_inodefmt);
1336: fs->fs_maxfilesize = be64toh(fs->fs_maxfilesize);
1337: fs->fs_qbmask = be64toh(fs->fs_qbmask);
1338: fs->fs_qfmask = be64toh(fs->fs_qfmask);
1339: fs->fs_state = be32toh(fs->fs_state);
1340: fs->fs_old_postblformat = be32toh(fs->fs_old_postblformat);
1341: fs->fs_old_nrpos = be32toh(fs->fs_old_nrpos);
1342: // uint32_t fs->fs_spare5[2]
1343: fs->fs_magic = be32toh(fs->fs_magic);
1344:
1345: // マジック
1346: if (fs->fs_magic != FS_UFS1_MAGIC) {
1347: errstr = string_format("Bad superblock magic 0x%08x (!= 0x%08x)",
1348: fs->fs_magic, FS_UFS1_MAGIC);
1349: return false;
1350: }
1351:
1352: // なんとなく主要なパラメータを表示してみるか
1353: if (parent->loglevel >= 2) {
1354: uint32 sblkno = fs->fs_sblkno; // super block in filesys
1355: uint32 cblkno = fs->fs_cblkno; // cylinder group block in filesys
1356: uint32 iblkno = fs->fs_iblkno; // inode block in filesys
1357: uint32 dblkno = fs->fs_dblkno; // data block in filesys
1358: uint32 bsize = fs->fs_bsize; // size of basic blocks
1359: uint32 ipg = fs->fs_ipg; // inodes per group
1360: uint32 fpg = fs->fs_fpg; // blocks per group x fs_frag
1361: uint32 old_cgoffset = fs->fs_old_cgoffset; // cg. offset in cyl.
1362:
1363: parent->putmsg("%s fs.fs_sblkno=0x%x (0x%x)",
1364: __func__, sblkno, sblkno * 512);
1365: parent->putmsg("%s fs.fs_cblkno=0x%x (0x%x)",
1366: __func__, cblkno, cblkno * 512);
1367: parent->putmsg("%s fs.fs_dblkno=0x%x (0x%x)",
1368: __func__, dblkno, dblkno * 512);
1369: parent->putmsg("%s fs.fs_iblkno=0x%x (0x%x)",
1370: __func__, iblkno, iblkno * 512);
1371: parent->putmsg("%s fs.fs_bsize=0x%x", __func__, bsize);
1372: parent->putmsg("%s fs.fs_ipg=0x%x", __func__, ipg);
1373: parent->putmsg("%s fs.fs_fpg=0x%x", __func__, fpg);
1374: parent->putmsg("%s fs.fs_old_cgoffset=0x%x", __func__, old_cgoffset);
1375: }
1376:
1377: return true;
1378: }
1379:
1380: // inode.ino で指定された disk inode 情報を inode.di に返す
1381: bool
1382: Filesys::Readi(inodefile& inode)
1383: {
1384: // di は inode のディスク上の記録構造で 128 バイト長。
1385: // 詳細は <ufs/ufs/dinode.h> の ufs1_dinode 参照。
1386: // 基本的には inode ブロックにこの dinode が配列のように並んでいるが
1387: // シリンダグループを越えるためには謎のマクロを使う。
1388: // sys/lib/libsa/ufs.c 参照。
1389: //
1390: // dinode のうち、ここで関係するかも知れないのは
1391: // di_mode (のうちファイルモード)
1392: // di_size (ファイルサイズ)
1393: // di_db[] (直接ブロック)
1394: // di_ib[] (間接ブロック)
1395: // di_blocks (ブロック数)
1396: // くらいか。
1397:
1398: uint32 ino = inode.ino;
1399: struct ufs1_dinode *di = &inode.di;
1400:
1401: // ino が含まれるブロックを読み込む
1.1.1.2 root 1402: std::vector<char> buf(fs->fs_bsize);
1.1 root 1403: uint32 ino_sector = FFS_FSBTODB(fs, ino_to_fsba(fs, ino));
1.1.1.2 root 1404: hd->PeekImage(&buf[0], part_start + ino_sector * 512, buf.size());
1.1 root 1405:
1406: // ino で示される inode を取得
1.1.1.2 root 1407: *di = ((ufs1_dinode *)(&buf[0]))[ino_to_fsbo(fs, ino)];
1.1 root 1408: uint16 di_mode = be16toh(di->di_mode);
1409: uint16 di_nlink = be16toh(di->di_nlink);
1410: uint64 di_size = be64toh(di->di_size);
1411: parent->putmsg(2, "%s di[%d].di_mode=%05o di_nlink=%d di_size=%" PRIu64,
1412: __func__, ino, di_mode, di_nlink, di_size);
1413:
1414: return true;
1415: }
1416:
1417: // inode.di で指定されたファイル(またはディレクトリ)の本文を
1418: // inode.data に返す
1419: bool
1420: Filesys::ReadData(inodefile& inode)
1421: {
1422: // di (ufs1_dinode) にはこの inode (ファイルまたはディレクトリ)の
1423: // データの格納ブロックが記録されている。ディレクトリとは中身が
1424: // ディレクトリエントリであるファイルと同じ構造のもの。
1425: // di_db[] (直接ブロック)
1426: // di_ib[] (間接ブロック)
1427: // di_blocks (実際に使用しているブロック数)
1428: //
1429: // 直接ブロック di_db[] は12要素の配列で、データブロックの位置が
1430: // 記録されている (未使用なら 0 のようだ)。
1431: // データブロックは fs->fs_bsize でたぶん基本 4KB のやつ。
1432: // これもパーティション先頭からのセクタ番号で表記される。なので
1433: // ディスク上で連続しているファイルなら di_db[N] と di_db[N + 1] は
1434: // 8 ずつ増えてるのが見えるはず。
1435: // 例えば以下のサンプル断片は、直接ブロック2つでセクタ 100 から
1436: // セクタ 115 までの連続する 16KB を所有している様子を示している。
1437: //
1438: // #100 #101 #102 #107
1439: // +------+------+------+- .. -+------+
1440: // di_db[0] = 100 ---> | Data | Data | Data | | Data |
1441: // +------+------+------+- .. -+------+
1442: //
1443: // #108 #115
1444: // +------+------+------+- .. -+------+
1445: // di_db[1] = 108 ---> | Data | Data | Data | | Data |
1446: // +------+------+------+- .. -+------+
1447: //
1448: // 間接ブロック di_ib[0], [1], [2] はそれぞれ一次、二次、三次の
1449: // 間接記録になっている。一次間接だとこういう感じ。
1450: //
1451: // #120
1452: // +--------+ +------+- ..
1453: // di_ib[0] = 120 ---> | #200 ------> #200 | Data |
1454: // | | +------+- ..
1455: // | #208 ------> #208 | Data |
1456: // +--------+ +------+- ..
1457: //
1458:
1459: uint32 ino = inode.ino;
1460: struct ufs1_dinode *di = &inode.di;
1461: std::vector<uint8>& data = inode.data;
1462: uint64 bsize64 = (uint64)fs->fs_bsize;
1463:
1464: uint64 di_size = be64toh(di->di_size);
1465: uint32 di_blocks = be32toh(di->di_blocks);
1466: // di->di_db[] にファイル(またはディレクトリ)の内容の位置が並んでいる
1467: if (parent->loglevel >= 2) {
1468: parent->putmsg("%s di[%d].di_blocks=%u", __func__, ino, di_blocks);
1469:
1470: std::string dbstr;
1471: for (int i = 0; i < UFS_NDADDR; i++) {
1472: dbstr += string_format(" %u", be32toh(di->di_db[i]));
1473: }
1474: parent->putmsg("%s di[%d].di_db[]=%s",
1475: __func__, ino, dbstr.c_str() + 1);
1476:
1477: std::string distr;
1478: for (int i = 0; i < UFS_NIADDR; i++) {
1479: distr += string_format(" %u", be32toh(di->di_ib[i]));
1480: }
1481: parent->putmsg("%s di[%d].di_ib[]=%s",
1482: __func__, ino, distr.c_str() + 1);
1483: }
1484:
1485: uint64 offset = 0; // 次に読み込む位置 (ここまでに読み込んだバイト数)
1486:
1487: // サイズが分かっているのであらかじめ確保しておく
1488: data.resize(di_size);
1489:
1490: // 直接ブロック
1491: for (int i = 0; i < countof(di->di_db); i++) {
1492: uint64 remain = di_size - offset; // 残りバイト数
1493: uint32 nbytes = std::min(remain, bsize64); // 今回読み込み分
1494: hd->PeekImage(&data[offset], be32toh(di->di_db[i]) * 512, nbytes);
1495: offset += nbytes;
1496: if (offset >= di_size)
1497: break;
1498: }
1499: // 一次間接ブロック
1.1.1.2 root 1500: std::vector<uint32> ib0buf(fs->fs_bsize / sizeof(uint32));
1501: hd->PeekImage(&ib0buf[0], be32toh(di->di_ib[0]) * 512,
1502: ib0buf.size() * sizeof(uint32));
1.1 root 1503: if (parent->loglevel >= 2) {
1504: std::string distr;
1.1.1.2 root 1505: for (const auto& x : ib0buf) {
1506: if (x == 0)
1.1 root 1507: break;
1.1.1.2 root 1508: distr += string_format(" %u", be32toh(x));
1.1 root 1509: }
1510: parent->putmsg("%s di[%d].di_ib[0]=%s",
1511: __func__, ino, distr.c_str() + 1);
1512: }
1.1.1.2 root 1513: for (const auto& x : ib0buf) {
1.1 root 1514: uint64 remain = di_size - offset; // 残りバイト数
1515: uint32 nbytes = std::min(remain, bsize64); // 今回読み込み分
1.1.1.2 root 1516: hd->PeekImage(&data[offset], be32toh(x) * 512, nbytes);
1.1 root 1517: offset += nbytes;
1518: if (offset >= di_size)
1519: break;
1520: }
1521:
1522: // 二次、三次ブロックは未対応
1523: if (offset < di_size) {
1524: PANIC("di_ib[1] not supported");
1525: }
1526:
1527: return true;
1528: }
1529:
1530: // ディレクトリエントリ dirbuf から name で指定されるファイル名に対応する
1531: // inode 番号を返す。見付からなければ 0 を返す。
1532: uint32
1533: inodefile::FindFile(const std::string& name)
1534: {
1535: // ディレクトリエントリは可変長で並んでいる。
1536: // d->d_reclen がこの1つ分のエントリの長さなので、次のエントリ位置は
1537: // このエントリの頭から d->d_reclen を足したところになる。
1538: // 最終エントリの d_reclen を足すとバッファ末尾になるようになっている
1539: // ようだ。
1540: const char *dp = (const char *)(data.data());
1541: const char *dend = dp + data.size();
1542: uint32 file_ino;
1543: uint16 reclen;
1544:
1545: for (; dp < dend; dp += reclen) {
1546: const struct direct *d = (const struct direct *)dp;
1547: file_ino = be32toh(d->d_fileno);
1548: reclen = be16toh(d->d_reclen);
1549:
1550: std::string str = string_format("ino=%u reclen=%u name=|%s|",
1551: file_ino, reclen, d->d_name);
1552: if (strcmp(name.c_str(), d->d_name) == 0) {
1553: parent->putmsg(2, "%s %s found", __func__, str.c_str());
1.1.1.2 root 1554: return file_ino;
1.1 root 1555: } else {
1556: parent->putmsg(2, "%s %s skip", __func__, str.c_str());
1557: }
1558: }
1.1.1.2 root 1559: parent->putmsg(2, "%s %s not found", __func__, name.c_str());
1560: return 0;
1.1 root 1561: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.