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

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

unix.superglobalmegacorp.com

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