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