|
|
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.