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

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

unix.superglobalmegacorp.com

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