Annotation of nono/vm/ram.cpp, revision 1.1.1.2

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.