|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.