|
|
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: //
20: // RAM はロングワード単位でホストバイトオーダ配置なので、
21: // バイトアクセス、ワードアクセス時は HLB(), HLW() マクロを使用のこと。
22: //
23:
1.1.1.2 root 24: std::unique_ptr<uint8[]> ram;
1.1 root 25: int ram_size;
1.1.1.2 root 26: std::unique_ptr<RAMDevice> gRAM;
1.1 root 27:
28: RAMDevice::RAMDevice()
29: {
30: logname = "ram";
31: devname = "MainRAM";
32: devaddr = 0;
33:
34: ram_size = 0;
35: }
36:
37: RAMDevice::~RAMDevice()
38: {
39: ram_size = 0;
40: }
41:
42: bool
43: RAMDevice::Init()
44: {
1.1.1.3 ! root 45: const ConfigItem& item = gConfig->Find("ram-size"); // [MB]
! 46: ram_size = item.AsInt();
! 47: // XXX 上限は適当。
! 48: // LUNA は空間は 1024MB 分あるが末尾に Null と BusErr 領域が必要なはずで
! 49: // メモリの上限は 1023MB くらいのはず。
! 50: if (ram_size < 1 || ram_size > 1023) {
! 51: item.Err();
! 52: return false;
1.1 root 53: }
1.1.1.3 ! root 54:
! 55: // MB 表記をバイト単位にしてから確保
! 56: ram_size = ram_size * 1024 * 1024;
1.1.1.2 root 57: ram.reset(new uint8[ram_size]);
1.1 root 58:
59: return true;
60: }
61:
62: uint64
63: RAMDevice::Read8(uint32 addr)
64: {
65: uint32 data;
66: data = ram[HLB(addr)];
67: return data;
68: }
69:
70: uint64
71: RAMDevice::Read16(uint32 addr)
72: {
73: uint32 data;
74: data = *(uint16 *)&ram[HLW(addr)];
75: return data;
76: }
77:
78: uint64
79: RAMDevice::Read32(uint32 addr)
80: {
81: uint32 data;
82: data = *(uint32 *)&ram[addr];
83: return data;
84: }
85:
86: uint64
87: RAMDevice::Write8(uint32 addr, uint32 data)
88: {
89: ram[HLB(addr)] = data;
90: return 0;
91: }
92:
93: uint64
94: RAMDevice::Write16(uint32 addr, uint32 data)
95: {
96: *(uint16 *)&ram[HLW(addr)] = data;
97: return 0;
98: }
99:
100: uint64
101: RAMDevice::Write32(uint32 addr, uint32 data)
102: {
103: *(uint32 *)&ram[addr] = data;
104: return 0;
105: }
106:
107: uint64
108: RAMDevice::Peek8(uint32 addr)
109: {
110: return ram[HLB(addr)];
111: }
1.1.1.2 root 112:
113: // filepath で指定されるホストの実行ファイルをゲストにロードする。
114: // ファイルのフォーマットは自動で認識する。
115: // target_mid は machine id で、実体は a.out の値を流用してはいるが
116: // ELF であってもこちらで読み替える。
117: // 戻り値はエントリポイントアドレス。エラーなら 0 を返す。
118: uint32
119: RAMDevice::LoadFile(const char *filepath, uint32 target_mid)
120: {
121: uint8 *file;
122: struct stat st;
123: size_t filesize;
124: uint32 entry;
125: int fd;
126:
127: fd = open(filepath, O_RDONLY);
128: if (fd == -1) {
129: warn("LoadFile \"%s\" open failed", filepath);
130: return 0;
131: }
132:
133: if (fstat(fd, &st) == -1) {
134: warn("LoadFile \"%s\" fstat failed", filepath);
135: close(fd);
136: return 0;
137: }
138:
139: if (!S_ISREG(st.st_mode)) {
140: warnx("LoadFile \"%s\" not a regular file", filepath);
141: close(fd);
142: return 0;
143: }
144:
145: // 今の所マジック判定は最初の4バイトで出来るので
146: if (st.st_size < 4) {
147: warnx("LoadFile \"%s\" file too short", filepath);
148: close(fd);
149: return 0;
150: }
151: filesize = st.st_size;
152:
153: file = (uint8 *)mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
154: if (file == MAP_FAILED) {
155: warn("LoadFile \"%s\" mmap failed", filepath);
156: close(fd);
157: return 0;
158: }
159:
160: // 先頭3バイトの gzip マジックを調べる
161: if (file[0] == 0x1f && file[1] == 0x8b && file[2] == 0x08) {
162: // gzip
163: entry = LoadGzFile(filepath, file, filesize, target_mid);
164: } else {
165: // 通常ファイル
166: entry = LoadFile(filepath, file, filesize, target_mid);
167: }
168:
169: munmap(file, filesize);
170: close(fd);
171: return entry;
172: }
173:
174: // gzip を展開してゲストにロードする。
175: uint32
176: RAMDevice::LoadGzFile(const char *filepath,
177: const uint8 *infile, size_t infilesize, uint32 target_mid)
178: {
179: z_stream z {};
180: uint32 decompsize;
181: int rv;
182:
183: // gzip なら(?) ファイルの末尾4バイトが (LE で?) 展開後サイズ(?)。
1.1.1.3 ! root 184: decompsize = le32toh(*(const uint32 *)&infile[infilesize - 4]);
1.1.1.2 root 185: std::unique_ptr<uint8[]> dstbuf(new uint8[decompsize]);
186:
187: // gzip 展開
188: // (uncompress() の方が楽そうに見えるが gzip 形式に対応してないらしい)
189: z.zalloc = Z_NULL;
190: z.zfree = Z_NULL;
191: z.opaque = Z_NULL;
192: rv = inflateInit2(&z, 47);
193: if (rv != Z_OK) {
194: warnx("LoadGzFile \"%s\" inflateInit2 failed %d", filepath, rv);
195: return 0;
196: }
197:
1.1.1.3 ! root 198: z.next_in = const_cast<Bytef *>(infile);
1.1.1.2 root 199: z.avail_in = infilesize - 4;
200: z.next_out = dstbuf.get();
201: z.avail_out = decompsize;
202: rv = inflate(&z, Z_NO_FLUSH);
203: if (rv != Z_OK) {
204: warnx("LoadGzFile \"%s\" inflate failed %d", filepath, rv);
205: return 0;
206: }
207:
208: inflateEnd(&z);
209:
210: // 展開できたのでロード
211: return LoadFile(filepath, dstbuf.get(), decompsize, target_mid);
212: }
213:
214: // file, filesize で示されるバッファを実行ファイルとみなしてゲストにロードする。
215: // ファイルのフォーマットは自動で認識する。
216: // target_mid は machine id で、実体は a.out の値を流用してはいるが
217: // ELF であってもこちらで読み替える。
218: // name はここではエラーメッセージとかの表示用でしかない。
219: // 戻り値はエントリポイントアドレス。エラーなら 0 を返す。
220: uint32
221: RAMDevice::LoadFile(const char *name, const uint8 *file, size_t filesize,
222: uint32 target_mid)
223: {
224: uint32 entry = 0;
225:
226: // フォーマットだけ判定して分岐
1.1.1.3 ! root 227: const Elf32_Ehdr *ehdr = (const Elf32_Ehdr *)file;
! 228: const aout_header *aout = (const aout_header *)file;
1.1.1.2 root 229: if (memcmp(ehdr->e_ident, "\177ELF", 4) == 0) {
230: entry = Load_elf32(name, file, filesize, target_mid);
231: } else if ((be32toh(aout->magic) & 0xffff) == AOUT_OMAGIC) {
232: entry = Load_aout(name, file, filesize, target_mid);
233: } else {
234: warnx("LoadFile \"%s\" unknown executable file format", name);
235: }
236:
237: return entry;
238: }
239:
240: // ホストの a.out をゲストの RAM に読み込む。
241: // 戻り値はエントリポイントアドレス。エラーなら 0 を返す。
242: uint32
243: RAMDevice::Load_aout(const char *name, const uint8 *file, size_t filesize,
244: uint32 target_mid)
245: {
246: aout_header aout;
247: int copylen;
248:
249: if (filesize < sizeof(aout)) {
250: warnx("Load_aout \"%s\" file too short", name);
251: return 0;
252: }
253:
254: // ヘッダを読み込む
255: // BigEndian しかターゲットにしてないので最初から全部変換しとく
1.1.1.3 ! root 256: aout.magic = be32toh(((const aout_header *)file)->magic);
! 257: aout.textsize = be32toh(((const aout_header *)file)->textsize);
! 258: aout.datasize = be32toh(((const aout_header *)file)->datasize);
! 259: aout.bsssize = be32toh(((const aout_header *)file)->bsssize);
! 260: aout.symsize = be32toh(((const aout_header *)file)->symsize);
! 261: aout.entry = be32toh(((const aout_header *)file)->entry);
! 262: aout.textrelsize = be32toh(((const aout_header *)file)->textrelsize);
! 263: aout.datarelsize = be32toh(((const aout_header *)file)->datarelsize);
1.1.1.2 root 264:
265: // マジックをチェック
266: uint32 mid = AOUT_MID(aout.magic);
267: if (mid != target_mid) {
268: warnx("Load_aout \"%s\" machine id mismatch (%03x expected but %03x)",
269: name, target_mid, mid);
270: return 0;
271: }
272:
273: putmsg(1, "%s textsize = %08x", __func__, aout.textsize);
274: putmsg(1, "%s datasize = %08x", __func__, aout.datasize);
275: putmsg(1, "%s bsssize = %08x", __func__, aout.bsssize);
276: putmsg(1, "%s symsize = %08x", __func__, aout.symsize);
277: putmsg(1, "%s entry = %08x", __func__, aout.entry);
278: putmsg(1, "%s textrelsize = %08x", __func__, aout.textrelsize);
279: putmsg(1, "%s datarelsize = %08x", __func__, aout.datarelsize);
280:
281: // とりあえず再配置は無視
282: if (aout.textrelsize != 0 || aout.datarelsize != 0) {
283: warnx("Load_aout \"%s\" relocation not supported", name);
284: return 0;
285: }
286:
287: // OpenBSD の boot は text+data が filesize より大きくて何かおかしいが
288: // OMAGIC の a.out は text+data が連続しているだけなので、とりあえず
289: // 何も考えずにファイルサイズ分ロードしておく。
290: copylen = aout.textsize + aout.datasize;
291: if (copylen >= filesize) {
292: copylen = filesize;
293: warnx("warning: Load_aout \"%s\" coruppted? "
294: "(text=%u + data=%u, filesize=%d); loading %d bytes",
295: name, aout.textsize, aout.datasize, (int)filesize, copylen);
296: /* FALLTHROUGH */
297: }
298:
299: // 再配置不要なので、text + data を entry に置いて entry に飛ぶだけ
300: IODeviceStream ds(this, aout.entry);
301: const uint8 *src = file + sizeof(aout);
302: for (int i = 0; i < copylen; i++) {
303: ds.Write8(*src++);
304: }
305:
306: return aout.entry;
307: }
308:
309: // ELF フォーマットのエンディアンからホストエンディアンに変換
310: #define elf16toh(x) ((ehdr->e_ident[EI_DATA] == ELFDATA2MSB) ? \
311: be16toh(x) : le16toh(x))
312: #define elf32toh(x) ((ehdr->e_ident[EI_DATA] == ELFDATA2MSB) ? \
313: be32toh(x) : le32toh(x))
314:
315: // ホストの a.out をゲストの RAM に読み込む。
316: // target_aout_mid は a.out での machine id (機種情報) で、こっちで ELF の
317: // machine type に読み替える。
318: // 戻り値はエントリポイントアドレス。エラーなら 0 を返す。
319: uint32
320: RAMDevice::Load_elf32(const char *name, const uint8 *file, size_t filesize,
321: uint32 target_aout_mid)
322: {
323: uint16 target_elf_mid;
324:
325: // a.out machine id から ELF machine type への読み替え
326: switch (target_aout_mid) {
327: case AOUT_MID_M68K:
328: target_elf_mid = EM_68K;
329: break;
330: case AOUT_MID_M88K:
331: target_elf_mid = EM_88K;
332: break;
333: default:
334: warnx("Load_elf32 unknown target_aout_mid $%x", target_aout_mid);
335: return 0;
336: }
337:
338: // ヘッダをチェック
339: const Elf32_Ehdr *ehdr = (const Elf32_Ehdr *)file;
340: if (filesize < sizeof(*ehdr)) {
341: warnx("Load_elf32 \"%s\" file too short", name);
342: return 0;
343: }
344: uint16 e_type = elf16toh(ehdr->e_type);
345: uint16 e_machine = elf16toh(ehdr->e_machine);
346: uint32 e_entry = elf32toh(ehdr->e_entry);
347: putlog(1, "EI_CLASS = $%02x", ehdr->e_ident[EI_CLASS]);
348: putlog(1, "EI_DATA = $%02x", ehdr->e_ident[EI_DATA]);
349: putlog(1, "e_type = $%02x", e_type);
350: putlog(1, "e_machine = $%02x", e_machine);
351: putlog(1, "e_entry = $%08x", e_entry);
352:
353: if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) {
354: warnx("Load_elf32 \"%s\" not ELF32 format", name);
355: return 0;
356: }
357: if (e_type != ET_EXEC) {
358: warnx("Load_elf32 \"%s\" not executable file", name);
359: return 0;
360: }
361: if (e_machine != target_elf_mid) {
362: warnx("Load_elf32 \"%s\" machine type mismatch "
363: "($%02x expected but $%02x)",
364: name, target_elf_mid, e_machine);
365: return 0;
366: }
367:
368: // プログラムヘッダに読み込むべき領域が示されている
369: // phoff がプログラムヘッダのファイル先頭からのオフセット
370: // phentsize がプログラムヘッダ1つの大きさ
371: // phnum がプログラムヘッダの個数
372: uint32 e_phoff = elf32toh(ehdr->e_phoff);
373: uint16 e_phentsize = elf16toh(ehdr->e_phentsize);
374: uint16 e_phnum = elf16toh(ehdr->e_phnum);
375: putlog(1, "e_phoff = $%08x", e_phoff);
376: putlog(1, "e_phentsize = $%04x", e_phentsize);
377: putlog(1, "e_phnum = %d", e_phnum);
378: if (e_phentsize != sizeof(Elf32_Phdr)) {
379: warnx("Load_elf32 \"%s\" unknown program header size 0x%x",
380: name, e_phentsize);
381: return 0;
382: }
383:
384: IODeviceStream ds(this);
385:
386: for (int j = 0; j < e_phnum; j++) {
387: const Elf32_Phdr *phdr = &((const Elf32_Phdr*)(file + e_phoff))[j];
388: // p_type が PT_LOAD なところをロードする。
389: // その際 p_memsz > p_filesz なら差が BSS 領域。
390: uint32 p_type = elf32toh(phdr->p_type); // タイプ
391: uint32 p_offset = elf32toh(phdr->p_offset); // ファイル上のオフセット
392: uint32 p_vaddr = elf32toh(phdr->p_vaddr); // 仮想アドレス
393: uint32 p_filesz = elf32toh(phdr->p_filesz); // 読み込み元ファイルサイズ
394: uint32 p_memsz = elf32toh(phdr->p_memsz); // 書き込み先サイズ
395: putlog(1, "[%d] p_type = $%08x", j, p_type);
396: putlog(1, "[%d] p_offset = $%08x", j, p_offset);
397: putlog(1, "[%d] p_vaddr = $%08x", j, p_vaddr);
398: putlog(1, "[%d] p_filesz = $%08x", j, p_filesz);
399: putlog(1, "[%d] p_memsz = $%08x", j, p_memsz);
400:
401: if (p_type == PT_LOAD) {
402: ds.SetAddr(p_vaddr);
403: const uint8 *src = file + p_offset;
404: int i = 0;
405: for (; i < p_filesz; i++) {
406: ds.Write8(*src++);
407: }
408: for (; i < p_memsz; i++) {
409: ds.Write8(0);
410: }
1.1.1.3 ! root 411: } else if (p_type == 0x65a3dbe6) { // PT_OPENBSD_RANDOMIZE
! 412: // XXX どうする?
! 413: continue;
! 414:
1.1.1.2 root 415: } else {
416: warnx("Load_elf32 \"%s\" p_type %d not supported", name, p_type);
417: return 0;
418: }
419: }
420:
421: return e_entry;
422: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.