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