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