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