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