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