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