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

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

unix.superglobalmegacorp.com

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