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

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

unix.superglobalmegacorp.com

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