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