|
|
1.1 ! root 1: // vi:set ts=4: ! 2: ! 3: // ! 4: // nono ! 5: // Copyright (C) 2024 nono project ! 6: // Licensed under nono-license.txt ! 7: // ! 8: ! 9: // ! 10: // Human68k の実行ファイルを NetBSD/m68k 上で実行する。 ! 11: // ! 12: ! 13: // メモリマップ ! 14: // 0000'0000 ベクタ ! 15: // 0000'1000 (4KB/ページの時の mmap 開始アドレス) ! 16: // 0000'2000 (8KB/ページの時の mmap 開始アドレス) ! 17: // 0000'2000 初期 A7 保存 (func_t から戻るため) ! 18: // 0000'2200 エントリポイント ! 19: // 0000'c000 コマンドライン引数 ! 20: // 0001'ff00 SSP 初期値 ! 21: // 0001'ff00 PSP (プロセスエントリ) ! 22: // 0002'0000 ロードアドレス ! 23: ! 24: #include "runx.h" ! 25: #include <fcntl.h> ! 26: #include <inttypes.h> ! 27: #include <libgen.h> ! 28: #include <limits.h> ! 29: #include <signal.h> ! 30: #include <stdlib.h> ! 31: #include <string.h> ! 32: #include <sys/mman.h> ! 33: #include <sys/stat.h> ! 34: #include <sys/wait.h> ! 35: ! 36: // ぽかよけ ! 37: #if !defined(__m68k__) ! 38: #error "This file must be compiled on m68k!" ! 39: #endif ! 40: ! 41: #define LOAD_ADDR (0x20000) ! 42: ! 43: typedef int (*func_t)(); ! 44: ! 45: // .X ファイルのヘッダ構造 ! 46: struct XFileHeader { ! 47: uint8 magic[2]; // +00 識別子 'HU' ! 48: uint8 unused1; ! 49: uint8 loadmode; // +03 ロードモード ! 50: uint32 base_addr; // +04 ベースアドレス ! 51: uint32 exec_addr; // +08 実行開始アドレス ! 52: uint32 text_size; // +0c テキストサイズ ! 53: uint32 data_size; // +10 データサイズ ! 54: uint32 bss_size; // +14 BSS サイズ ! 55: uint32 reloc_size; // +18 再配置テーブルサイズ ! 56: uint32 symbol_size; // +1c シンボルサイズ ! 57: uint32 scdline_size; // +20 SCD 行番号テーブルサイズ ! 58: uint32 scdsym_size; // +24 SCD シンボルテーブルサイズ ! 59: uint32 scdstr_size; // +28 SCD 文字列テーブルサイズ ! 60: uint32 unused2[4]; ! 61: uint32 module_offset; // +3c モジュールリストの位置 ! 62: } __packed; ! 63: ! 64: static void usage(); ! 65: static void init_mem(); ! 66: static uint32 loadfile(); ! 67: static uint32 loadfileX(); ! 68: static uint32 loadfileR(); ! 69: static void dumpwait(int); ! 70: static void dumpreg(const struct reg *); ! 71: static int run(uint32); ! 72: ! 73: int opt_debug; ! 74: int opt_trace; ! 75: struct reg_emul emul; ! 76: ! 77: static const char *filename; ! 78: static uint32 last_addr; ! 79: static uint8 *mem; ! 80: static uint8 *page0; ! 81: static uint32 pgsize; ! 82: static uint32 ret_addr; ! 83: static char human68k_arg[255]; ! 84: ! 85: int ! 86: main(int ac, char *av[]) ! 87: { ! 88: int c; ! 89: ! 90: while ((c = getopt(ac, av, "dt")) != -1) { ! 91: switch (c) { ! 92: case 'd': ! 93: opt_debug++; ! 94: break; ! 95: case 't': ! 96: opt_trace = 1; ! 97: break; ! 98: default: ! 99: usage(); ! 100: } ! 101: } ! 102: ac -= optind; ! 103: av += optind; ! 104: ! 105: if (ac < 1) { ! 106: usage(); ! 107: } ! 108: ! 109: // Human68k コマンド名と引数。 ! 110: filename = av[0]; ! 111: human68k_arg[0] = '\0'; ! 112: for (int i = 1; i < ac; i++) { ! 113: if (i != 1) { ! 114: strlcat(human68k_arg, " ", sizeof(human68k_arg)); ! 115: } ! 116: strlcat(human68k_arg, av[i], sizeof(human68k_arg)); ! 117: } ! 118: DEBUG(1, "human68k_arg=\"%s\"", human68k_arg); ! 119: ! 120: // 0番地を含む1ページは mmap(2) とかでも割り当て出来ないようなので ! 121: // 仕方ないので自前で用意する。 ! 122: pgsize = getpgsize(); ! 123: page0 = (uint8 *)calloc(1, pgsize); ! 124: if (page0 == NULL) { ! 125: err(1, "calloc failed"); ! 126: } ! 127: ! 128: // mem は pgsize 番地以降 12MB までを確保しておく。 ! 129: // このためにこの実行ファイル自身は 16MB 以降にロードしてある。 ! 130: mem = (uint8 *)mmap((void *)pgsize, 12*1024*1024 - pgsize, ! 131: PROT_READ | PROT_WRITE, ! 132: MAP_FIXED | MAP_ANON | MAP_SHARED | MAP_INHERIT, -1, 0); ! 133: if (mem == MAP_FAILED) { ! 134: err(1, "mmap"); ! 135: } ! 136: DEBUG(2, "mem=%p", mem); ! 137: init_mem(); ! 138: init_doscall(); ! 139: ! 140: // ロード。 ! 141: uint32 exec_addr = loadfile(); ! 142: if (exec_addr == (uint32)-1) { ! 143: exit(1); ! 144: } ! 145: ! 146: // 実行。 ! 147: if (run(exec_addr) < 0) { ! 148: exit(1); ! 149: } ! 150: ! 151: return 0; ! 152: } ! 153: ! 154: static void ! 155: usage() ! 156: { ! 157: fprintf(stderr, "%s <Human68k executable> [<argument...>]\n", ! 158: getprogname()); ! 159: exit(1); ! 160: } ! 161: ! 162: // メモリの初期化。何をどこまでやるか。 ! 163: static void ! 164: init_mem() ! 165: { ! 166: writemem1(0xcbc, 3); // とりあえず ! 167: writemem1(0xcbd, 0xff); ! 168: } ! 169: ! 170: // 実行ファイルをロードする。 ! 171: static uint32 ! 172: loadfile() ! 173: { ! 174: const char *e; ! 175: ! 176: // Support only .X and .R executable for now. ! 177: e = strrchr(filename, '.'); ! 178: if (e != NULL && strcasecmp(e, ".x") == 0) { ! 179: return loadfileX(); ! 180: } else { ! 181: return loadfileR(); ! 182: } ! 183: } ! 184: ! 185: // .X ファイルを読み込んで、リロケートまで行う。 ! 186: static uint32 ! 187: loadfileX() ! 188: { ! 189: struct stat st; ! 190: struct XFileHeader *xhdr; ! 191: uint8 *file; ! 192: int fd; ! 193: ! 194: fd = open(filename, O_RDONLY); ! 195: if (fd < 0) { ! 196: err(1, "%s", filename); ! 197: } ! 198: ! 199: if (fstat(fd, &st) < 0) { ! 200: err(1, "%s: stat failed", filename); ! 201: } ! 202: DEBUG(1, "loading %s %u bytes", filename, (uint)st.st_size); ! 203: ! 204: file = (uint8 *)mmap(NULL, st.st_size, PROT_READ, MAP_FILE | MAP_SHARED, ! 205: fd, 0); ! 206: if (file == MAP_FAILED) { ! 207: err(1, "%s: mmap failed", filename); ! 208: } ! 209: ! 210: xhdr = (struct XFileHeader *)file; ! 211: if (xhdr->magic[0] != 'H' || xhdr->magic[1] != 'U') { ! 212: errx(1, "%s: is not .X file", filename); ! 213: } ! 214: ! 215: uint32 base_addr = be32toh(xhdr->base_addr); ! 216: uint32 exec_addr = be32toh(xhdr->exec_addr); ! 217: uint32 text_size = be32toh(xhdr->text_size); ! 218: uint32 data_size = be32toh(xhdr->data_size); ! 219: uint32 bss_size = be32toh(xhdr->bss_size); ! 220: DEBUG(1, "base=$%x exec=$%x (text=$%x data=$%x bss=$%x)", ! 221: base_addr, exec_addr, text_size, data_size, bss_size); ! 222: ! 223: uint32 reloc_size = be32toh(xhdr->reloc_size); ! 224: ! 225: uint32 load_addr = LOAD_ADDR; ! 226: uint32 load_size = text_size + data_size; ! 227: last_addr = load_addr + load_size + bss_size; ! 228: ! 229: if (load_addr < pgsize) { ! 230: for (int i = 0; i < load_size; i++) { ! 231: writemem1(load_addr + i, file[sizeof(XFileHeader) + i]); ! 232: } ! 233: } else { ! 234: memcpy((void *)load_addr, &file[sizeof(XFileHeader)], load_size); ! 235: } ! 236: ! 237: // 再配置テーブルの file での位置。 ! 238: uint32 reloc_pos = sizeof(XFileHeader) + load_size; ! 239: ! 240: // 再配置。 ! 241: uint32 offset = load_addr - base_addr; ! 242: uint32 reloc_end = reloc_pos + reloc_size; ! 243: uint32 A = load_addr; ! 244: uint32 B = base_addr; ! 245: uint32 C = A - B; ! 246: while (reloc_pos < reloc_end) { ! 247: uint32 D; ! 248: D = be16toh(*(const uint16 *)&file[reloc_pos]); ! 249: DEBUG(2, "D=%x", D); ! 250: reloc_pos += 2; ! 251: if (D == 1) { ! 252: D = be32toh(*(const uint32 *)&file[reloc_pos]); ! 253: DEBUG(2, " odd, D=%x", D); ! 254: reloc_pos += 4; ! 255: } ! 256: if ((D & 1) == 0) { ! 257: A += D; ! 258: uint32 old = readmem4(A); ! 259: writemem4(A, old + C); ! 260: DEBUG(2, " Write_L A=%x, old=%x new=%x", A, old, old + C); ! 261: } else { ! 262: A += D - 1; ! 263: uint32 old = readmem2(A); ! 264: writemem2(A, old + C); ! 265: DEBUG(2, " Write_W A=%x, old=%x new=%x", A, old, old + C); ! 266: } ! 267: } ! 268: munmap(file, st.st_size); ! 269: close(fd); ! 270: ! 271: exec_addr += offset; ! 272: DEBUG(1, ".x format, base_addr=%08x, exec_addr=%08x", base_addr, exec_addr); ! 273: return exec_addr; ! 274: } ! 275: ! 276: // .R ファイルを読み込む。 ! 277: static uint32 ! 278: loadfileR() ! 279: { ! 280: struct stat st; ! 281: int fd; ! 282: ! 283: fd = open(filename, O_RDONLY); ! 284: if (fd < 0) { ! 285: err(1, "%s", filename); ! 286: } ! 287: ! 288: if (fstat(fd, &st) < 0) { ! 289: err(1, "%s: stat failed", filename); ! 290: } ! 291: DEBUG(1, "loading %s %u bytes", filename, (uint)st.st_size); ! 292: ! 293: // XXX TODO ! 294: uint32 load_addr = LOAD_ADDR; ! 295: int n = read(fd, mem + load_addr, st.st_size); ! 296: if (n < 0) { ! 297: err(1, "%s", filename); ! 298: } ! 299: if (n < st.st_size) { ! 300: errx(1, "%s: read too short", filename); ! 301: } ! 302: close(fd); ! 303: ! 304: DEBUG(1, ".r format, exec_addr=%08x", load_addr); ! 305: return load_addr; ! 306: } ! 307: ! 308: uint32 ! 309: readmem1(uint32 addr) ! 310: { ! 311: if (addr < pgsize) { ! 312: return page0[addr]; ! 313: } else { ! 314: return mem[addr - pgsize]; ! 315: } ! 316: } ! 317: ! 318: uint32 ! 319: readmem2(uint32 addr) ! 320: { ! 321: uint32 data = (readmem1(addr) << 8) | readmem1(addr + 1); ! 322: return data; ! 323: } ! 324: ! 325: uint32 ! 326: readmem4(uint32 addr) ! 327: { ! 328: uint32 data = (readmem2(addr) << 16) | readmem2(addr + 2); ! 329: return data; ! 330: } ! 331: ! 332: void ! 333: writemem1(uint32 addr, uint32 data) ! 334: { ! 335: if (addr < pgsize) { ! 336: page0[addr] = data; ! 337: } else { ! 338: mem[addr - pgsize] = data; ! 339: } ! 340: } ! 341: ! 342: void ! 343: writemem2(uint32 addr, uint32 data) ! 344: { ! 345: writemem1(addr, data >> 8); ! 346: writemem1(addr + 1, data); ! 347: } ! 348: ! 349: void ! 350: writemem4(uint32 addr, uint32 data) ! 351: { ! 352: writemem2(addr, data >> 16); ! 353: writemem2(addr + 2, data); ! 354: } ! 355: ! 356: // wait(2) status のダンプ表示。 ! 357: static void ! 358: dumpwait(int status) ! 359: { ! 360: printf("wait status=0x%x", status); ! 361: if (WIFEXITED(status)) { ! 362: printf(" EXITED status=0x%x\n", WEXITSTATUS(status)); ! 363: } ! 364: if (WIFSIGNALED(status)) { ! 365: printf(" SIGNAL %d", WTERMSIG(status)); ! 366: if (WCOREDUMP(status)) { ! 367: printf(" coredump"); ! 368: } ! 369: printf("\n"); ! 370: } ! 371: if (WIFSTOPPED(status)) { ! 372: int signo = WSTOPSIG(status); ! 373: printf(" STOPPED by %s\n", signame(signo)); ! 374: } ! 375: } ! 376: ! 377: static void ! 378: dumpreg(const struct reg *reg) ! 379: { ! 380: printf("D0:%08x D4:%08x A0:%08x A4:%08x SR:%04x\n", ! 381: RegD(0), RegD(4), RegA(0), RegA(4), GetSR); ! 382: printf("D1:%08x D5:%08x A1:%08x A5:%08x\n", ! 383: RegD(1), RegD(5), RegA(1), RegA(5)); ! 384: printf("D2:%08x D6:%08x A2:%08x A6:%08x\n", ! 385: RegD(2), RegD(6), RegA(2), RegA(6)); ! 386: printf("D3:%08x D7:%08x A3:%08x A7:%08x\n", ! 387: RegD(3), RegD(7), RegA(3), RegA(7)); ! 388: } ! 389: ! 390: static int ! 391: run(uint32 exec_addr) ! 392: { ! 393: int fd[2]; ! 394: int r; ! 395: pid_t pid; ! 396: uint32 addr; ! 397: ! 398: #define Write2(d) do { \ ! 399: writemem2(addr, (d)); \ ! 400: addr += 2; \ ! 401: } while (0) ! 402: #define Write4(d) do { \ ! 403: writemem4(addr, (d)); \ ! 404: addr += 4; \ ! 405: } while (0) ! 406: ! 407: // コマンドライン文字列を書き込む。LASCII 形式。 ! 408: addr = 0xc001; ! 409: for (int i = 0; i < 255; i++) { ! 410: uint8 c = (uint8)human68k_arg[i]; ! 411: writemem1(addr++, c); ! 412: if (c == '\0') { ! 413: writemem1(0xc000, i); ! 414: break; ! 415: } ! 416: } ! 417: ! 418: // PSP (Process Entry) を書き込む。 ! 419: // PSP はロードアドレス-256 の位置でなければならないようだ。 ! 420: uint32 psp_base = LOAD_ADDR - 0x100; ! 421: uint32 ram_size = 10 * 1024 * 1024; ! 422: uint32 sp_backup = 0x2000; ! 423: uint32 boot_addr = 0x2200; ! 424: addr = psp_base; ! 425: Write4(-1); // 1つ前のメモリ管理ポインタ ! 426: Write4(-1); // このメモリを確保したプロセスのメモリ管理ポインタ ! 427: Write4(ram_size - 4 + 1); // このメモリブロックの終わり+1のアドレス ! 428: Write4(-1); // 次のメモリ管理ポインタ ! 429: ! 430: addr = psp_base + 0x20; ! 431: Write4(0xc000); // コマンドライン ! 432: ! 433: addr = boot_addr; ! 434: Write2(0x23cf); // move.l a7,sp_backup // スタックを保存 ! 435: Write4(sp_backup); ! 436: Write2(0x2e7c); // move.l psp_base,a7 ! 437: Write4(psp_base); ! 438: Write2(0x207c); // move.l psp_base,a0 ! 439: Write4(psp_base); ! 440: Write2(0x227c); // move.l last_addr,a1 ! 441: Write4(last_addr); ! 442: Write2(0x247c); // move.l #$c000,a2 ! 443: Write4(0xc000); ! 444: Write2(0x267c); // move.l #$e000,a3 ! 445: Write4(0xe000); ! 446: Write2(0x287c); // move.l exec_addr,a4 ! 447: Write4(exec_addr); ! 448: Write2(0x2c49); // move.l a1,a6 ! 449: Write2(0x4eb9); // jsr.l exec_addr ! 450: Write4(exec_addr); ! 451: ret_addr = addr; ! 452: Write2(0x2e79); // movea.l sp_backup,a7 // スタックを戻して ! 453: Write4(sp_backup); ! 454: Write2(0x4e75); // rts ! 455: ! 456: // READY 通知用パイプ。 ! 457: if (pipe2(fd, O_NOSIGPIPE) < 0) { ! 458: err(1, "pipe"); ! 459: } ! 460: ! 461: pid = fork(); ! 462: if (pid < 0) { ! 463: err(1, "fork"); ! 464: } ! 465: if (pid == 0) { ! 466: /* child */ ! 467: ! 468: // 親の準備が出来るのを待つ。 ! 469: char dummy[1]; ! 470: if (read(fd[0], dummy, 1) < 0) { ! 471: err(1, "child: read failed"); ! 472: } ! 473: ! 474: func_t p = (func_t)boot_addr; ! 475: r = p(); ! 476: exit(r); ! 477: } else { ! 478: /* parent */ ! 479: int status; ! 480: uint32 nextpc; ! 481: ! 482: DEBUG(1, "child pid=%u", pid); ! 483: ! 484: if (ptrace(PT_ATTACH, pid, NULL, 0) < 0) { ! 485: err(1, "ptrace(PT_ATTACH)"); ! 486: } ! 487: ! 488: // nextpc==1 は STOP した直後から再開、の意。 ! 489: nextpc = 1; ! 490: ! 491: for (;;) { ! 492: r = wait(&status); ! 493: if (opt_debug >= 2) { ! 494: dumpwait(status); ! 495: } ! 496: if (WIFEXITED(status)) { ! 497: break; ! 498: } ! 499: ! 500: if (WIFSTOPPED(status)) { ! 501: struct reg regbuf; ! 502: struct reg *reg = ®buf; ! 503: ! 504: if (ptrace(PT_GETREGS, pid, reg, 0) < 0) { ! 505: err(1, "ptrace(PT_GETREGS)"); ! 506: } ! 507: ! 508: int signo = WSTOPSIG(status); ! 509: ! 510: // PT_ATTACH が効くと SIGSTOP が来る。 ! 511: if (signo == SIGSTOP) { ! 512: // 準備が出来たので通知 (同期)。 ! 513: write(fd[1], "", 1); ! 514: DEBUG(1, "STOP -> Continue"); ! 515: } else if (signo == SIGILL || signo == SIGSEGV) { ! 516: // 特権違反命令や未実装命令は SIGILL、 ! 517: // メモリアクセス違反は SIGSEGV が飛んでくるが、 ! 518: // どちらも命令コードを調べて再実行するところは同じ。 ! 519: uint32 inst32; ! 520: errno = 0; ! 521: inst32 = ptrace(PT_READ_I, pid, (void *)RegPC, 0); ! 522: if (errno != 0) { ! 523: err(1, "ptrace(PT_READ_I)"); ! 524: } ! 525: if (opt_debug >= 2) { ! 526: dumpreg(reg); ! 527: } ! 528: DEBUG(2, "pc=%08x inst32=%08x", RegPC, inst32); ! 529: uint16 inst = inst32 >> 16; ! 530: ! 531: if ((inst & 0xff00) == 0xff00) { ! 532: r = doscall(pid, (inst & 0xff), reg); ! 533: if (r == 1) { ! 534: nextpc = ret_addr; ! 535: goto next; ! 536: } ! 537: nextpc = RegPC + 2; ! 538: } else { ! 539: nextpc = exec_op(pid, reg, signo, inst); ! 540: if (nextpc == (uint32)-1) { ! 541: break; ! 542: } ! 543: } ! 544: ! 545: } else if (signo == SIGTRAP || signo == SIGFPE) { ! 546: // TRAP15 は SIGTRAP、CHK* 命令やゼロ除算は SIGFPE、と ! 547: // 本来別のシグナルが飛んで来るが ! 548: // どちらも同じ形式で処理できるのでまとめてある。 ! 549: struct ptrace_siginfo psi; ! 550: if (ptrace(PT_GET_SIGINFO, pid, &psi, sizeof(psi)) < 0) { ! 551: err(1, "ptrace(PT_GET_SIGINFO)"); ! 552: } ! 553: if (signo == SIGTRAP ! 554: && psi.psi_siginfo.si_code == TRAP_CHLD) { ! 555: // 子プロセス起動時にこちらが来る場合がある。 ! 556: // see kern_sig.c eventswitch() ! 557: DEBUG(1, "TRAP_CHLD"); ! 558: goto next; ! 559: } ! 560: switch (psi.psi_siginfo.si_trap) { ! 561: case T_ZERODIV: ! 562: TRACE(RegPC, "previous op issued T_ZERODIV"); ! 563: nextpc = exception_format2(reg, 5); ! 564: break; ! 565: case T_CHKINST: ! 566: TRACE(RegPC, "previous op issued T_CHKINST"); ! 567: nextpc = exception_format2(reg, 6); ! 568: break; ! 569: case T_TRAPVINST: ! 570: TRACE(RegPC, "previous op issued T_TRAPVINST"); ! 571: nextpc = exception_format2(reg, 7); ! 572: break; ! 573: case T_TRAP15: ! 574: iocscall(pid, reg); ! 575: // nextpc は更新されている。 ! 576: break; ! 577: default: ! 578: errx(1, "%06x: Not implemented si_trap %d", ! 579: RegPC, psi.psi_siginfo.si_trap); ! 580: } ! 581: ! 582: } else { ! 583: errx(1, "%06x: Unknown signal %d", RegPC, signo); ! 584: } ! 585: ! 586: next: ! 587: if (ptrace(PT_SETREGS, pid, reg, 0) < 0) { ! 588: err(1, "ptrace(PT_SETREGS)"); ! 589: } ! 590: ! 591: // 実行を再開し、次に何か起きるまで待つ。 ! 592: if (ptrace(PT_CONTINUE, pid, (void *)nextpc, 0) < 0) { ! 593: err(1, "PT_CONTINUE"); ! 594: } ! 595: nextpc = 1; ! 596: } ! 597: } ! 598: } ! 599: ! 600: kill(pid, SIGKILL); ! 601: return 0; ! 602: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.