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