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