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