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