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

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2024 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
                      7: //
1.1.1.2 ! root        8: // virt-m68k の IPLROM 相当の何か
1.1       root        9: //
                     10: 
1.1.1.2 ! root       11: // IODevice
        !            12: //  |
        !            13: //  +- ROMDevice (LoadROM()、ウェイト、マスク等を持つ)
        !            14: //  |   +- PROMDevice    (LUNA* の PROM)
        !            15: //  |   +- IPLROM1Device (X680x0 の IPLROM 後半)
        !            16: //  |   +- IPLROM2Device (X680x0 の IPLROM 前半)
        !            17: //  |   +- CGROMDevice   (X680x0 の CGROM)
        !            18: //  |   |
        !            19: //  |   +- ROMEmuDevice
        !            20: //  |       +- LunaPROMEmuDevice (LUNA PROM エミュレーションの共通部分)
        !            21: //  |       |   +- Luna1PROMEmuDevice   (LUNA-I の PROM エミュレーション)
        !            22: //  |       |   +- Luna88kPROMEmuDevice (LUNA-88K の PROM エミュレーション)
        !            23: //  |       +- NewsROMEmuDevice (NEWS の ROM エミュレーション)
        !            24: //  |       +- ROM30EmuDevice   (X68030 の ROM30 エミュレーション)
        !            25: //  |       |
        !            26: //  |       | +---------------------+
        !            27: //  |       +-| Virt68kROMEmuDevice | (virt-m68k の IPLROM 相当の何か)
        !            28: //  |         +---------------------+
        !            29: //  |
        !            30: //  +- PROM0Device   (LUNA* のブートページ切り替え用プロキシ)
        !            31: //  +- IPLROM0Device (X680x0 のブートページ切り替え用プロキシ)
        !            32: 
1.1       root       33: #include "romemu_virt68k.h"
                     34: #include "autofd.h"
                     35: #include "config.h"
1.1.1.2 ! root       36: #include "iodevstream.h"
1.1       root       37: #include "mainapp.h"
                     38: #include "mainram.h"
                     39: #include "memorystream.h"
                     40: #include <fcntl.h>
                     41: #include <sys/stat.h>
                     42: #include <sys/mman.h>
                     43: #include <random>
                     44: 
                     45: // コンストラクタ
                     46: Virt68kROMEmuDevice::Virt68kROMEmuDevice()
                     47:        : inherited(OBJ_PROM)
                     48: {
                     49:        // 適当
                     50:        uint devlen = 4096;
                     51:        imagebuf.reset(new uint8 [devlen]);
                     52:        memset(&imagebuf[0], 0, devlen);
                     53: 
                     54:        mem = &imagebuf[0];
                     55:        mask = devlen - 1;
                     56: }
                     57: 
                     58: // デストラクタ
                     59: Virt68kROMEmuDevice::~Virt68kROMEmuDevice()
                     60: {
                     61: }
                     62: 
                     63: // 初期化
                     64: bool
                     65: Virt68kROMEmuDevice::Init()
                     66: {
                     67:        if (inherited::Init() == false) {
                     68:                return false;
                     69:        }
                     70: 
                     71:        mainram = GetMainRAMDevice();
                     72: 
1.1.1.2 ! root       73:        MemoryStreamBE roms(imagebuf.get());
        !            74:        roms.Write4(0x00001000);        // +00: リセットベクタ SP
        !            75:        roms.Write4(0xff000400);        // +04: リセットベクタ PC
1.1       root       76:        // ベクタ
                     77:        for (int i = 2; i < 256; i++) {
1.1.1.2 ! root       78:                roms.Write4(0);
1.1       root       79:        }
                     80: 
                     81:        // リセット時に実行する命令
1.1.1.2 ! root       82:        roms.SetOffset(0x400);
        !            83:        roms.Write2(0x2039);            //      move.l  (ROMIO_INIT),d0
        !            84:        roms.Write4(ROMIO_INIT);
        !            85:        roms.Write2(0x0c80);            //      cmpi.l  #0xffffffff,d0
        !            86:        roms.Write4(0xffffffff);
        !            87:        roms.Write2(0x6704);            //      beq             @f
        !            88:        roms.Write2(0x2040);            //      move.l  d0,a0   ; ジャンプ先を a0 にセット
        !            89:        roms.Write2(0x4ed0);            //      jmp             (a0)    ; ジャンプ
1.1       root       90:                                                                //@@:
1.1.1.2 ! root       91:        roms.Write4(0x4e722700);        //      stop    #0x2700
        !            92:        roms.Write2(0x67fc);            //      bra             @b
1.1       root       93: 
                     94:        return true;
                     95: }
                     96: 
                     97: void
                     98: Virt68kROMEmuDevice::ResetHard(bool poweron)
                     99: {
                    100:        // 起動時に RAM にリセットベクタを書き込んでおく。(手抜き)
1.1.1.2 ! root      101:        mainram->WriteMem(0, &imagebuf[0], 8);
1.1       root      102: }
                    103: 
1.1.1.2 ! root      104: // ROMIO から読み出す。
        !           105: // ROMIO が応答すべきでなければ (uint64)-1 を返す。
        !           106: uint64
        !           107: Virt68kROMEmuDevice::ReadROMIO(busaddr addr)
1.1       root      108: {
1.1.1.2 ! root      109:        if (addr.GetSize() == 4) {
        !           110:                switch (addr.Addr()) {
        !           111:                 case ROMIO_INIT:
        !           112:                        return ROM_Init();
1.1       root      113: 
1.1.1.2 ! root      114:                 default:
        !           115:                        break;
        !           116:                }
1.1       root      117:        }
                    118: 
1.1.1.2 ! root      119:        return (uint64)-1;
1.1       root      120: }
                    121: 
                    122: uint32
                    123: Virt68kROMEmuDevice::ROM_Init()
                    124: {
                    125:        // -X file で指定されたホストファイルを読み込んでエントリポイント
                    126:        // アドレスをこの Read アクセスの読み出し値として返す。
                    127: 
                    128:        LoadInfo info(gMainApp.exec_file);
                    129:        // QEMU がシンボルテーブルをロードしないので、
                    130:        // カーネル側もそのように想定しているようだ。
                    131:        info.loadsym = false;
                    132: 
                    133:        if (mainram->LoadExec(&info) == false) {
                    134:                warnx("** Couldn't load specified host program.");
                    135:                return -1;
                    136:        }
                    137: 
                    138:        if (MakeBootInfo(&info) == false) {
                    139:                return -1;
                    140:        }
                    141: 
                    142:        putmsg(1, "Host program loaded.  Entry point = $%08x", info.entry);
                    143: 
                    144:        return info.entry;
                    145: }
                    146: 
                    147: // ブート情報を便利に扱うためのクラス。
                    148: class BootInfo
                    149: {
                    150:  public:
                    151:        uint16 bi_tag {};                               // タグ
                    152:        std::vector<uint8> bi_data {};  // タグごと決まる可変のデータ
                    153: 
                    154:  public:
                    155:        BootInfo();
                    156: 
                    157:        void Init(uint16 tag_);
                    158:        void Init(uint16 tag_, uint32 val_);
                    159:        void AddWord(uint32 val_);
                    160:        void AddLong(uint32 val_);
                    161: 
                    162:        uint32 GetWord(uint32 offset) const;
                    163:        uint32 GetLong(uint32 offset) const;
                    164: };
                    165: 
                    166: // コンストラクタ
                    167: BootInfo::BootInfo()
                    168: {
                    169: }
                    170: 
                    171: // タグだけ指定して初期化。
                    172: void
                    173: BootInfo::Init(uint16 tag_)
                    174: {
                    175:        bi_tag = tag_;
                    176:        bi_data.clear();
                    177: }
                    178: 
                    179: // タグと整数1つを指定して初期化。
                    180: void
                    181: BootInfo::Init(uint16 tag_, uint32 val_)
                    182: {
                    183:        Init(tag_);
                    184:        AddLong(val_);
                    185: }
                    186: 
                    187: // データに 16 ビット整数を1つ追加。
                    188: void
                    189: BootInfo::AddWord(uint32 val_)
                    190: {
                    191:        // bi_data はビッグエンディアン
                    192:        bi_data.push_back(val_ >> 8);
                    193:        bi_data.push_back(val_);
                    194: }
                    195: 
                    196: // データに 32 ビット整数を1つ追加。
                    197: void
                    198: BootInfo::AddLong(uint32 val_)
                    199: {
                    200:        // bi_data はビッグエンディアン
                    201:        bi_data.push_back(val_ >> 24);
                    202:        bi_data.push_back(val_ >> 16);
                    203:        bi_data.push_back(val_ >> 8);
                    204:        bi_data.push_back(val_);
                    205: }
                    206: 
                    207: // offset の位置からの 16 ビット整数を取得。
                    208: uint32
                    209: BootInfo::GetWord(uint32 offset) const
                    210: {
                    211:        if (offset + 2 > bi_data.size()) {
                    212:                return -1;
                    213:        }
                    214:        uint32 data;
                    215:        data  = bi_data[offset + 0] << 8;
                    216:        data |= bi_data[offset + 1];
                    217:        return data;
                    218: }
                    219: 
                    220: // offset の位置からの 32 ビット整数を取得。
                    221: uint32
                    222: BootInfo::GetLong(uint32 offset) const
                    223: {
                    224:        if (offset + 4 > bi_data.size()) {
                    225:                return -1;
                    226:        }
                    227:        uint32 data;
                    228:        data  = GetWord(offset + 0) << 16;
                    229:        data |= GetWord(offset + 2);
                    230:        return data;
                    231: }
                    232: 
                    233: 
                    234: // BootInfo について。
                    235: //
                    236: // 伝統的な(?) NetBSD アーキテクチャは、ブートローダからカーネルへの
                    237: // パラメータはレジスタ渡しかスタック渡しでアーキテクチャごとに固定の
                    238: // フォーマットになっている。少なくともいくつかの m68k では。
1.1.1.2 ! root      239: // NetBSD/virt68k では Linux/m68k の方式を採用しているようだが、
        !           240: // これは virt-m68k がそういう仕様だから。
1.1       root      241: //
                    242: // NetBSD/virt68k のブートパラメータ BootInfo は、カーネルがロードされた
                    243: // セクションの末尾 (リンカによってアドレスが求まる) 以降の次のロングワード
                    244: // 境界から連続して配置する。BootInfo はいわゆる Type-Length-Value (TLV)
                    245: // 形式のデータで、基本フォーマットは次の通り。
                    246: //
                    247: //  struct {
                    248: //    uint16 bi_tag;
                    249: //    uint16 bi_size;
                    250: //    uint8[] bi_data;
                    251: //  };
                    252: //
                    253: // bi_tag がこのパラメータの種別を表し、BI_LAST(=0) のタグで終了する。
                    254: // bi_size はタグとサイズフィールドも含めたバイト数。
                    255: // bi_data は 0 バイト以上の可変長データで、内容はタグによって決まっている。
                    256: // 各パラメータはロングワード境界に整列していること。
                    257: //
                    258: // これによって、ブートローダというかエミュレータ本体から自由度の高い
                    259: // パラメータを受け渡すことが出来る。例えば通常の m68k カーネルが自力で
                    260: // 判別している CPU 種別や FPU 種別、デバイスがどのアドレス・IRQ にあるか
                    261: // といったハードウェア構成情報もすべてブートパラメータで渡す。
                    262: 
                    263: // ブートパラメータを用意。
                    264: bool
                    265: Virt68kROMEmuDevice::MakeBootInfo(LoadInfo *info)
                    266: {
                    267:        IODeviceStream ds(mainram, info->end);
                    268:        BootInfo bi;
                    269: 
                    270:        bi.Init(BI_MACHTYPE, BI_MACH_VIRT);
                    271:        WriteBootInfo(ds, bi);
                    272: 
                    273:        bi.Init(BI_CPUTYPE, BI_CPU_68030);
                    274:        WriteBootInfo(ds, bi);
                    275: 
                    276:        bi.Init(BI_FPUTYPE, BI_FPU_68881);
                    277:        WriteBootInfo(ds, bi);
                    278: 
                    279:        bi.Init(BI_MMUTYPE, BI_MMU_68030);
                    280:        WriteBootInfo(ds, bi);
                    281: 
                    282:        uint32 ramsize = mainram->GetSize();
                    283: 
                    284:        // RAMDISK は (あれば) initrd イメージ置き場で、
                    285:        // メモリセグメントとは別セグメントにする必要がある。
                    286:        // そのためここでは MainRAM の末尾に置く。
                    287:        const ConfigItem& initrd_item = gConfig->Find("exec-initrd");
                    288:        const std::string& initrd = initrd_item.AsString();
                    289:        if (initrd.empty() == false) {
                    290:                std::string path = gMainApp.NormalizePath(initrd);
                    291:                const char *cpath = path.c_str();
                    292:                autofd fd;
                    293:                struct stat st;
                    294:                uint8 *mdata;
                    295:                size_t rdsize;
                    296: 
                    297:                fd = open(cpath, O_RDONLY);
                    298:                if (fd == -1) {
                    299:                        warn("%s \"%s\" open failed", __func__, cpath);
                    300:                        return false;
                    301:                }
                    302: 
                    303:                if (fstat(fd, &st) == -1) {
                    304:                        warn("%s \"%s\" fstat failed", __func__, cpath);
                    305:                        return false;
                    306:                }
                    307: 
                    308:                if (S_ISDIR(st.st_mode)) {
                    309:                        warnx("%s \"%s\" is a directory", __func__, cpath);
                    310:                        return false;
                    311:                }
                    312:                rdsize = st.st_size;
                    313: 
                    314:                mdata = (uint8 *)mmap(NULL, rdsize, PROT_READ, MAP_PRIVATE, fd, 0);
                    315:                if (mdata == MAP_FAILED) {
                    316:                        warnx("%s \"%s\" mmap failed", __func__, cpath);
                    317:                        return false;
                    318:                }
                    319: 
                    320:                // ロード。
                    321:                LoadInfo rdinfo(cpath, mdata, rdsize);
                    322:                rdinfo.start = (ramsize - rdsize) & ~0xfff;
                    323:                bool rv = mainram->LoadData(&rdinfo);
                    324:                munmap(mdata, rdsize);
                    325:                if (rv == false) {
                    326:                        warnx("** Couldn't load specified initrd.");
                    327:                        return false;
                    328:                }
                    329: 
                    330:                bi.Init(BI_RAMDISK);
                    331:                bi.AddLong(rdinfo.start);       // bi_mem_info.addr
                    332:                bi.AddLong(rdsize);                     // bi_mem_info.size
                    333:                WriteBootInfo(ds, bi);
                    334: 
                    335:                // ここをメモリ上限にする。
                    336:                ramsize = rdinfo.start;
                    337:        }
                    338: 
                    339:        // MEMCHUNK は RAM 配置情報。
                    340:        bi.Init(BI_MEMCHUNK);
                    341:        bi.AddLong(0);                  // bi_mem_info.addr
                    342:        bi.AddLong(ramsize);    // bi_mem_info.size
                    343:        WriteBootInfo(ds, bi);
                    344: 
                    345:        // 乱数シード。QEMU は 32 バイト渡してるようだ。
                    346:        std::random_device rng;
                    347:        const uint32 rng_bytes = 32;
                    348:        bi.Init(BI_RNG_SEED);
                    349:        bi.AddWord(rng_bytes);  // bi_data.data_length
                    350:        for (int i = 0; i < rng_bytes / 4; i++) {
                    351:                bi.AddLong(rng());      // bi_data.data_bytes
                    352:        }
                    353:        WriteBootInfo(ds, bi);
                    354: 
                    355:        // GFPIC
                    356:        // 先頭のデバイスのアドレスとベース IRQ だけ指定する。
                    357:        // アドレスを +$1000 番地、IRQ は +1 ずつずらして計 6個あるというのは
                    358:        // カーネル側にハードコードしてあるようだ。
                    359:        //  $ff01'1000 IRQ=1 : GFPIC1
                    360:        //  $ff01'2000 IRQ=2 : GFPIC2
                    361:        //  $ff01'3000 IRQ=3 : GFPIC3
                    362:        //  $ff01'4000 IRQ=4 : GFPIC4
                    363:        //  $ff01'5000 IRQ=5 : GFPIC5
                    364:        //  $ff01'6000 IRQ=6 : GFPIC6
                    365:        bi.Init(BI_GFPIC);
                    366:        bi.AddLong(0xff011000); // addr
                    367:        bi.AddLong(1);                  // irq
                    368:        WriteBootInfo(ds, bi);
                    369: 
                    370:        // GFRTC
                    371:        // 先頭アドレスと IRQ だけ指定する。
                    372:        // アドレスを +$1000 番地したところに GFRTC があるというのは
                    373:        // カーネル側にハードコードしてあるようだ。
                    374:        //  $ff02'0000 IRQ=PIC6:IRQ1 : GFTimer
                    375:        //  $ff02'1000               : GFRTC
                    376:        bi.Init(BI_GFRTC);
                    377:        bi.AddLong(0xff020000);                         // addr
                    378:        bi.AddLong(8 + (6 - 1) * 32 + 0);       // irq
                    379:        WriteBootInfo(ds, bi);
                    380: 
                    381:        // GFTTY
                    382:        // IRQ は PIC1:IRQ32
                    383:        bi.Init(BI_GFTTY);
                    384:        bi.AddLong(0xff030000);                         // addr
                    385:        bi.AddLong(8 + (1 - 1) * 32 + 31);      // irq
                    386:        WriteBootInfo(ds, bi);
                    387: 
                    388:        // QemuSysCtlr
                    389:        bi.Init(BI_CTRL);
                    390:        bi.AddLong(0xff040000);                         // addr
                    391:        bi.AddLong(0);                                          // irq
                    392:        WriteBootInfo(ds, bi);
                    393: 
                    394:        // VirtIO
                    395:        bi.Init(BI_VIRTIO);
                    396:        bi.AddLong(0xff070000);                         // addr
                    397:        bi.AddLong(8 + (2 - 1) * 32 + 0);       // base irq
                    398:        WriteBootInfo(ds, bi);
                    399: 
                    400:        bi.Init(BI_LAST);
                    401:        WriteBootInfo(ds, bi);
                    402: 
                    403:        return true;
                    404: }
                    405: 
                    406: #define ENTRY(x)       { (x),  #x }
                    407: /*static*/ const std::pair<uint32, const char *>
                    408: Virt68kROMEmuDevice::tag_str[] = {
                    409:        ENTRY(BI_LAST),
                    410:        ENTRY(BI_MACHTYPE),
                    411:        ENTRY(BI_CPUTYPE),
                    412:        ENTRY(BI_FPUTYPE),
                    413:        ENTRY(BI_MMUTYPE),
                    414:        ENTRY(BI_MEMCHUNK),
                    415:        ENTRY(BI_RAMDISK),
                    416:        ENTRY(BI_COMMANDLINE),
                    417:        ENTRY(BI_RNG_SEED),
                    418: 
                    419:        ENTRY(BI_QEMU_VER),
                    420:        ENTRY(BI_GFPIC),
                    421:        ENTRY(BI_GFRTC),
                    422:        ENTRY(BI_GFTTY),
                    423:        ENTRY(BI_VIRTIO),
                    424:        ENTRY(BI_CTRL),
                    425: };
                    426: 
                    427: /*static*/ const std::pair<uint32, const char *>
                    428: Virt68kROMEmuDevice::mach_str[] = {
                    429:        ENTRY(BI_MACH_VIRT),
                    430: };
                    431: 
                    432: /*static*/ const std::pair<uint32, const char *>
                    433: Virt68kROMEmuDevice::cpu_str[] = {
                    434:        ENTRY(BI_CPU_68020),
                    435:        ENTRY(BI_CPU_68030),
                    436:        ENTRY(BI_CPU_68040),
                    437:        ENTRY(BI_CPU_68060),
                    438: };
                    439: 
                    440: /*static*/ const std::pair<uint32, const char *>
                    441: Virt68kROMEmuDevice::fpu_str[] = {
                    442:        ENTRY(BI_FPU_68881),
                    443:        ENTRY(BI_FPU_68882),
                    444:        ENTRY(BI_FPU_68040),
                    445:        ENTRY(BI_FPU_68060),
                    446: };
                    447: 
                    448: /*static*/ const std::pair<uint32, const char *>
                    449: Virt68kROMEmuDevice::mmu_str[] = {
                    450:        ENTRY(BI_MMU_68851),
                    451:        ENTRY(BI_MMU_68030),
                    452:        ENTRY(BI_MMU_68040),
                    453:        ENTRY(BI_MMU_68060),
                    454: };
                    455: #undef ENTRY
                    456: 
                    457: #define GetStr(array, v)       GetStr1(array, countof(array), v)
                    458: static std::string
                    459: GetStr1(const std::pair<uint32, const char *> *map, uint mapcount, uint32 v)
                    460: {
                    461:        for (int i = 0; i < mapcount; i++) {
                    462:                if (map[i].first == v) {
                    463:                        return map[i].second;
                    464:                }
                    465:        }
                    466:        return string_format("(%u)", v);
                    467: }
                    468: 
                    469: // BootInfo を stream に書き出す。
                    470: void
                    471: Virt68kROMEmuDevice::WriteBootInfo(IODeviceStream& stream, BootInfo& bi)
                    472: {
                    473:        // ロングワード境界にパディング。
                    474:        uint16 size = 2 + 2 + bi.bi_data.size();
                    475:        for (; (size % 4) != 0; size++) {
                    476:                bi.bi_data.push_back(0);
                    477:        }
                    478: 
                    479:        // デバッグ表示。
                    480:        if (__predict_false(loglevel >= 1)) {
                    481:                std::string tagname = GetStr(tag_str, bi.bi_tag);
                    482:                std::string buf;
                    483:                switch (bi.bi_tag) {
                    484:                 case BI_LAST:
                    485:                        break;
                    486:                 case BI_MACHTYPE:
                    487:                        buf = GetStr(mach_str, bi.GetLong(0));
                    488:                        break;
                    489:                 case BI_CPUTYPE:
                    490:                        buf = GetStr(cpu_str, bi.GetLong(0));
                    491:                        break;
                    492:                 case BI_FPUTYPE:
                    493:                        buf = GetStr(fpu_str, bi.GetLong(0));
                    494:                        break;
                    495:                 case BI_MMUTYPE:
                    496:                        buf = GetStr(mmu_str, bi.GetLong(0));
                    497:                        break;
                    498:                 case BI_MEMCHUNK:
                    499:                 case BI_RAMDISK:
                    500:                        buf = string_format("addr=$%08x, len=$%08x",
                    501:                                bi.GetLong(0), bi.GetLong(4));
                    502:                        break;
                    503:                 case BI_GFPIC:
                    504:                 case BI_GFRTC:
                    505:                 case BI_GFTTY:
                    506:                 case BI_VIRTIO:
                    507:                 case BI_CTRL:
                    508:                        buf = string_format("addr=$%08x, irq=$%08x",
                    509:                                bi.GetLong(0), bi.GetLong(4));
                    510:                        break;
                    511:                 case BI_RNG_SEED:
                    512:                        buf = string_format("len=%u", bi.GetWord(0));
                    513:                        break;
                    514:                }
                    515:                putmsg(1, "BootInfo $%08x: %-14s: %s",
1.1.1.2 ! root      516:                        stream.GetOffset(), tagname.c_str(), buf.c_str());
1.1       root      517:        }
                    518: 
                    519:        // 出力。
1.1.1.2 ! root      520:        stream.Write2(bi.bi_tag);
        !           521:        stream.Write2(size);
1.1       root      522:        for (const auto c : bi.bi_data) {
1.1.1.2 ! root      523:                stream.Write1(c);
1.1       root      524:        }
                    525: }

unix.superglobalmegacorp.com

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