File:  [Isaki's NoNo m68k/m88k emulator] / nono / vm / v68kio.cpp
Revision 1.1.1.6 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:06:01 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
nono 1.7.0

//
// nono
// Copyright (C) 2024 nono project
// Licensed under nono-license.txt
//

//
// virt-m68k の I/O 空間担当
//

// メモリマップ。
// virt-m68k は $ff00'0000 以降が I/O 空間で、デバイス配置は
// ブートローダ (実質 BIOS) からカーネルにパラメータとして渡される。
// 各デバイスの先頭番地こそ自由だが、同種のデバイスが連続する場合
// 配置間隔がデバイスによって異なっておりその長さはハードコードのようだ。
// GF* は 0x1000 番地ずつ、VirtIO は 0x200 番地ずつとなっている。
//
// メモリマップはこんな感じにする。
//
// $ff00'0000-$ff00'ffff PROM
// $ff01'xxxx            Goldfish Interrupt(PIC) #1..#6
// $ff01'1000-$ff01'1fff  Lv1
// $ff01'2000-$ff01'2fff  Lv2
// $ff01'3000-$ff01'3fff  Lv3
// $ff01'4000-$ff01'4fff  Lv4
// $ff01'5000-$ff01'5fff  Lv5
// $ff01'6000-$ff01'6fff  Lv6
// $ff02'xxxx            Goldfish RTC+Timer
// $ff02'0000-$ff02'0fff  Timer
// $ff02'1000-$ff02'1fff  RTC
// $ff03'0000-$ff03'0fff Goldfish TTY
// $ff04'0000-$ff04'0fff Qemu Virtual System Controller
// $ff07'xxxx            VirtIO
//
// VirtIO は間隔が違うので最後に置く。
// ここは 1スロット $200 で、最大 128個まで置けるようだ。
// デバイスが存在しないスロットはバスエラーではなく、DEVICE_ID = INVALID
// で応答しなければならない。
//
// $ff07'0000-$ff07'01ff  VirtIO (slot $00)
// $ff07'0200-$ff07'03ff  VirtIO (slot $01)
//  :
// $ff07'fe00-$ff07'ffff  VirtIO (slot $7f)

#include "v68kio.h"
#include "buserr.h"
#include "busio.h"
#include "config.h"
#include "goldfish_pic.h"
#include "goldfish_rtc.h"
#include "goldfish_timer.h"
#include "goldfish_tty.h"
#include "qemusysctlr.h"
#include "romemu_virt68k.h"
#include "virtio_base.h"
#include "virtio_block.h"
#include "virtio_entropy.h"
#include "virtio_net.h"
#include "virtio_scsi.h"
#include "vm.h"

// コンストラクタ
V68kIODevice::V68kIODevice()
	: inherited(OBJ_V68KIO)
{
	NEWDV(PROM, new Virt68kROMEmuDevice());
	NEWDV(GFPIC1, new BusIO_L<GFPICDevice>(1));
	NEWDV(GFPIC2, new BusIO_L<GFPICDevice>(2));
	NEWDV(GFPIC3, new BusIO_L<GFPICDevice>(3));
	NEWDV(GFPIC4, new BusIO_L<GFPICDevice>(4));
	NEWDV(GFPIC5, new BusIO_L<GFPICDevice>(5));
	NEWDV(GFPIC6, new BusIO_L<GFPICDevice>(6));
	NEWDV(GFRTC, new BusIO_L<GFRTCDevice>());		// required by GFTimer
	NEWDV(GFTimer, new BusIO_L<GFTimerDevice>());
	NEWDV(GFTTY, new BusIO_L<GFTTYDevice>());
	NEWDV(QemuSysCtlr, new BusIO_L<QemuSysCtlrDevice>());
	NEWDV(VirtIONone, new BusIO_L<VirtIONoneDevice>());

	buserr = GetBusErrDevice();

	// まず全域をバスエラーで埋める。
	for (int i = 0; i < table.size(); i++) {
		table[i] = buserr;
	}

	// 少ないので手動で埋めていく。
	for (int i = 0; i < 0x10; i++) {
		table[i] = pPROM.get();
	}
	table[0x11] = pGFPIC1.get();
	table[0x12] = pGFPIC2.get();
	table[0x13] = pGFPIC3.get();
	table[0x14] = pGFPIC4.get();
	table[0x15] = pGFPIC5.get();
	table[0x16] = pGFPIC6.get();
	table[0x20] = pGFTimer.get();
	table[0x21] = pGFRTC.get();
	table[0x30] = pGFTTY.get();
	table[0x40] = pQemuSysCtlr.get();

	// VirtIO 用は、設定によるので後で代入する。
	// ここはバスエラーではない。
	for (int i = 0; i < viotable.size(); i++) {
		viotable[i] = pVirtIONone.get();
	}
}

// デストラクタ
V68kIODevice::~V68kIODevice()
{
}

// 動的なコンストラクション
bool
V68kIODevice::Create()
{
	uint slot;

	// (QEMU virt-m68k の?)
	// virtio はスロットによって割り込み先を固定してあるようだ。
	// スロット  0 .. 31 は PIC2(Lv2) の IRQ1..32、
	// スロット 32 .. 63 は PIC3(Lv3) の IRQ1..32、
	// スロット 64 .. 95 は PIC4(Lv4) の IRQ1..32、
	// スロット 96 ..127 は PIC5(Lv5) の IRQ1..32 となる。
	//
	// VirtIO* はとりあえず先例にならって Lv5 につなぐことにしてみる。
	slot = 96;

	// Net
	if (gConfig->Find("virtio-net").AsBool()) {
		try {
			pVirtIO[slot].reset(new BusIO_L<VirtIONetDevice>(slot));
		} catch (...) { }
		if ((bool)pVirtIO[slot] == false) {
			warnx("Failed to initialize VirtIONetDevice at %s", __method__);
			return false;
		}
		viotable[slot] = pVirtIO[slot].get();
		slot++;
	}

	// Entropy
	if (gConfig->Find("virtio-entropy").AsBool()) {
		try {
			pVirtIO[slot].reset(new BusIO_L<VirtIOEntropyDevice>(slot));
		} catch (...) { }
		if ((bool)pVirtIO[slot] == false) {
			warnx("Failed to initialize VirtIOEntropyDevice at %s", __method__);
			return false;
		}
		viotable[slot] = pVirtIO[slot].get();
		slot++;
	}

	// Block (とりあえず8個まで)
	for (int id = 0; id < 8; id++) {
		const std::string key = string_format("virtio-block%u-image", id);
		const ConfigItem& item = gConfig->Find(key);
		const std::string& image = item.AsString();

		// 空ならデバイスなし
		if (image.empty()) {
			// 未接続部分のパラメータは減らしておくか。
			// --show-config するとさすがに無駄に多くて邪魔なので。
			gConfig->Delete(string_format("virtio-block%u-writeignore", id));
			continue;
		}

		try {
			pVirtIO[slot].reset(new BusIO_L<VirtIOBlockDevice>(slot, id));
		} catch (...) { }
		if ((bool)pVirtIO[slot] == false) {
			warnx("Failed to initialize VirtIOBlockDevice(%u) at %s",
				id, __method__);
			return false;
		}
		viotable[slot] = pVirtIO[slot].get();
		slot++;
	}

	// SCSI
	if (gConfig->Find("virtio-scsi").AsBool()) {
		pVirtIO[slot].reset(new BusIO_L<VirtIOSCSIDevice>(slot));
		if ((bool)pVirtIO[slot] == false) {
			warnx("Failed to initialize VirtIOSCSIDevice at %s", __method__);
			return false;
		}
		viotable[slot] = pVirtIO[slot].get();
		slot++;
	}

	return true;
}

inline IODevice *
V68kIODevice::SearchDevice(uint32 addr) const
{
	if (__predict_true(addr < 0xff07'0000)) {
		// FF0X'X000
		uint32 idx = (addr >> 12) & 0x7f;
		return table[idx];
	} else if (__predict_true(addr < 0xff08'0000)) {
		// FF07'XX00
		uint32 idx = (addr >> 9) & 0x7f;
		return viotable[idx];
	} else {
		return buserr;
	}
}

busdata
V68kIODevice::Read(busaddr addr)
{
	IODevice *d = SearchDevice(addr.Addr());
	return d->Read(addr);
}

busdata
V68kIODevice::Write(busaddr addr, uint32 data)
{
	IODevice *d = SearchDevice(addr.Addr());
	return d->Write(addr, data);
}

busdata
V68kIODevice::Peek1(uint32 addr)
{
	IODevice *d = SearchDevice(addr);
	return d->Peek1(addr);
}

bool
V68kIODevice::Poke1(uint32 addr, uint32 data)
{
	IODevice *d = SearchDevice(addr);
	return d->Poke1(addr, data);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.