|
|
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:
7: #include "ram.h"
1.1.1.2 root 8: #include "aout.h"
9: #include "elf.h"
10: #include "iodevstream.h"
11: #include "mainapp.h"
12: #include <fcntl.h>
13: #include <sys/stat.h>
14: #include <sys/mman.h>
15: #include <zlib.h>
1.1 root 16:
17: //
18: // メインメモリ
19: //
1.1.1.4 root 20:
21: std::unique_ptr<RAMDevice> gRAM;
22:
1.1 root 23: // RAM はロングワード単位でホストバイトオーダ配置なので、
24: // バイトアクセス、ワードアクセス時は HLB(), HLW() マクロを使用のこと。
1.1.1.2 root 25: std::unique_ptr<uint8[]> ram;
1.1.1.4 root 26:
27: // RAM 容量(バイト単位)であり ram[] の確保したバイト数
1.1 root 28: int ram_size;
29:
30: RAMDevice::RAMDevice()
31: {
32: logname = "ram";
33: devname = "MainRAM";
34: devaddr = 0;
35:
36: ram_size = 0;
1.1.1.4 root 37:
38: // アクセスウェイト
39: switch (gMainApp.GetVMType()) {
40: case VMTYPE_LUNA88K:
41: // 取扱説明書p.21 に、CMMU からのアクセスタイムが
42: // リード(4byte)で 200ns、バーストリード(16byte)で 320ns
43: // ライト(4byte)で 160ns、バーストライト(16byte)で 280ns とある。
44: // 25MHz は 40ns/clockで、この数値は CMMU からのアクセス全体の時間
45: // らしいので、ここで加算するウェイトはそれから 2クロック引いたもの。
46: read_wait = 3;
47: write_wait = 2;
48: break;
49: case VMTYPE_LUNA1:
50: // LUNA-I は1ウェイト。
51: read_wait = 1;
52: write_wait = 1;
53: break;
54:
55: case VMTYPE_X68030:
56: default:
57: // X68030 ではシステムポートが設定するのでここでは不要。
58: break;
59: }
1.1 root 60: }
61:
62: RAMDevice::~RAMDevice()
63: {
64: ram_size = 0;
65: }
66:
67: bool
68: RAMDevice::Init()
69: {
1.1.1.4 root 70: int ram_size_MB;
71:
72: const ConfigItem& item = gConfig->Find("ram-size");
73: ram_size_MB = item.AsInt();
1.1.1.3 root 74: // XXX 上限は適当。
75: // LUNA は空間は 1024MB 分あるが末尾に Null と BusErr 領域が必要なはずで
76: // メモリの上限は 1023MB くらいのはず。
1.1.1.4 root 77: if (ram_size_MB < 1 || ram_size_MB > 1023) {
1.1.1.3 root 78: item.Err();
79: return false;
1.1 root 80: }
1.1.1.3 root 81:
1.1.1.4 root 82: ram_size = ram_size_MB * 1024 * 1024;
1.1.1.2 root 83: ram.reset(new uint8[ram_size]);
1.1 root 84:
85: return true;
86: }
87:
1.1.1.4 root 88: void
89: RAMDevice::ResetHard()
90: {
91: // X68030 ではリセットで戻るはず。(LUNA は固定なので関係ない)
92: if (gMainApp.GetVMType() == VMTYPE_X68030) {
93: read_wait = 0;
94: write_wait = 0;
95: }
96: }
97:
1.1 root 98: uint64
99: RAMDevice::Read8(uint32 addr)
100: {
1.1.1.4 root 101: gMPU->AddCycle(read_wait);
1.1 root 102: uint32 data;
103: data = ram[HLB(addr)];
1.1.1.6 ! root 104: putlog(4, "$%08x.B -> $%02x", addr, data);
1.1 root 105: return data;
106: }
107:
108: uint64
109: RAMDevice::Read16(uint32 addr)
110: {
1.1.1.4 root 111: gMPU->AddCycle(read_wait);
1.1 root 112: uint32 data;
113: data = *(uint16 *)&ram[HLW(addr)];
1.1.1.6 ! root 114: putlog(4, "$%08x.W -> $%04x", addr, data);
1.1 root 115: return data;
116: }
117:
118: uint64
119: RAMDevice::Read32(uint32 addr)
120: {
1.1.1.4 root 121: gMPU->AddCycle(read_wait);
1.1 root 122: uint32 data;
123: data = *(uint32 *)&ram[addr];
1.1.1.6 ! root 124: putlog(4, "$%08x.L -> $%08x", addr, data);
1.1 root 125: return data;
126: }
127:
128: uint64
129: RAMDevice::Write8(uint32 addr, uint32 data)
130: {
1.1.1.4 root 131: gMPU->AddCycle(write_wait);
1.1 root 132: ram[HLB(addr)] = data;
1.1.1.6 ! root 133: putlog(3, "$%08x.B <- $%02x", addr, data);
1.1 root 134: return 0;
135: }
136:
137: uint64
138: RAMDevice::Write16(uint32 addr, uint32 data)
139: {
1.1.1.4 root 140: gMPU->AddCycle(write_wait);
1.1 root 141: *(uint16 *)&ram[HLW(addr)] = data;
1.1.1.6 ! root 142: putlog(3, "$%08x.W <- $%04x", addr, data);
1.1 root 143: return 0;
144: }
145:
146: uint64
147: RAMDevice::Write32(uint32 addr, uint32 data)
148: {
1.1.1.4 root 149: gMPU->AddCycle(write_wait);
1.1 root 150: *(uint32 *)&ram[addr] = data;
1.1.1.6 ! root 151: putlog(3, "$%08x.L <- $%08x", addr, data);
1.1 root 152: return 0;
153: }
154:
155: uint64
156: RAMDevice::Peek8(uint32 addr)
157: {
158: return ram[HLB(addr)];
159: }
1.1.1.2 root 160:
1.1.1.4 root 161: // アクセスウェイトを設定 (X68030 でシステムポートから呼ばれる)
162: void
163: RAMDevice::SetWait(uint32 wait)
164: {
165: read_wait = wait;
166: write_wait = wait;
167: }
168:
1.1.1.2 root 169: // filepath で指定されるホストの実行ファイルをゲストにロードする。
170: // ファイルのフォーマットは自動で認識する。
171: // target_mid は machine id で、実体は a.out の値を流用してはいるが
172: // ELF であってもこちらで読み替える。
173: // 戻り値はエントリポイントアドレス。エラーなら 0 を返す。
174: uint32
175: RAMDevice::LoadFile(const char *filepath, uint32 target_mid)
176: {
177: uint8 *file;
178: struct stat st;
179: size_t filesize;
180: uint32 entry;
181: int fd;
182:
183: fd = open(filepath, O_RDONLY);
184: if (fd == -1) {
185: warn("LoadFile \"%s\" open failed", filepath);
186: return 0;
187: }
188:
189: if (fstat(fd, &st) == -1) {
190: warn("LoadFile \"%s\" fstat failed", filepath);
191: close(fd);
192: return 0;
193: }
194:
195: if (!S_ISREG(st.st_mode)) {
196: warnx("LoadFile \"%s\" not a regular file", filepath);
197: close(fd);
198: return 0;
199: }
200:
201: // 今の所マジック判定は最初の4バイトで出来るので
202: if (st.st_size < 4) {
203: warnx("LoadFile \"%s\" file too short", filepath);
204: close(fd);
205: return 0;
206: }
207: filesize = st.st_size;
208:
209: file = (uint8 *)mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
210: if (file == MAP_FAILED) {
211: warn("LoadFile \"%s\" mmap failed", filepath);
212: close(fd);
213: return 0;
214: }
215:
216: // 先頭3バイトの gzip マジックを調べる
217: if (file[0] == 0x1f && file[1] == 0x8b && file[2] == 0x08) {
218: // gzip
219: entry = LoadGzFile(filepath, file, filesize, target_mid);
220: } else {
221: // 通常ファイル
222: entry = LoadFile(filepath, file, filesize, target_mid);
223: }
224:
225: munmap(file, filesize);
226: close(fd);
227: return entry;
228: }
229:
230: // gzip を展開してゲストにロードする。
231: uint32
232: RAMDevice::LoadGzFile(const char *filepath,
233: const uint8 *infile, size_t infilesize, uint32 target_mid)
234: {
235: z_stream z {};
236: uint32 decompsize;
237: int rv;
238:
239: // gzip なら(?) ファイルの末尾4バイトが (LE で?) 展開後サイズ(?)。
1.1.1.3 root 240: decompsize = le32toh(*(const uint32 *)&infile[infilesize - 4]);
1.1.1.2 root 241: std::unique_ptr<uint8[]> dstbuf(new uint8[decompsize]);
242:
243: // gzip 展開
244: // (uncompress() の方が楽そうに見えるが gzip 形式に対応してないらしい)
245: z.zalloc = Z_NULL;
246: z.zfree = Z_NULL;
247: z.opaque = Z_NULL;
248: rv = inflateInit2(&z, 47);
249: if (rv != Z_OK) {
250: warnx("LoadGzFile \"%s\" inflateInit2 failed %d", filepath, rv);
251: return 0;
252: }
253:
1.1.1.3 root 254: z.next_in = const_cast<Bytef *>(infile);
1.1.1.2 root 255: z.avail_in = infilesize - 4;
256: z.next_out = dstbuf.get();
257: z.avail_out = decompsize;
258: rv = inflate(&z, Z_NO_FLUSH);
259: if (rv != Z_OK) {
260: warnx("LoadGzFile \"%s\" inflate failed %d", filepath, rv);
261: return 0;
262: }
263:
264: inflateEnd(&z);
265:
266: // 展開できたのでロード
267: return LoadFile(filepath, dstbuf.get(), decompsize, target_mid);
268: }
269:
270: // file, filesize で示されるバッファを実行ファイルとみなしてゲストにロードする。
271: // ファイルのフォーマットは自動で認識する。
272: // target_mid は machine id で、実体は a.out の値を流用してはいるが
273: // ELF であってもこちらで読み替える。
274: // name はここではエラーメッセージとかの表示用でしかない。
275: // 戻り値はエントリポイントアドレス。エラーなら 0 を返す。
276: uint32
277: RAMDevice::LoadFile(const char *name, const uint8 *file, size_t filesize,
278: uint32 target_mid)
279: {
280: uint32 entry = 0;
281:
282: // フォーマットだけ判定して分岐
1.1.1.3 root 283: const Elf32_Ehdr *ehdr = (const Elf32_Ehdr *)file;
284: const aout_header *aout = (const aout_header *)file;
1.1.1.2 root 285: if (memcmp(ehdr->e_ident, "\177ELF", 4) == 0) {
286: entry = Load_elf32(name, file, filesize, target_mid);
287: } else if ((be32toh(aout->magic) & 0xffff) == AOUT_OMAGIC) {
288: entry = Load_aout(name, file, filesize, target_mid);
289: } else {
290: warnx("LoadFile \"%s\" unknown executable file format", name);
291: }
292:
293: return entry;
294: }
295:
296: // ホストの a.out をゲストの RAM に読み込む。
297: // 戻り値はエントリポイントアドレス。エラーなら 0 を返す。
298: uint32
299: RAMDevice::Load_aout(const char *name, const uint8 *file, size_t filesize,
300: uint32 target_mid)
301: {
302: aout_header aout;
303: int copylen;
304:
305: if (filesize < sizeof(aout)) {
306: warnx("Load_aout \"%s\" file too short", name);
307: return 0;
308: }
309:
310: // ヘッダを読み込む
311: // BigEndian しかターゲットにしてないので最初から全部変換しとく
1.1.1.3 root 312: aout.magic = be32toh(((const aout_header *)file)->magic);
313: aout.textsize = be32toh(((const aout_header *)file)->textsize);
314: aout.datasize = be32toh(((const aout_header *)file)->datasize);
315: aout.bsssize = be32toh(((const aout_header *)file)->bsssize);
316: aout.symsize = be32toh(((const aout_header *)file)->symsize);
317: aout.entry = be32toh(((const aout_header *)file)->entry);
318: aout.textrelsize = be32toh(((const aout_header *)file)->textrelsize);
319: aout.datarelsize = be32toh(((const aout_header *)file)->datarelsize);
1.1.1.2 root 320:
321: // マジックをチェック
322: uint32 mid = AOUT_MID(aout.magic);
323: if (mid != target_mid) {
324: warnx("Load_aout \"%s\" machine id mismatch (%03x expected but %03x)",
325: name, target_mid, mid);
326: return 0;
327: }
328:
329: putmsg(1, "%s textsize = %08x", __func__, aout.textsize);
330: putmsg(1, "%s datasize = %08x", __func__, aout.datasize);
331: putmsg(1, "%s bsssize = %08x", __func__, aout.bsssize);
332: putmsg(1, "%s symsize = %08x", __func__, aout.symsize);
333: putmsg(1, "%s entry = %08x", __func__, aout.entry);
334: putmsg(1, "%s textrelsize = %08x", __func__, aout.textrelsize);
335: putmsg(1, "%s datarelsize = %08x", __func__, aout.datarelsize);
336:
337: // とりあえず再配置は無視
338: if (aout.textrelsize != 0 || aout.datarelsize != 0) {
339: warnx("Load_aout \"%s\" relocation not supported", name);
340: return 0;
341: }
342:
343: // OpenBSD の boot は text+data が filesize より大きくて何かおかしいが
344: // OMAGIC の a.out は text+data が連続しているだけなので、とりあえず
345: // 何も考えずにファイルサイズ分ロードしておく。
346: copylen = aout.textsize + aout.datasize;
347: if (copylen >= filesize) {
348: copylen = filesize;
349: warnx("warning: Load_aout \"%s\" coruppted? "
350: "(text=%u + data=%u, filesize=%d); loading %d bytes",
351: name, aout.textsize, aout.datasize, (int)filesize, copylen);
352: /* FALLTHROUGH */
353: }
354:
355: // 再配置不要なので、text + data を entry に置いて entry に飛ぶだけ
356: IODeviceStream ds(this, aout.entry);
357: const uint8 *src = file + sizeof(aout);
358: for (int i = 0; i < copylen; i++) {
359: ds.Write8(*src++);
360: }
361:
362: return aout.entry;
363: }
364:
365: // ELF フォーマットのエンディアンからホストエンディアンに変換
366: #define elf16toh(x) ((ehdr->e_ident[EI_DATA] == ELFDATA2MSB) ? \
367: be16toh(x) : le16toh(x))
368: #define elf32toh(x) ((ehdr->e_ident[EI_DATA] == ELFDATA2MSB) ? \
369: be32toh(x) : le32toh(x))
370:
1.1.1.5 root 371: // XXX このへん、そのうちきれいにする
372: #define GetElfStr1(array, v) GetElfStr(array, countof(array), v)
373: static const char *
374: GetElfStr(const std::pair<uint, const char *> *map, uint mapcount, uint v)
375: {
376: for (int i = 0; i < mapcount; i++) {
377: if (map[i].first == v) {
378: return map[i].second;
379: }
380: }
381: return "?";
382: }
383:
384: static std::pair<uint, const char *> class_str[] = {
385: { ELFCLASSNONE, "NONE" },
386: { ELFCLASS32, "32bit" },
387: { ELFCLASS64, "64bit" },
388: };
389: #define GetElfClassStr(x) GetElfStr1(class_str, (x))
390:
391: static std::pair<uint, const char *> data_str[] = {
392: { ELFDATANONE, "NONE" },
393: { ELFDATA2LSB, "LE" },
394: { ELFDATA2MSB, "BE" },
395: };
396: #define GetElfDataStr(x) GetElfStr1(data_str, (x))
397:
398: static std::pair<uint, const char *> etype_str[] = {
399: { ET_NONE, "ET_NONE" },
400: { ET_REL, "ET_REL" },
401: { ET_EXEC, "ET_EXEC" },
402: { ET_DYN, "ET_DYN" },
403: { ET_CORE, "ET_CORE" },
404: };
405: #define GetElfETypeStr(x) GetElfStr1(etype_str, (x))
406:
407: static std::pair<uint, const char *> machine_str[] = {
408: // 多いので関係分のみ
409: { EM_NONE, "NONE" },
410: { EM_68K, "Motorola 68000" },
411: { EM_88K, "Motorola 88000" },
412: };
413: #define GetElfMachineStr(x) GetElfStr1(machine_str, (x))
414:
415: static std::pair<uint, const char *> ptype_str[] = {
416: { PT_NULL, "PT_NULL" },
417: { PT_LOAD, "PT_LOAD" },
418: { PT_DYNAMIC, "PT_DYNAMIC" },
419: { PT_INTERP, "PT_INTERP" },
420: { PT_NOTE, "PT_NOTE" },
421: { PT_SHLIB, "PT_SHLIB" },
422: { PT_PHDR, "PT_PHDR" },
423: { PT_GNU_RELRO, "PT_GNU_RELRO" }, // GNU specific
424: { PT_OPENBSD_RANDOMIZE, "PT_OPENBSD_RANDOMIZE" },
425: { PT_OPENBSD_WXNEEDED, "PT_OPENBSD_WXNEEDED" },
426: { PT_OPENBSD_BOOTDATA, "PT_OPENBSD_BOOTDATA" },
427: };
428: #define GetElfPTypeStr(x) GetElfStr1(ptype_str, (x))
429:
1.1.1.2 root 430: // ホストの a.out をゲストの RAM に読み込む。
431: // target_aout_mid は a.out での machine id (機種情報) で、こっちで ELF の
432: // machine type に読み替える。
433: // 戻り値はエントリポイントアドレス。エラーなら 0 を返す。
434: uint32
435: RAMDevice::Load_elf32(const char *name, const uint8 *file, size_t filesize,
436: uint32 target_aout_mid)
437: {
438: uint16 target_elf_mid;
439:
440: // a.out machine id から ELF machine type への読み替え
441: switch (target_aout_mid) {
442: case AOUT_MID_M68K:
443: target_elf_mid = EM_68K;
444: break;
445: case AOUT_MID_M88K:
446: target_elf_mid = EM_88K;
447: break;
448: default:
449: warnx("Load_elf32 unknown target_aout_mid $%x", target_aout_mid);
450: return 0;
451: }
452:
453: // ヘッダをチェック
454: const Elf32_Ehdr *ehdr = (const Elf32_Ehdr *)file;
455: if (filesize < sizeof(*ehdr)) {
456: warnx("Load_elf32 \"%s\" file too short", name);
457: return 0;
458: }
459: uint16 e_type = elf16toh(ehdr->e_type);
460: uint16 e_machine = elf16toh(ehdr->e_machine);
461: uint32 e_entry = elf32toh(ehdr->e_entry);
1.1.1.5 root 462: if (loglevel >= 1) {
463: uint n;
464: n = ehdr->e_ident[EI_CLASS];
465: putlog("EI_CLASS = $%02x (%s)", n, GetElfClassStr(n));
466: n = ehdr->e_ident[EI_DATA];
467: putlog("EI_DATA = $%02x (%s)", n, GetElfDataStr(n));
468:
469: putlog("e_type = $%02x (%s)", e_type, GetElfETypeStr(e_type));
470: putlog("e_machine = $%02x (%s)", e_machine,
471: GetElfMachineStr(e_machine));
472: putlog("e_entry = $%08x", e_entry);
473: }
1.1.1.2 root 474:
475: if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) {
476: warnx("Load_elf32 \"%s\" not ELF32 format", name);
477: return 0;
478: }
479: if (e_type != ET_EXEC) {
480: warnx("Load_elf32 \"%s\" not executable file", name);
481: return 0;
482: }
483: if (e_machine != target_elf_mid) {
484: warnx("Load_elf32 \"%s\" machine type mismatch "
485: "($%02x expected but $%02x)",
486: name, target_elf_mid, e_machine);
487: return 0;
488: }
489:
490: // プログラムヘッダに読み込むべき領域が示されている
491: // phoff がプログラムヘッダのファイル先頭からのオフセット
492: // phentsize がプログラムヘッダ1つの大きさ
493: // phnum がプログラムヘッダの個数
494: uint32 e_phoff = elf32toh(ehdr->e_phoff);
495: uint16 e_phentsize = elf16toh(ehdr->e_phentsize);
496: uint16 e_phnum = elf16toh(ehdr->e_phnum);
1.1.1.5 root 497: if (loglevel >= 1) {
498: putlog("e_phoff = $%08x", e_phoff);
499: putlog("e_phentsize = $%04x", e_phentsize);
500: putlog("e_phnum = %d", e_phnum);
501: }
1.1.1.2 root 502: if (e_phentsize != sizeof(Elf32_Phdr)) {
503: warnx("Load_elf32 \"%s\" unknown program header size 0x%x",
504: name, e_phentsize);
505: return 0;
506: }
507:
508: IODeviceStream ds(this);
509:
510: for (int j = 0; j < e_phnum; j++) {
511: const Elf32_Phdr *phdr = &((const Elf32_Phdr*)(file + e_phoff))[j];
512: // p_type が PT_LOAD なところをロードする。
513: // その際 p_memsz > p_filesz なら差が BSS 領域。
514: uint32 p_type = elf32toh(phdr->p_type); // タイプ
515: uint32 p_offset = elf32toh(phdr->p_offset); // ファイル上のオフセット
516: uint32 p_vaddr = elf32toh(phdr->p_vaddr); // 仮想アドレス
517: uint32 p_filesz = elf32toh(phdr->p_filesz); // 読み込み元ファイルサイズ
518: uint32 p_memsz = elf32toh(phdr->p_memsz); // 書き込み先サイズ
1.1.1.5 root 519: if (loglevel >= 1) {
520: putlog("[%d] p_type = $%08x (%s)", j,
521: p_type, GetElfPTypeStr(p_type));
522: putlog(" p_offset = $%08x", p_offset);
523: putlog(" p_vaddr = $%08x", p_vaddr);
524: putlog(" p_filesz = $%08x", p_filesz);
525: putlog(" p_memsz = $%08x", p_memsz);
526: }
1.1.1.2 root 527:
528: if (p_type == PT_LOAD) {
529: ds.SetAddr(p_vaddr);
530: const uint8 *src = file + p_offset;
531: int i = 0;
532: for (; i < p_filesz; i++) {
533: ds.Write8(*src++);
534: }
535: for (; i < p_memsz; i++) {
536: ds.Write8(0);
537: }
1.1.1.5 root 538: } else if (p_type == PT_NOTE) {
539: // ベンダー文字列みたいなのらしいので無視してよい
540: continue;
541: } else if (p_type == PT_OPENBSD_RANDOMIZE) {
1.1.1.3 root 542: // XXX どうする?
543: continue;
544:
1.1.1.2 root 545: } else {
1.1.1.5 root 546: warnx("Load_elf32 \"%s\" p_type 0x%x(%s) not supported",
547: name, p_type, GetElfPTypeStr(p_type));
1.1.1.2 root 548: return 0;
549: }
550: }
551:
552: return e_entry;
553: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.