|
|
1.1 root 1: //
2: // nono
1.1.1.3 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.12 root 7: //
8: // CLI/GUI 共通のメイン部分
9: //
10:
1.1 root 11: #include "mainapp.h"
1.1.1.16! root 12: #include "accel_avx2.h"
1.1.1.12 root 13: #include "config.h"
14: #include "hostnet.h"
15: #include "logger.h"
16: #include "monitor.h"
1.1.1.2 root 17: #include "mystring.h"
1.1.1.12 root 18: #include "mythread.h"
1.1.1.14 root 19: #include "sram.h"
1.1.1.12 root 20: #include "vm_luna.h"
1.1.1.14 root 21: #include "vm_news.h"
1.1.1.16! root 22: #include "vm_virt68k.h"
1.1.1.12 root 23: #include "vm_x68k.h"
1.1.1.14 root 24: #include <fcntl.h>
1.1.1.2 root 25: #include <getopt.h>
1.1.1.14 root 26: #include <pwd.h>
1.1.1.16! root 27: #include <sys/param.h>
1.1.1.2 root 28: #include <sys/stat.h>
1.1.1.8 root 29: #include <algorithm>
1.1.1.12 root 30: #if defined(__linux__)
31: #include <sys/prctl.h>
32: #endif
1.1.1.2 root 33:
34: // グローバルインスタンス
35: MainApp gMainApp;
36:
1.1.1.12 root 37: // コンストラクタ
38: MainApp::MainApp()
39: {
40: }
41:
42: // デストラクタ
43: MainApp::~MainApp()
44: {
45: // 逆順に解放
46:
47: pVM.reset();
1.1.1.14 root 48:
49: pConfig.reset();
50: gConfig = NULL;
51:
52: pMonitorManager.reset();
53: gMonitorManager = NULL;
54:
1.1.1.12 root 55: #if 0 // 確認用
56: if (objects.empty() == false) {
57: printf("~MainApp: undead objects are:");
58: for (const auto *obj : objects) {
1.1.1.14 root 59: printf(" %s", obj->GetIdStr());
1.1.1.12 root 60: }
61: printf("\n");
62: }
63: #endif
64:
65: logger.reset();
66: }
67:
1.1.1.16! root 68: // VM を解放。
! 69: void
! 70: MainApp::Dispose()
! 71: {
! 72: if ((bool)pVM) {
! 73: pVM->Dispose();
! 74: }
! 75: }
! 76:
1.1.1.2 root 77: // ヘルプメッセージ
78: void
79: MainApp::ShowHelp(bool all) const
80: {
81: ShowVersion();
82:
83: #define p(msg) printf(" " msg "\n")
84:
85: printf("usage: %s [<options>]\n", getprogname());
1.1.1.6 root 86: p("-c <path> vm directory or configuration file");
1.1.1.14 root 87: p("-f fast mode (same as '-V fast-mode=1')");
1.1.1.2 root 88: if (IsGUI())
1.1.1.14 root 89: p("--fontsize <height> fontsize (same as '-V monitor-fontsize=<height>')");
1.1.1.2 root 90: p("-h show brief help message");
91: p("--help show all help message including for developers");
92: if (IsGUI())
1.1.1.14 root 93: p("-s <scale> mainview scale (same as '-V mainview-scale=<scale>')");
1.1.1.2 root 94: p("--show-config show configuration variables");
1.1.1.8 root 95: p("--show-hostnet show list of hostnet drivers");
1.1.1.2 root 96: p("-v show version");
97: p("-V <key>=<val> overwrite config option");
1.1.1.16! root 98: p("-X <file> load and execute host binary (a.out or ELF)");
! 99: p("--initrd <img> initial ramdisk (only for virt68k)");
1.1.1.2 root 100:
101: if (all) {
102: printf("\n(options for developers)\n");
1.1.1.14 root 103: p("-b [<cpu>,]<addr>[,<skip>]");
104: p(" set breakpoint. <cpu> is either 'main'(default) or 'xp'");
1.1.1.16! root 105: p("--bi-exg set instruction breakpoint on 'exg sp,sp'");
1.1.1.2 root 106: p("-B <rom#> benchmark mode");
107: p("-C output log to console");
108: p("-d debugger prompt on startup");
1.1.1.12 root 109: p("-D same as '-V debugger-driver=stdio'");
1.1.1.10 root 110: p("-H human68k console emulation");
1.1.1.2 root 111: p("-L <name>=<lv>[,..] set loglevel (-Lhelp displays names)");
112: p("-M <name>[,..] monitors to display at startup (-Mhelp displays names)");
1.1.1.14 root 113: p(" memdump<N>[=<hex-addr>[.<fmt>]] fmt := B/W/L/M(MMU)/I(Disasm)/Z(XPDisasm)");
114: p("-S MSX-DOS console emulation");
1.1.1.12 root 115: p("--perf performance measure mode");
1.1.1.2 root 116: }
117: }
118:
119: // long option は enum と struct option をアルファベット順に並べる。
120: // enum は getopt() の1文字のオプションと衝突しなければいいので適当に
121: // 0x80 から始めておく。
122: enum {
1.1.1.14 root 123: OPTstart = 0x80 - 1,
1.1.1.16! root 124: OPT_bi_exg,
1.1.1.14 root 125: OPT_create_sram,
126: OPT_fontsize,
1.1.1.2 root 127: OPT_help,
1.1.1.16! root 128: OPT_initrd,
1.1.1.12 root 129: OPT_perf,
1.1.1.2 root 130: OPT_show_config,
1.1.1.3 root 131: OPT_show_config_all,
1.1.1.8 root 132: OPT_show_hostnet,
1.1.1.2 root 133: };
134: static struct option longopts[] = {
1.1.1.16! root 135: { "bi-exg", no_argument, NULL, OPT_bi_exg },
1.1.1.14 root 136: { "create-sram", no_argument, NULL, OPT_create_sram },
1.1.1.2 root 137: { "fontsize", required_argument, NULL, OPT_fontsize },
138: { "help", no_argument, NULL, OPT_help },
1.1.1.16! root 139: { "initrd", required_argument, NULL, OPT_initrd },
1.1.1.12 root 140: { "perf", no_argument, NULL, OPT_perf },
1.1.1.2 root 141: { "show-config", no_argument, NULL, OPT_show_config },
1.1.1.3 root 142: // --show-config-all は開発用なのでヘルプには載せない
143: { "show-config-all",no_argument, NULL, OPT_show_config_all },
1.1.1.8 root 144: { "show-hostnet", no_argument, NULL, OPT_show_hostnet },
1.1.1.2 root 145: { NULL, 0, NULL, 0 },
146: };
147:
1.1.1.7 root 148: // VM の初期化、ステージ1。
149: // VM 作成と設定確定あたりまで。スレッド生成を伴わないもの。
1.1.1.2 root 150: // 所々 CLI と GUI で処理が違う。
1.1.1.5 root 151: // 戻り値は以下のいずれか。
152: // MainApp::PASS .. 実行を継続(通常パス)
153: // EXIT_SUCCESS / EXIT_FAILURE .. この終了コードでアプリケーションを終了
154: int
1.1.1.7 root 155: MainApp::Init1(bool is_cli_, int ac, char *av[])
1.1.1.2 root 156: {
1.1.1.14 root 157: int rv;
158:
1.1.1.2 root 159: is_cli = is_cli_;
160:
1.1.1.16! root 161: rv = Init1a(ac, av);
! 162: if (rv != MainApp::PASS) {
! 163: return rv;
! 164: }
! 165:
! 166: rv = Init1b();
! 167: return rv;
! 168: }
! 169:
! 170: // VM の初期化、ステージ1 の前半。
! 171: // Config.Fix まで。
! 172: int
! 173: MainApp::Init1a(int ac, char *av[])
! 174: {
! 175: int rv;
! 176:
1.1.1.12 root 177: #if defined(__linux__)
178: // Linux ではケーパビリティが設定されていると coredump しないので
179: // 明示的に許可する必要がある
180: prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
181: #endif
182:
1.1.1.14 root 183: rv = ParseOpt(ac, av);
184: if (rv != MainApp::PASS) {
185: return rv;
1.1.1.2 root 186: }
1.1 root 187:
1.1.1.2 root 188: // ログ機構は引数処理後なるはや
189: // CLI ならログは常に標準出力へ(も)出力。
190: // GUI なら -C で指定。
1.1.1.12 root 191: logger.reset(new Logger());
192: logger->UseStdout(IsCLI() ? true : log_to_console);
193:
194: // モニタマネージャ (VM 作成より前、もしかしたら Config より前?)
195: pMonitorManager.reset(new MonitorManager());
196: gMonitorManager = pMonitorManager.get();
1.1.1.2 root 197:
1.1.1.14 root 198: // 設定を作成。
199: // c0. コンストラクタで初期値を用意。
1.1.1.12 root 200: pConfig.reset(new Config());
201: gConfig = pConfig.get();
1.1.1.2 root 202:
1.1.1.14 root 203: // c1. ホームディレクトリに設定ファイルがあれば読み込んで設定を更新。
204: struct passwd *passwd = getpwuid(getuid());
205: // 見付からないことはないはずだが、一応。
206: if (passwd != NULL) {
207: std::string filename = std::string(passwd->pw_dir) + "/.nono.cfg";
1.1.1.16! root 208: ConfigFile dotfile(filename, ConfigItem::FromHome);
1.1.1.14 root 209: if (dotfile.Load()) {
210: if (gConfig->Update(dotfile) == false) {
211: return EXIT_FAILURE;
212: }
213: }
1.1.1.2 root 214: }
1.1.1.14 root 215:
216: // c2. VM ディレクトリに設定ファイルがあれば読み込んで設定を更新。
217: // vmfile が存在するかどうかはここで調べるまで分からない。
1.1.1.16! root 218: ConfigFile cfgfile(vmfile, ConfigItem::FromConfig);
1.1.1.14 root 219: if (cfgfile.Load()) {
220: if (gConfig->Update(cfgfile) == false) {
221: return EXIT_FAILURE;
222: }
1.1.1.2 root 223: }
1.1.1.14 root 224:
225: // c3. 最後にコマンドライン引数で更新
226: for (const auto& pair : config_options) {
227: if (gConfig->Update(pair.first, pair.second) == false) {
228: return EXIT_FAILURE;
229: }
230: }
231:
1.1.1.16! root 232: // c4. exec-file, exec-arg をここでメンバ変数 exec_file, exec_arg に代入。
! 233: // (c3 までに確定していて、1. VM コンストラクト時には必要)
! 234: const ConfigItem& itemexec = gConfig->Find("exec-file");
! 235: const std::string& execstr = itemexec.AsString();
! 236: if (execstr.empty() == false) {
! 237: // 歴史的経緯で exec_file は const char *。
! 238: exec_file = strdup(NormalizePath(execstr).c_str());
! 239: }
! 240: const ConfigItem& itemarg = gConfig->Find("exec-arg");
! 241: exec_arg = itemarg.AsString();
! 242:
1.1.1.14 root 243: // MSX-DOS モードならここでいくつかパラメータを強制的に変更。
244: if (msxdos_mode) {
245: // LUNA-I 最小構成でいい
246: gConfig->Update("vmtype=luna", "-S");
247: gConfig->Update("ram-size=16", "-S");
248: gConfig->Update("luna-video-plane=1", "-S");
249: // ホスト側には干渉しない。
250: gConfig->Update("hostcom-driver=none", "-S");
251: gConfig->Update("hostnet-driver=none", "-S");
1.1.1.2 root 252: }
1.1 root 253:
1.1.1.16! root 254: // ホスト CPU の機能チェック。
! 255: if (CheckCPU() == false) {
! 256: return EXIT_FAILURE;
! 257: }
! 258:
1.1.1.12 root 259: // 0. VM 種別を決定
1.1.1.9 root 260: // VM 種別文字列から vmtype を決定
261: const ConfigItem& item = gConfig->Find("vmtype");
1.1.1.14 root 262: vmstr = string_tolower(item.AsString());
1.1.1.9 root 263: if (vmstr == "x68030") {
1.1.1.13 root 264: vmtype = VMType::X68030;
1.1.1.9 root 265: } else if (vmstr == "luna") {
1.1.1.13 root 266: vmtype = VMType::LUNA1;
1.1.1.9 root 267: } else if (vmstr == "luna88k") {
1.1.1.13 root 268: vmtype = VMType::LUNA88K;
1.1.1.14 root 269: } else if (vmstr == "news") {
270: vmtype = VMType::NEWS;
1.1.1.16! root 271: } else if (vmstr == "virt68k") {
! 272: vmtype = VMType::VIRT68K;
1.1.1.2 root 273: } else {
1.1.1.9 root 274: if (item.GetFrom() == ConfigItem::FromInitial) {
275: // 未指定の時
276: warnx("vmtype must be specified");
1.1.1.2 root 277: } else {
1.1.1.9 root 278: // ユーザ由来の時
1.1.1.14 root 279: item.Err("Invalid vmtype");
1.1.1.2 root 280: }
1.1.1.9 root 281: return EXIT_FAILURE;
1.1.1.2 root 282: }
1.1 root 283:
1.1.1.14 root 284: // 0.5. VM が確定したところで SRAM 作成。
285: if (create_sram) {
286: if (vmtype == VMType::X68030) {
287: return CreateSRAM();
288: } else {
289: warnx("--create-sram is only for X68030 mode");
290: return EXIT_FAILURE;
291: }
292: }
293:
1.1.1.12 root 294: // 1. VM 作成
1.1.1.2 root 295: switch (vmtype) {
1.1.1.13 root 296: case VMType::X68030:
1.1.1.12 root 297: pVM.reset(new VM_X68030());
1.1.1.2 root 298: break;
299:
1.1.1.13 root 300: case VMType::LUNA1:
1.1.1.12 root 301: pVM.reset(new VM_LUNA1());
1.1.1.2 root 302: break;
303:
1.1.1.13 root 304: case VMType::LUNA88K:
1.1.1.12 root 305: pVM.reset(new VM_LUNA88K());
1.1.1.2 root 306: break;
1.1 root 307:
1.1.1.14 root 308: case VMType::NEWS:
309: pVM.reset(new VM_NEWS());
310: break;
311:
1.1.1.16! root 312: case VMType::VIRT68K:
! 313: pVM.reset(new VM_Virt68k());
! 314: break;
! 315:
1.1.1.2 root 316: default:
1.1.1.16! root 317: PANIC("vmtype=%s not configured", vmstr.c_str());
1.1.1.2 root 318: }
319:
1.1.1.12 root 320: // 2. 動的なコンストラクション
1.1.1.16! root 321: if (!pVM->Create()) {
1.1.1.12 root 322: pVM.reset();
1.1.1.5 root 323: return EXIT_FAILURE;
324: }
325:
1.1.1.16! root 326: // 3. 設定を確定。
1.1.1.5 root 327: // (VM コンストラクタで変数の増減があるのでそれより後、
328: // Create() でも SCSI パラメータを減らすので、それより後)
1.1.1.14 root 329: if (gConfig->Fix() == false) {
330: pVM.reset();
331: return EXIT_SUCCESS;
332: }
1.1.1.16! root 333:
! 334: // Fix() は配列から削除を行うため、これより前に取得していた ConfigItem
! 335: // のポインタや参照は無効になっている可能性がある。Fix() をまたいで
! 336: // ポインタや参照を使ってしまわないようここで一旦フォーカスを抜ける。
! 337: // なお Fix() 後は要素の削除は出来ないため、以降はこの問題は発生しない。
! 338:
! 339: return PASS;
! 340: }
! 341:
! 342: // VM の初期化、ステージ1 の後半。
! 343: // Config.Fix 以降。
! 344: int
! 345: MainApp::Init1b()
! 346: {
! 347: // 3.1 設定が確定したので --show-config なら設定内容を表示して終了。
1.1.1.2 root 348: if (show_config) {
1.1.1.3 root 349: gConfig->Show(show_config - 1);
1.1.1.12 root 350: pVM.reset();
1.1.1.5 root 351: return EXIT_SUCCESS;
1.1.1.2 root 352: }
353:
1.1.1.16! root 354: // 3.2 ログレベルを設定。コンストラクト後すぐに行う。
1.1.1.2 root 355: // -L help もここで処理。
356: if (!ParseLogopt()) {
1.1.1.12 root 357: pVM.reset();
1.1.1.5 root 358: return EXIT_FAILURE;
1.1.1.2 root 359: }
360:
1.1.1.16! root 361: // 3.3 -X のチェック。
! 362: // -L help より後にしないとヘルプが表示できない。
! 363: const ConfigItem& itemexec = gConfig->Find("exec-file");
! 364: if (exec_file == NULL) {
! 365: // -X が必要なケースで指定されてなければエラー。
! 366:
! 367: // 実行ファイル指定が必須の VM。
! 368: if (vmtype == VMType::NEWS || vmtype == VMType::VIRT68K) {
! 369: if (itemexec.GetFrom() == ConfigItem::FromInitial) {
! 370: warnx("vmtype=%s requires -X option (or exec-file).",
! 371: vmstr.c_str());
! 372: } else {
! 373: itemexec.Err("filename must be specified.");
! 374: }
! 375: return EXIT_FAILURE;
! 376: }
! 377:
! 378: // Human モード、MSX-DOS モードでも実行ファイル名が必要。
! 379: if (human_mode) {
! 380: warnx("-H option needs -X");
! 381: return EXIT_FAILURE;
! 382: }
! 383: if (msxdos_mode) {
! 384: warnx("-S option needs -X");
! 385: return EXIT_FAILURE;
! 386: }
! 387: } else {
! 388: // 実行ファイルのファイル名を間違えたくらいならここでエラーに出来る。
! 389: int r = access(exec_file, R_OK);
! 390: if (r != 0) {
! 391: itemexec.Err("%s", strerror(errno));
! 392: return EXIT_FAILURE;
! 393: }
! 394: }
! 395:
1.1.1.5 root 396: return PASS;
1.1.1.2 root 397: }
398:
1.1.1.7 root 399: // VM の初期化、ステージ2。スレッド生成を伴う。
400: // Init1() で引数を受け付けてから Init2() でデバッガスレッドなどを起動するが、
1.1.1.2 root 401: // -Mhelp とかが指定された場合はここでスレッド開始する前にプロセスを終了する
1.1.1.7 root 402: // 必要があるので分けてある。wxapp.cpp も参照。
1.1.1.2 root 403: bool
1.1.1.7 root 404: MainApp::Init2()
1.1.1.2 root 405: {
1.1.1.12 root 406: // 5. VM 初期化。
1.1.1.16! root 407: if (!pVM->Init()) {
1.1.1.2 root 408: return false;
409: }
410:
1.1.1.12 root 411: // メインスレッド名を設定
412: PTHREAD_SETNAME("Main");
413:
414: // 6. スレッド開始
1.1.1.16! root 415: if (!pVM->StartThread()) {
1.1.1.12 root 416: return false;
417: }
418:
419: // 7. 起動時設定の適用
1.1.1.16! root 420: if (!pVM->Apply()) {
1.1.1.2 root 421: return false;
422: }
423:
424: return true;
425: }
426:
427: // コマンドライン引数を処理する。
428: // 知らない引数とかがあればこちらで usage を表示して false を返す。
1.1.1.14 root 429: // 戻り値は、MainApp::PASS なら実行を継続、
430: // EXIT_SUCCESS/EXIT_FAILURE ならこのコードで終了。
431: int
1.1.1.2 root 432: MainApp::ParseOpt(int ac, char *av[])
433: {
1.1.1.6 root 434: struct stat st;
435: const char *cpath;
1.1.1.2 root 436: int b;
437: int c;
438:
1.1.1.6 root 439: cpath = ".";
1.1.1.2 root 440:
1.1.1.14 root 441: while ((c = getopt_long(ac, av, "b:B:c:CdDfhHL:M:s:SvV:X:",
1.1.1.2 root 442: longopts, NULL)) != -1) {
443: switch (c) {
444: case 'b':
1.1.1.4 root 445: debug_breakaddr.push_back(optarg);
1.1.1.2 root 446: break;
447:
448: case 'B':
449: b = atoi(optarg);
450: if (b < 2 || b > 6) {
451: fprintf(stderr, "-B <benchmark_mode>: 2..6\n");
1.1.1.14 root 452: return EXIT_FAILURE;
1.1.1.2 root 453: }
454: benchmark_mode = b;
455: break;
456:
457: case 'c':
1.1.1.6 root 458: cpath = optarg;
459: // 空文字列なら再び初期値に
460: if (cpath[0] == '\0') {
461: cpath = ".";
462: }
1.1.1.2 root 463: break;
464:
465: case 'C':
466: log_to_console = true;
467: break;
468:
469: case 'd':
470: debug_on_start = true;
471: break;
472:
473: case 'D':
1.1.1.12 root 474: // -D は -V debugger-driver=stdio と等価。
1.1.1.14 root 475: config_options.emplace_back("debugger-driver=stdio", "-D");
1.1.1.2 root 476: break;
477:
478: case 'f':
1.1.1.14 root 479: // -f は -V fast-mode=1 と等価。
480: config_options.emplace_back("fast-mode=1", "-f");
1.1.1.2 root 481: break;
482:
1.1.1.9 root 483: case 'H':
484: human_mode = true;
485: break;
486:
1.1.1.2 root 487: case 'L':
1.1.1.12 root 488: AddLogopt(optarg);
1.1.1.2 root 489: break;
490:
491: case 'M':
492: if (!monitor_opt.empty()) {
493: monitor_opt += ",";
494: }
495: monitor_opt += optarg;
496: break;
497:
498: case 's':
1.1.1.14 root 499: {
500: auto line = string_format("mainview-scale=%s", optarg);
501: config_options.emplace_back(line, "-s");
502: break;
503: }
504:
505: case 'S':
506: msxdos_mode = true;
1.1.1.2 root 507: break;
508:
509: case 'X':
1.1.1.16! root 510: {
! 511: // 相対パスはカレントディレクトリ起点とする。
! 512: char cwd[PATH_MAX];
! 513: getcwd(cwd, sizeof(cwd));
! 514: std::string path = NormalizePath(optarg, std::string(cwd));
! 515: std::string line = "exec-file=" + path;
! 516: config_options.emplace_back(line, "-X");
! 517:
! 518: // Human68k モードなら後続を引数にする。
! 519: // UNIX モードでは親和性が悪いのでしない。
! 520: if (human_mode) {
! 521: std::string arg;
! 522: arg = "exec-arg=";
! 523: for (int i = optind; i < ac; i++) {
! 524: if (i != optind) {
! 525: arg += " ";
! 526: }
! 527: arg += av[i];
1.1.1.2 root 528: }
1.1.1.16! root 529: optind = ac;
! 530: config_options.emplace_back(arg, "-X");
1.1.1.2 root 531: }
532: break;
1.1.1.16! root 533: }
1.1.1.2 root 534:
535: case 'v':
536: ShowVersion();
537: exit(0);
538:
539: case 'V':
1.1.1.14 root 540: config_options.emplace_back(optarg, "-V");
541: break;
542:
1.1.1.16! root 543: case OPT_bi_exg:
! 544: debug_breakinst_exg = true;
! 545: break;
! 546:
1.1.1.14 root 547: case OPT_create_sram:
548: create_sram = true;
1.1.1.2 root 549: break;
550:
551: case OPT_fontsize:
1.1.1.14 root 552: {
553: auto line = string_format("monitor-fontsize=%s", optarg);
554: config_options.emplace_back(line, "--fontsize");
1.1.1.2 root 555: break;
1.1.1.14 root 556: }
1.1.1.2 root 557:
558: case OPT_help:
559: ShowHelp(true);
1.1.1.14 root 560: return EXIT_SUCCESS;
1.1.1.2 root 561:
1.1.1.16! root 562: case OPT_initrd:
! 563: {
! 564: // 相対パスはカレントディレクトリを起点とする。
! 565: char cwd[PATH_MAX];
! 566: getcwd(cwd, sizeof(cwd));
! 567: std::string path = NormalizePath(optarg, std::string(cwd));
! 568: std::string line = "exec-initrd=" + path;
! 569: config_options.emplace_back(line, "--initrd");
! 570: break;
! 571: }
! 572:
1.1.1.12 root 573: case OPT_perf:
1.1.1.16! root 574: {
1.1.1.12 root 575: // パフォーマンス確認用。
576: // -Vprom-image=PROM.DAT はパスの問題があるので各自で追加指定
577: // する必要がある。
1.1.1.16! root 578: std::string where = "--perf -V";
! 579: config_options.emplace_back(".rtc-force-fixed=1", where);
! 580: config_options.emplace_back("clock-sync=virtual", where);
1.1.1.14 root 581: config_options.emplace_back("ethernet-macaddr=02:00:00:00:00:01",
1.1.1.16! root 582: where);
! 583: config_options.emplace_back("hostcom-driver=none", where);
! 584: config_options.emplace_back("hostnet-driver=none", where);
! 585: config_options.emplace_back("luna-dipsw1=11110111", where);
! 586: config_options.emplace_back("spc0-id0-writeignore=1", where);
! 587: config_options.emplace_back("spc0-id1-writeignore=1", where);
! 588: config_options.emplace_back("spc0-id2-writeignore=1", where);
! 589: config_options.emplace_back("spc0-id3-writeignore=1", where);
! 590: config_options.emplace_back("spc0-id4-writeignore=1", where);
! 591: config_options.emplace_back("spc0-id5-writeignore=1", where);
! 592: config_options.emplace_back("spc0-id6-writeignore=1", where);
! 593: config_options.emplace_back("fast-mode=1", where);
1.1.1.14 root 594: // -C
1.1.1.12 root 595: log_to_console = true;
596: break;
1.1.1.16! root 597: }
1.1.1.12 root 598:
1.1.1.2 root 599: case OPT_show_config:
1.1.1.3 root 600: show_config = 1;
601: break;
602:
603: case OPT_show_config_all:
604: show_config = 2;
1.1.1.2 root 605: break;
606:
1.1.1.8 root 607: case OPT_show_hostnet:
608: ShowHostnet();
1.1.1.14 root 609: return EXIT_SUCCESS;
1.1.1.8 root 610:
1.1.1.2 root 611: case 'h':
1.1.1.14 root 612: ShowHelp(false);
613: return EXIT_SUCCESS;
614:
1.1.1.2 root 615: default:
616: ShowHelp(false);
1.1.1.14 root 617: return EXIT_FAILURE;
1.1.1.2 root 618: }
619: }
620:
1.1.1.6 root 621: // 引数がディレクトリなら、それを VM ディレクトリとし、その中の
622: // nono.cfg を設定ファイルとする。
623: // 引数がファイルなら、それを設定ファイルとし、そのファイルがある
624: // ディレクトリを VM ディレクトリとする。
625: // -c DIR => vmdir = DIR, vmfile = DIR/nono.cfg
626: // -c DIR/FILE => vmdir = DIR, vmfile = FILE
627: if (stat(cpath, &st) < 0) {
628: warn("stat %s", cpath);
1.1.1.14 root 629: return EXIT_FAILURE;
1.1.1.2 root 630: }
1.1.1.6 root 631: if (S_ISDIR(st.st_mode)) {
632: vmdir = std::string(cpath);
633: if (vmdir.back() != '/') {
634: vmdir += '/';
635: }
636: vmfile = vmdir + "nono.cfg";
637: } else if (S_ISREG(st.st_mode)) {
638: vmfile = std::string(cpath);
639: auto pos = vmfile.rfind('/');
640: if (pos != std::string::npos) {
641: vmdir = vmfile.substr(0, pos + 1);
642: } else {
643: vmdir = "./";
644: }
645: } else {
646: warnx("-c %s: path must be file or directory", cpath);
1.1.1.14 root 647: return EXIT_FAILURE;
1.1.1.2 root 648: }
649:
650: // CLI 版で -Mhelp つけても黙って起動するのはさすがにどうかと思う。
651: // とは言え GUI 版のコマンドラインを CLI 版用に書き換えた時に -M を
652: // 消して回らないと起動できないのも面倒なので -M オプション自体は
653: // 無意味だけど許容する。うーん。
654: if (IsCLI()) {
655: if (monitor_opt == "help") {
656: warnx("-Mhelp is not available on CLI");
1.1.1.14 root 657: return EXIT_FAILURE;
1.1.1.2 root 658: }
659: if (!monitor_opt.empty()) {
660: warnx("-M option is ignored on CLI");
1.1.1.14 root 661: return EXIT_FAILURE;
1.1.1.2 root 662: }
663: }
664:
1.1.1.16! root 665: return MainApp::PASS;
! 666: }
1.1.1.9 root 667:
1.1.1.16! root 668: // CPU の機能とかをチェック。
! 669: bool
! 670: MainApp::CheckCPU()
! 671: {
! 672: #if defined(HAVE_AVX2)
! 673: // AVX2
! 674: detect_avx2 = ::DetectAVX2();
! 675:
! 676: const ConfigItem& item_avx2 = gConfig->Find("host-avx2");
! 677: std::string cfg_avx2 = item_avx2.AsString();
! 678:
! 679: if (cfg_avx2 == "auto") {
! 680: enable_avx2 = detect_avx2;
! 681: } else if (cfg_avx2 == "no") {
! 682: enable_avx2 = false;
! 683: } else {
! 684: item_avx2.Err("must be either \"auto\" or \"no\".");
! 685: return false;
1.1.1.14 root 686: }
1.1.1.16! root 687: #endif
! 688: // AVX2 以外は何もしない。設定のチェックもしないほうがいい。
! 689: return true;
1.1.1.2 root 690: }
691:
692: // バージョンを表示
693: void
694: MainApp::ShowVersion() const
695: {
696: // ここは実行ファイル名によらず nono にする
697: fprintf(stderr, "nono version %d.%d.%d (%s)\n",
698: NONO_MAJOR_VER, NONO_MINOR_VER, NONO_PATCH_VER, NONO_DATE);
699: }
700:
1.1.1.12 root 701: // ログレベル指定文字列を logopt に追加する。
702: void
703: MainApp::AddLogopt(const char *opt)
704: {
705: if (logopt.empty() == false) {
706: logopt += ',';
707: }
708: logopt += opt;
709: }
710:
1.1.1.8 root 711: // ログレベル指定文字列を処理する。
712: // str はログレベル指定文字列を ',' で連結した "foo=1,bar=2" 形式の文字列で、
713: // これを分解してそれぞれ担当するオブジェクトのログレベルにセットする。
714: // "help" があれば設定は行わず一覧を表示。
1.1 root 715: bool
1.1.1.2 root 716: MainApp::ParseLogopt()
1.1 root 717: {
1.1.1.8 root 718: std::vector<std::string> items;
719:
720: // 分解して..
1.1.1.12 root 721: items = string_split(logopt.c_str(), ',');
1.1.1.8 root 722:
723: // "help" があればヘルプを表示して終了
724: for (const auto& item : items) {
725: if (item == "help") {
726: std::vector<std::string> list = GetLogNames();
727: // less したいだろうから stderr ではなく stdout に出力する
728: for (const auto& name : list) {
729: printf(" %s\n", name.c_str());
730: }
731: return false;
1.1 root 732: }
1.1.1.8 root 733: }
734:
735: // ログレベルを設定
736: std::string errmsg;
737: if (SetLogopt(items, &errmsg) == false) {
738: warnx("%s", errmsg.c_str());
1.1 root 739: return false;
740: }
741:
1.1.1.8 root 742: return true;
743: }
1.1 root 744:
1.1.1.8 root 745: // ログレベルを設定する。
746: // ログレベル指定文字列を 1つずつに分解したリスト items を処理する。
747: // "help" があるケースはここに来るまでに処理してあるので、ここには来ない。
748: // 成功なら何も表示せず true を返す。失敗なら *errmsg にエラーメッセージを
749: // 格納して false を返す。
750: // MainApp 内と Debugger からも呼ばれる。
751: /*static*/ bool
752: MainApp::SetLogopt(const std::vector<std::string>& items, std::string *errmsg)
753: {
754: for (const auto& item : items) {
755: if (SetLogopt1(item.c_str(), errmsg) == false) {
756: return false;
757: }
758: }
759: if (0) { // デバッグ用
1.1.1.12 root 760: for (const auto o : gMainApp.GetObjects()) {
1.1.1.8 root 761: printf("%-16s %d\n", o->GetName().c_str(), o->loglevel);
1.1 root 762: }
1.1.1.8 root 763: }
764: return true;
765: }
766:
767: // "logname[=loglevel]" 形式をパースしてオブジェクトにログレベルを設定する。
768: // loglevel は省略なら 1 とする。
769: // logname が "all" なら全オブジェクトにセットする。
770: // そうでない場合は case ignore で完全一致するか前方一致で1つに確定すれば、
771: // そのオブジェクトにログレベルを設定して true を返す。
772: // 見付からないか候補が複数ある場合はエラーメッセージを *errmsg に出力して
773: // false を返す。
774: // この関数は (このすぐ上の SetLogopt() を経由して)
775: // MainApp と Debugger から呼ばれることに注意。
776: /*static*/ bool
777: MainApp::SetLogopt1(const std::string& item, std::string *errmsg)
778: {
779: std::string name;
780: const char *v;
781: int level;
782:
783: v = strchr(item.c_str(), '=');
784: if (v) {
785: name = std::string(item.c_str(), v - item.c_str());
786: level = atoi(++v);
787: } else {
788: name = item;
789: level = 1;
790: }
791: // ここで name は変数名、level は値(省略されたら1)
792:
793: if (name.empty()) {
794: *errmsg = "logname must be specified";
795: return false;
796: }
1.1 root 797:
1.1.1.12 root 798: // 今の所、値域は -1 〜 9 ということにしておく。
799: if (level < -1) {
800: level = -1;
801: } else if (level > 9) {
802: level = 9;
803: }
804:
1.1.1.8 root 805: // "all" なら全部にセット
806: if (name == "all") {
1.1.1.12 root 807: for (auto obj : gMainApp.GetObjects()) {
1.1.1.8 root 808: if (obj->GetName().empty() == false) {
1.1.1.12 root 809: obj->SetLogLevel(level);
1.1.1.8 root 810: }
1.1 root 811: }
1.1.1.8 root 812: return true;
813: }
1.1.1.13 root 814: // "fdd" なら "fdd*" 全部にする
815: if (name == "fdd") {
816: for (auto obj : gMainApp.GetObjects()) {
817: if (strncasecmp(obj->GetName().c_str(), "fdd", 3) == 0) {
818: obj->SetLogLevel(level);
819: }
820: }
821: return true;
822: }
1.1.1.15 root 823: // "bankram" も "bankram[01]" 両方にする
824: if (name == "bankram") {
825: for (auto obj : gMainApp.GetObjects()) {
826: if (strncasecmp(obj->GetName().c_str(), "bankram", 7) == 0) {
827: obj->SetLogLevel(level);
828: }
829: }
830: return true;
831: }
1.1.1.16! root 832: // "gfpic" も "gfpic*" 全部にする
! 833: if (name == "gfpic") {
! 834: for (auto obj : gMainApp.GetObjects()) {
! 835: if (strncasecmp(obj->GetName().c_str(), "gfpic", 5) == 0) {
! 836: obj->SetLogLevel(level);
! 837: }
! 838: }
! 839: return true;
! 840: }
1.1.1.8 root 841:
1.1.1.12 root 842: // エイリアスリストを作る
843: using aliaslist_t = std::vector<std::pair<std::string, Object *>>;
844: aliaslist_t alias_list;
845: for (const auto& obj : gMainApp.GetObjects()) {
1.1.1.8 root 846: const std::vector<std::string>& aliases = obj->GetAliases();
847:
848: for (const auto& a : aliases) {
1.1.1.12 root 849: alias_list.emplace_back(a, obj);
850: }
851: }
1.1.1.8 root 852:
1.1.1.12 root 853: // エイリアスを完全一致のみのものと部分一致も許容するものに分ける
854: aliaslist_t exact_alias;
855: aliaslist_t partial_alias;
856: for (const auto& a0 : alias_list) {
857: bool exact = false;
858: for (const auto& a1 : alias_list) {
859: if (a0.first == a1.first) {
860: continue;
861: }
862: if (starts_with_ignorecase(a0.first, a1.first)) {
863: exact = true;
864: break;
1.1 root 865: }
1.1.1.8 root 866: }
1.1.1.12 root 867: if (exact) {
868: exact_alias.push_back(a0);
869: } else {
870: partial_alias.push_back(a0);
871: }
872: }
873:
874: // 完全一致をまず調べる
875: for (const auto& a : exact_alias) {
876: if (strcasecmp(a.first.c_str(), name.c_str()) == 0) {
877: a.second->SetLogLevel(level);
878: return true;
879: }
880: }
881:
882: aliaslist_t found;
883: for (const auto& a : partial_alias) {
884: // 前方一致したら覚えておく
885: if (starts_with_ignorecase(a.first, name)) {
886: found.push_back(a);
887: }
1.1.1.8 root 888: }
889:
890: // 見付からない場合はエラー
891: if (found.empty()) {
892: *errmsg = string_format("Unknown logname \"%s\"", name.c_str());
893: return false;
894: }
895:
896: // 1つだけなら確定
897: if (found.size() == 1) {
1.1.1.12 root 898: found[0].second->SetLogLevel(level);
1.1.1.8 root 899: return true;
900: }
901:
902: // 複数あれば候補文字列を作成
903: *errmsg = string_format("Ambiguous logname \"%s\": candidates are",
904: name.c_str());
1.1.1.12 root 905: for (const auto& cand : found) {
906: *errmsg += string_format(" \"%s\"", cand.first.c_str());
1.1.1.8 root 907: }
908: return false;
909: }
910:
911: // ログ名の一覧を取得する。
912: // MainApp と Debugger から呼ばれる。
913: /*static*/ std::vector<std::string>
914: MainApp::GetLogNames()
915: {
916: std::vector<Object *> sortobj;
917:
918: // エイリアスを持つオブジェクトだけ抜き出す
1.1.1.12 root 919: for (const auto& obj : gMainApp.GetObjects()) {
1.1.1.8 root 920: if (obj->GetAliases().empty() == false) {
921: sortobj.emplace_back(obj);
922: }
923: }
924:
925: // aliases の一語目で sortobj をソートする
926: std::sort(sortobj.begin(), sortobj.end(),
1.1.1.12 root 927: [](const auto a, const auto b) {
1.1.1.8 root 928: const auto& sa = a->GetAliases()[0];
929: const auto& sb = b->GetAliases()[0];
1.1.1.12 root 930: return sa < sb;
1.1.1.8 root 931: }
932: );
933:
934: std::vector<std::string> list;
935: for (const auto *obj : sortobj) {
936: const auto& aliases = obj->GetAliases();
937: std::string str;
938:
1.1.1.12 root 939: str = aliases[0];
1.1.1.8 root 940: if (aliases.size() > 1) {
941: str += " (alias:";
942: for (int i = 1, sz = aliases.size(); i < sz; i++) {
943: str += ' ';
944: str += aliases[i];
1.1 root 945: }
1.1.1.8 root 946: str += ')';
1.1.1.6 root 947: }
1.1.1.8 root 948: list.emplace_back(str);
1.1 root 949: }
1.1.1.8 root 950: list.emplace_back("all");
1.1 root 951:
1.1.1.8 root 952: return list;
1.1 root 953: }
1.1.1.2 root 954:
955: // 関連するファイルのパスを取得。
1.1.1.3 root 956: // 1. name がファイル名のみなら、VM ディレクトリとその親ディレクトリを検索。
957: // ファイルが存在すればその時点で戻り値とする。ファイルが存在したけど
958: // 何らか不都合があったとしても (例えばファイルサイズが 0 だとか
959: // パーミッションが足りないとか) 次を試すとかはしない。
1.1.1.16! root 960: // 2. 通常手順(相対パスは VM ディレクトリ起点) でパスを展開。
! 961: // ファイルの有無は不問。
1.1.1.2 root 962: std::string
963: MainApp::SearchFile(const std::string& name) const
964: {
1.1.1.3 root 965: // 1. パス区切りを含んでなければ、ファイル名のみ
966: if (name.find('/') == std::string::npos) {
1.1.1.16! root 967: std::string path;
! 968: struct stat st;
! 969:
1.1.1.3 root 970: // 1a. VM ディレクトリ
971: path = GetVMDir() + name;
972: if (stat(path.c_str(), &st) == 0) {
973: return path;
974: }
975:
1.1.1.16! root 976: // 1b. 親ディレクトリ
1.1.1.3 root 977: path = GetVMDir() + "../" + name;
978: if (stat(path.c_str(), &st) == 0) {
979: return path;
980: }
981:
982: // どちらもなければエラー
983: return "";
984: }
1.1.1.2 root 985:
1.1.1.16! root 986: // 2. 通常手順でパスを展開。
! 987: return NormalizePath(name);
! 988: }
! 989:
! 990: // path を必要なら絶対パスに展開して返す。
! 991: // 相対パスは VM ディレクトリを起点とする。
! 992: std::string
! 993: MainApp::NormalizePath(const std::string& path) const
! 994: {
! 995: return NormalizePath(path, GetVMDir());
! 996: }
! 997:
! 998: // path を必要なら絶対パスに展開して返す。
! 999: // 相対パスは basedir を起点とする。
! 1000: std::string
! 1001: MainApp::NormalizePath(const std::string& path,
! 1002: const std::string& basedir) const
! 1003: {
! 1004: // 絶対パスならそのまま返す。
! 1005: if (path[0] == '/') {
! 1006: return path;
1.1.1.2 root 1007: }
1008:
1.1.1.16! root 1009: std::string newpath;
! 1010:
! 1011: if (path[0] == '~') {
! 1012: // 先頭が '~' ならホームディレクトリに展開。
! 1013:
! 1014: struct passwd *passwd = getpwuid(getuid());
! 1015: // 見付からないことはないはずだが、一応。
! 1016: if (passwd != NULL) {
! 1017: newpath = std::string(passwd->pw_dir);
! 1018: }
! 1019: newpath += &path[1];
! 1020: } else {
! 1021: // basedir からの相対パスとして展開。
! 1022:
! 1023: newpath = basedir;
! 1024: if (newpath.empty() || newpath.back() != '/') {
! 1025: newpath += '/';
! 1026: }
! 1027: newpath += path;
1.1.1.2 root 1028: }
1029:
1.1.1.16! root 1030: return newpath;
1.1.1.3 root 1031: }
1032:
1.1.1.14 root 1033: // 初回起動時用に SRAM.DAT を作成する。
1034: // 戻り値は EXIT_SUCCESS / EXIT_FAILURE。
1035: int
1036: MainApp::CreateSRAM()
1037: {
1038: char buf[16 * 1024];
1039: std::string filename;
1040: autofd fd;
1041: int r;
1042:
1043: filename = GetVMDir() + "SRAM.DAT";
1044:
1045: // 存在確認のため一旦読み込み専用で開いてみる。
1046: // オープン出来たら何もしない。
1047: fd = open(filename.c_str(), O_RDONLY);
1048: if (fd >= 0) {
1049: warnx("%s: Already exists", filename.c_str());
1050: return EXIT_FAILURE;
1051: }
1052:
1053: fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
1054: if (fd < 0) {
1055: warn("%s: open failed", filename.c_str());
1056: return EXIT_FAILURE;
1057: }
1058:
1059: memset(&buf[0], 0, sizeof(buf));
1060: for (int i = 0; i < SRAMDevice::InitialData.size(); i++) {
1061: buf[i] = SRAMDevice::InitialData[i];
1062: }
1063:
1064: r = write(fd, buf, sizeof(buf));
1065: if (r < 0) {
1066: warn("%s: write failed", filename.c_str());
1067: return EXIT_FAILURE;
1068: }
1069:
1070: fd.Close();
1071: warnx("created %s", filename.c_str());
1072: return EXIT_SUCCESS;
1073: }
1074:
1.1.1.13 root 1075: // 現在の VM が指定のケーパビリティを持っているか?
1.1.1.3 root 1076: bool
1.1.1.13 root 1077: MainApp::Has(VMCap cap) const
1.1.1.3 root 1078: {
1.1.1.13 root 1079: uint32 vmcap = 1U << (int)GetVMType();
1080: return (vmcap & (uint32)cap) != 0;
1.1.1.2 root 1081: }
1.1.1.8 root 1082:
1.1.1.16! root 1083: // VM 機種名を返す。
! 1084: const char *
! 1085: MainApp::GetVMName() const
! 1086: {
! 1087: if ((bool)pVM) {
! 1088: return pVM->GetVMName();
! 1089: } else {
! 1090: return NULL; // not configured
! 1091: }
! 1092: }
! 1093:
1.1.1.8 root 1094: // コンパイルされている host netdriver の一覧を表示。
1095: void
1096: MainApp::ShowHostnet() const
1097: {
1.1.1.12 root 1098: auto list = HostNetDevice::GetDrivers();
1.1.1.8 root 1099: for (const auto& name : list) {
1100: printf(" %s\n", name.c_str());
1101: }
1102: }
1.1.1.12 root 1103:
1.1.1.14 root 1104: // オブジェクト登録
1.1.1.12 root 1105: void
1.1.1.14 root 1106: MainApp::RegistObject(Object *obj)
1.1.1.12 root 1107: {
1.1.1.14 root 1108: // ID が重複していないかチェック (NONE なら重複可)
1109: auto id = obj->GetId();
1110: if (id != OBJ_NONE) {
1111: if (FindObject(id)) {
1112: PANIC("%s already exists", Object::GetIdStr(id));
1113: }
1114: }
1115:
1116: obj->logger = gMainApp.GetLogger();
1.1.1.12 root 1117:
1118: objects.push_back(obj);
1119: }
1120:
1.1.1.14 root 1121: // オブジェクト削除
1.1.1.12 root 1122: void
1.1.1.14 root 1123: MainApp::UnregistObject(Object *obj)
1.1.1.12 root 1124: {
1.1.1.14 root 1125: // 最初に見付かった一つを削除するだけでいい
1.1.1.12 root 1126: for (auto it = objects.begin(); it != objects.end(); ++it) {
1127: if (*it == obj) {
1128: objects.erase(it);
1129: break;
1130: }
1131: }
1132: }
1.1.1.14 root 1133:
1134: // 指定された id を持つオブジェクトを返す。なければ NULL を返す。
1135: Object *
1136: MainApp::FindObject(int id) const
1137: {
1138: // NONE はここで検索しない (SCSI デバイスなど、そっちで検索する)
1139: if (id == OBJ_NONE) {
1140: return NULL;
1141: }
1142: for (auto obj : objects) {
1143: if (obj->GetId() == id) {
1144: return obj;
1145: }
1146: }
1147: return NULL;
1148: }
1149:
1150: // 指定された id を持つオブジェクトを探す。なければ assert する。
1151: Object *
1152: MainApp::GetObject(int id) const
1153: {
1154: Object *obj = FindObject(id);
1155: if (__predict_false(obj == NULL)) {
1156: PANIC("objid=%s not found", Object::GetIdStr(id));
1157: }
1158: return obj;
1159: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.