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