|
|
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:
365: // ホストの a.out をゲストの RAM に読み込む。
366: // target_aout_mid は a.out での machine id (機種情報) で、こっちで ELF の
367: // machine type に読み替える。
368: // 戻り値はエントリポイントアドレス。エラーなら 0 を返す。
369: uint32
370: RAMDevice::Load_elf32(const char *name, const uint8 *file, size_t filesize,
371: uint32 target_aout_mid)
372: {
373: uint16 target_elf_mid;
374:
375: // a.out machine id から ELF machine type への読み替え
376: switch (target_aout_mid) {
377: case AOUT_MID_M68K:
378: target_elf_mid = EM_68K;
379: break;
380: case AOUT_MID_M88K:
381: target_elf_mid = EM_88K;
382: break;
383: default:
384: warnx("Load_elf32 unknown target_aout_mid $%x", target_aout_mid);
385: return 0;
386: }
387:
388: // ヘッダをチェック
389: const Elf32_Ehdr *ehdr = (const Elf32_Ehdr *)file;
390: if (filesize < sizeof(*ehdr)) {
391: warnx("Load_elf32 \"%s\" file too short", name);
392: return 0;
393: }
394: uint16 e_type = elf16toh(ehdr->e_type);
395: uint16 e_machine = elf16toh(ehdr->e_machine);
396: uint32 e_entry = elf32toh(ehdr->e_entry);
397: putlog(1, "EI_CLASS = $%02x", ehdr->e_ident[EI_CLASS]);
398: putlog(1, "EI_DATA = $%02x", ehdr->e_ident[EI_DATA]);
399: putlog(1, "e_type = $%02x", e_type);
400: putlog(1, "e_machine = $%02x", e_machine);
401: putlog(1, "e_entry = $%08x", e_entry);
402:
403: if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) {
404: warnx("Load_elf32 \"%s\" not ELF32 format", name);
405: return 0;
406: }
407: if (e_type != ET_EXEC) {
408: warnx("Load_elf32 \"%s\" not executable file", name);
409: return 0;
410: }
411: if (e_machine != target_elf_mid) {
412: warnx("Load_elf32 \"%s\" machine type mismatch "
413: "($%02x expected but $%02x)",
414: name, target_elf_mid, e_machine);
415: return 0;
416: }
417:
418: // プログラムヘッダに読み込むべき領域が示されている
419: // phoff がプログラムヘッダのファイル先頭からのオフセット
420: // phentsize がプログラムヘッダ1つの大きさ
421: // phnum がプログラムヘッダの個数
422: uint32 e_phoff = elf32toh(ehdr->e_phoff);
423: uint16 e_phentsize = elf16toh(ehdr->e_phentsize);
424: uint16 e_phnum = elf16toh(ehdr->e_phnum);
425: putlog(1, "e_phoff = $%08x", e_phoff);
426: putlog(1, "e_phentsize = $%04x", e_phentsize);
427: putlog(1, "e_phnum = %d", e_phnum);
428: if (e_phentsize != sizeof(Elf32_Phdr)) {
429: warnx("Load_elf32 \"%s\" unknown program header size 0x%x",
430: name, e_phentsize);
431: return 0;
432: }
433:
434: IODeviceStream ds(this);
435:
436: for (int j = 0; j < e_phnum; j++) {
437: const Elf32_Phdr *phdr = &((const Elf32_Phdr*)(file + e_phoff))[j];
438: // p_type が PT_LOAD なところをロードする。
439: // その際 p_memsz > p_filesz なら差が BSS 領域。
440: uint32 p_type = elf32toh(phdr->p_type); // タイプ
441: uint32 p_offset = elf32toh(phdr->p_offset); // ファイル上のオフセット
442: uint32 p_vaddr = elf32toh(phdr->p_vaddr); // 仮想アドレス
443: uint32 p_filesz = elf32toh(phdr->p_filesz); // 読み込み元ファイルサイズ
444: uint32 p_memsz = elf32toh(phdr->p_memsz); // 書き込み先サイズ
445: putlog(1, "[%d] p_type = $%08x", j, p_type);
446: putlog(1, "[%d] p_offset = $%08x", j, p_offset);
447: putlog(1, "[%d] p_vaddr = $%08x", j, p_vaddr);
448: putlog(1, "[%d] p_filesz = $%08x", j, p_filesz);
449: putlog(1, "[%d] p_memsz = $%08x", j, p_memsz);
450:
451: if (p_type == PT_LOAD) {
452: ds.SetAddr(p_vaddr);
453: const uint8 *src = file + p_offset;
454: int i = 0;
455: for (; i < p_filesz; i++) {
456: ds.Write8(*src++);
457: }
458: for (; i < p_memsz; i++) {
459: ds.Write8(0);
460: }
1.1.1.3 root 461: } else if (p_type == 0x65a3dbe6) { // PT_OPENBSD_RANDOMIZE
462: // XXX どうする?
463: continue;
464:
1.1.1.2 root 465: } else {
466: warnx("Load_elf32 \"%s\" p_type %d not supported", name, p_type);
467: return 0;
468: }
469: }
470:
471: return e_entry;
472: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.