aboutsummaryrefslogtreecommitdiffstats
path: root/hw/m68k
diff options
context:
space:
mode:
Diffstat (limited to 'hw/m68k')
-rw-r--r--hw/m68k/Kconfig34
-rw-r--r--hw/m68k/an5206.c102
-rw-r--r--hw/m68k/bootinfo.h59
-rw-r--r--hw/m68k/mcf5206.c629
-rw-r--r--hw/m68k/mcf5208.c363
-rw-r--r--hw/m68k/mcf_intc.c217
-rw-r--r--hw/m68k/meson.build8
-rw-r--r--hw/m68k/next-cube.c1056
-rw-r--r--hw/m68k/next-kbd.c289
-rw-r--r--hw/m68k/q800.c711
-rw-r--r--hw/m68k/virt.c324
11 files changed, 3792 insertions, 0 deletions
diff --git a/hw/m68k/Kconfig b/hw/m68k/Kconfig
new file mode 100644
index 000000000..f839f8a03
--- /dev/null
+++ b/hw/m68k/Kconfig
@@ -0,0 +1,34 @@
+config AN5206
+ bool
+ select COLDFIRE
+ select PTIMER
+
+config MCF5208
+ bool
+ select COLDFIRE
+ select PTIMER
+
+config NEXTCUBE
+ bool
+ select FRAMEBUFFER
+ select ESCC
+
+config Q800
+ bool
+ select MAC_VIA
+ select NUBUS
+ select MACFB
+ select SWIM
+ select ESCC
+ select ESP
+ select DP8393X
+ select OR_IRQ
+
+config M68K_VIRT
+ bool
+ select M68K_IRQC
+ select VIRT_CTRL
+ select GOLDFISH_PIC
+ select GOLDFISH_TTY
+ select GOLDFISH_RTC
+ select VIRTIO_MMIO
diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c
new file mode 100644
index 000000000..11ae4c979
--- /dev/null
+++ b/hw/m68k/an5206.c
@@ -0,0 +1,102 @@
+/*
+ * Arnewsh 5206 ColdFire system emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licensed under the GPL
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "hw/m68k/mcf.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "qemu/error-report.h"
+#include "sysemu/qtest.h"
+
+#define KERNEL_LOAD_ADDR 0x10000
+#define AN5206_MBAR_ADDR 0x10000000
+#define AN5206_RAMBAR_ADDR 0x20000000
+
+static void mcf5206_init(MemoryRegion *sysmem, uint32_t base)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_new(TYPE_MCF5206_MBAR);
+ s = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(s, &error_fatal);
+
+ memory_region_add_subregion(sysmem, base, sysbus_mmio_get_region(s, 0));
+}
+
+static void an5206_init(MachineState *machine)
+{
+ ram_addr_t ram_size = machine->ram_size;
+ const char *kernel_filename = machine->kernel_filename;
+ M68kCPU *cpu;
+ CPUM68KState *env;
+ int kernel_size;
+ uint64_t elf_entry;
+ hwaddr entry;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *sram = g_new(MemoryRegion, 1);
+
+ cpu = M68K_CPU(cpu_create(machine->cpu_type));
+ env = &cpu->env;
+
+ /* Initialize CPU registers. */
+ env->vbr = 0;
+ /* TODO: allow changing MBAR and RAMBAR. */
+ env->mbar = AN5206_MBAR_ADDR | 1;
+ env->rambar0 = AN5206_RAMBAR_ADDR | 1;
+
+ /* DRAM at address zero */
+ memory_region_add_subregion(address_space_mem, 0, machine->ram);
+
+ /* Internal SRAM. */
+ memory_region_init_ram(sram, NULL, "an5206.sram", 512, &error_fatal);
+ memory_region_add_subregion(address_space_mem, AN5206_RAMBAR_ADDR, sram);
+
+ mcf5206_init(address_space_mem, AN5206_MBAR_ADDR);
+
+ /* Load kernel. */
+ if (!kernel_filename) {
+ if (qtest_enabled()) {
+ return;
+ }
+ error_report("Kernel image must be specified");
+ exit(1);
+ }
+
+ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry,
+ NULL, NULL, NULL, 1, EM_68K, 0, 0);
+ entry = elf_entry;
+ if (kernel_size < 0) {
+ kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
+ NULL, NULL);
+ }
+ if (kernel_size < 0) {
+ kernel_size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR,
+ ram_size - KERNEL_LOAD_ADDR);
+ entry = KERNEL_LOAD_ADDR;
+ }
+ if (kernel_size < 0) {
+ error_report("Could not load kernel '%s'", kernel_filename);
+ exit(1);
+ }
+
+ env->pc = entry;
+}
+
+static void an5206_machine_init(MachineClass *mc)
+{
+ mc->desc = "Arnewsh 5206";
+ mc->init = an5206_init;
+ mc->default_cpu_type = M68K_CPU_TYPE_NAME("m5206");
+ mc->default_ram_id = "an5206.ram";
+}
+
+DEFINE_MACHINE("an5206", an5206_machine_init)
diff --git a/hw/m68k/bootinfo.h b/hw/m68k/bootinfo.h
new file mode 100644
index 000000000..adbf0c552
--- /dev/null
+++ b/hw/m68k/bootinfo.h
@@ -0,0 +1,59 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+ *
+ * Bootinfo tags from linux bootinfo.h and bootinfo-mac.h:
+ * This is an easily parsable and extendable structure containing all
+ * information to be passed from the bootstrap to the kernel
+ *
+ * This structure is copied right after the kernel by the bootstrap
+ * routine.
+ */
+
+#ifndef HW_M68K_BOOTINFO_H
+#define HW_M68K_BOOTINFO_H
+
+#define BOOTINFO0(as, base, id) \
+ do { \
+ stw_phys(as, base, id); \
+ base += 2; \
+ stw_phys(as, base, sizeof(struct bi_record)); \
+ base += 2; \
+ } while (0)
+
+#define BOOTINFO1(as, base, id, value) \
+ do { \
+ stw_phys(as, base, id); \
+ base += 2; \
+ stw_phys(as, base, sizeof(struct bi_record) + 4); \
+ base += 2; \
+ stl_phys(as, base, value); \
+ base += 4; \
+ } while (0)
+
+#define BOOTINFO2(as, base, id, value1, value2) \
+ do { \
+ stw_phys(as, base, id); \
+ base += 2; \
+ stw_phys(as, base, sizeof(struct bi_record) + 8); \
+ base += 2; \
+ stl_phys(as, base, value1); \
+ base += 4; \
+ stl_phys(as, base, value2); \
+ base += 4; \
+ } while (0)
+
+#define BOOTINFOSTR(as, base, id, string) \
+ do { \
+ int i; \
+ stw_phys(as, base, id); \
+ base += 2; \
+ stw_phys(as, base, \
+ (sizeof(struct bi_record) + strlen(string) + 2) & ~1); \
+ base += 2; \
+ for (i = 0; string[i]; i++) { \
+ stb_phys(as, base++, string[i]); \
+ } \
+ stb_phys(as, base++, 0); \
+ base = (parameters_base + 1) & ~1; \
+ } while (0)
+#endif
diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c
new file mode 100644
index 000000000..6d93d761a
--- /dev/null
+++ b/hw/m68k/mcf5206.c
@@ -0,0 +1,629 @@
+/*
+ * Motorola ColdFire MCF5206 SoC embedded peripheral emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licensed under the GPL
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "cpu.h"
+#include "hw/boards.h"
+#include "hw/irq.h"
+#include "hw/m68k/mcf.h"
+#include "qemu/timer.h"
+#include "hw/ptimer.h"
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+
+/* General purpose timer module. */
+typedef struct {
+ uint16_t tmr;
+ uint16_t trr;
+ uint16_t tcr;
+ uint16_t ter;
+ ptimer_state *timer;
+ qemu_irq irq;
+ int irq_state;
+} m5206_timer_state;
+
+#define TMR_RST 0x01
+#define TMR_CLK 0x06
+#define TMR_FRR 0x08
+#define TMR_ORI 0x10
+#define TMR_OM 0x20
+#define TMR_CE 0xc0
+
+#define TER_CAP 0x01
+#define TER_REF 0x02
+
+static void m5206_timer_update(m5206_timer_state *s)
+{
+ if ((s->tmr & TMR_ORI) != 0 && (s->ter & TER_REF))
+ qemu_irq_raise(s->irq);
+ else
+ qemu_irq_lower(s->irq);
+}
+
+static void m5206_timer_reset(m5206_timer_state *s)
+{
+ s->tmr = 0;
+ s->trr = 0;
+}
+
+static void m5206_timer_recalibrate(m5206_timer_state *s)
+{
+ int prescale;
+ int mode;
+
+ ptimer_transaction_begin(s->timer);
+ ptimer_stop(s->timer);
+
+ if ((s->tmr & TMR_RST) == 0) {
+ goto exit;
+ }
+
+ prescale = (s->tmr >> 8) + 1;
+ mode = (s->tmr >> 1) & 3;
+ if (mode == 2)
+ prescale *= 16;
+
+ if (mode == 3 || mode == 0) {
+ qemu_log_mask(LOG_UNIMP, "m5206_timer: mode %d not implemented\n",
+ mode);
+ goto exit;
+ }
+ if ((s->tmr & TMR_FRR) == 0) {
+ qemu_log_mask(LOG_UNIMP,
+ "m5206_timer: free running mode not implemented\n");
+ goto exit;
+ }
+
+ /* Assume 66MHz system clock. */
+ ptimer_set_freq(s->timer, 66000000 / prescale);
+
+ ptimer_set_limit(s->timer, s->trr, 0);
+
+ ptimer_run(s->timer, 0);
+exit:
+ ptimer_transaction_commit(s->timer);
+}
+
+static void m5206_timer_trigger(void *opaque)
+{
+ m5206_timer_state *s = (m5206_timer_state *)opaque;
+ s->ter |= TER_REF;
+ m5206_timer_update(s);
+}
+
+static uint32_t m5206_timer_read(m5206_timer_state *s, uint32_t addr)
+{
+ switch (addr) {
+ case 0:
+ return s->tmr;
+ case 4:
+ return s->trr;
+ case 8:
+ return s->tcr;
+ case 0xc:
+ return s->trr - ptimer_get_count(s->timer);
+ case 0x11:
+ return s->ter;
+ default:
+ return 0;
+ }
+}
+
+static void m5206_timer_write(m5206_timer_state *s, uint32_t addr, uint32_t val)
+{
+ switch (addr) {
+ case 0:
+ if ((s->tmr & TMR_RST) != 0 && (val & TMR_RST) == 0) {
+ m5206_timer_reset(s);
+ }
+ s->tmr = val;
+ m5206_timer_recalibrate(s);
+ break;
+ case 4:
+ s->trr = val;
+ m5206_timer_recalibrate(s);
+ break;
+ case 8:
+ s->tcr = val;
+ break;
+ case 0xc:
+ ptimer_transaction_begin(s->timer);
+ ptimer_set_count(s->timer, val);
+ ptimer_transaction_commit(s->timer);
+ break;
+ case 0x11:
+ s->ter &= ~val;
+ break;
+ default:
+ break;
+ }
+ m5206_timer_update(s);
+}
+
+static m5206_timer_state *m5206_timer_init(qemu_irq irq)
+{
+ m5206_timer_state *s;
+
+ s = g_new0(m5206_timer_state, 1);
+ s->timer = ptimer_init(m5206_timer_trigger, s, PTIMER_POLICY_DEFAULT);
+ s->irq = irq;
+ m5206_timer_reset(s);
+ return s;
+}
+
+/* System Integration Module. */
+
+typedef struct {
+ SysBusDevice parent_obj;
+
+ M68kCPU *cpu;
+ MemoryRegion iomem;
+ qemu_irq *pic;
+ m5206_timer_state *timer[2];
+ void *uart[2];
+ uint8_t scr;
+ uint8_t icr[14];
+ uint16_t imr; /* 1 == interrupt is masked. */
+ uint16_t ipr;
+ uint8_t rsr;
+ uint8_t swivr;
+ uint8_t par;
+ /* Include the UART vector registers here. */
+ uint8_t uivr[2];
+} m5206_mbar_state;
+
+#define MCF5206_MBAR(obj) OBJECT_CHECK(m5206_mbar_state, (obj), TYPE_MCF5206_MBAR)
+
+/* Interrupt controller. */
+
+static int m5206_find_pending_irq(m5206_mbar_state *s)
+{
+ int level;
+ int vector;
+ uint16_t active;
+ int i;
+
+ level = 0;
+ vector = 0;
+ active = s->ipr & ~s->imr;
+ if (!active)
+ return 0;
+
+ for (i = 1; i < 14; i++) {
+ if (active & (1 << i)) {
+ if ((s->icr[i] & 0x1f) > level) {
+ level = s->icr[i] & 0x1f;
+ vector = i;
+ }
+ }
+ }
+
+ if (level < 4)
+ vector = 0;
+
+ return vector;
+}
+
+static void m5206_mbar_update(m5206_mbar_state *s)
+{
+ int irq;
+ int vector;
+ int level;
+
+ irq = m5206_find_pending_irq(s);
+ if (irq) {
+ int tmp;
+ tmp = s->icr[irq];
+ level = (tmp >> 2) & 7;
+ if (tmp & 0x80) {
+ /* Autovector. */
+ vector = 24 + level;
+ } else {
+ switch (irq) {
+ case 8: /* SWT */
+ vector = s->swivr;
+ break;
+ case 12: /* UART1 */
+ vector = s->uivr[0];
+ break;
+ case 13: /* UART2 */
+ vector = s->uivr[1];
+ break;
+ default:
+ /* Unknown vector. */
+ qemu_log_mask(LOG_UNIMP, "%s: Unhandled vector for IRQ %d\n",
+ __func__, irq);
+ vector = 0xf;
+ break;
+ }
+ }
+ } else {
+ level = 0;
+ vector = 0;
+ }
+ m68k_set_irq_level(s->cpu, level, vector);
+}
+
+static void m5206_mbar_set_irq(void *opaque, int irq, int level)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ if (level) {
+ s->ipr |= 1 << irq;
+ } else {
+ s->ipr &= ~(1 << irq);
+ }
+ m5206_mbar_update(s);
+}
+
+/* System Integration Module. */
+
+static void m5206_mbar_reset(DeviceState *dev)
+{
+ m5206_mbar_state *s = MCF5206_MBAR(dev);
+
+ s->scr = 0xc0;
+ s->icr[1] = 0x04;
+ s->icr[2] = 0x08;
+ s->icr[3] = 0x0c;
+ s->icr[4] = 0x10;
+ s->icr[5] = 0x14;
+ s->icr[6] = 0x18;
+ s->icr[7] = 0x1c;
+ s->icr[8] = 0x1c;
+ s->icr[9] = 0x80;
+ s->icr[10] = 0x80;
+ s->icr[11] = 0x80;
+ s->icr[12] = 0x00;
+ s->icr[13] = 0x00;
+ s->imr = 0x3ffe;
+ s->rsr = 0x80;
+ s->swivr = 0x0f;
+ s->par = 0;
+}
+
+static uint64_t m5206_mbar_read(m5206_mbar_state *s,
+ uint16_t offset, unsigned size)
+{
+ if (offset >= 0x100 && offset < 0x120) {
+ return m5206_timer_read(s->timer[0], offset - 0x100);
+ } else if (offset >= 0x120 && offset < 0x140) {
+ return m5206_timer_read(s->timer[1], offset - 0x120);
+ } else if (offset >= 0x140 && offset < 0x160) {
+ return mcf_uart_read(s->uart[0], offset - 0x140, size);
+ } else if (offset >= 0x180 && offset < 0x1a0) {
+ return mcf_uart_read(s->uart[1], offset - 0x180, size);
+ }
+ switch (offset) {
+ case 0x03: return s->scr;
+ case 0x14 ... 0x20: return s->icr[offset - 0x13];
+ case 0x36: return s->imr;
+ case 0x3a: return s->ipr;
+ case 0x40: return s->rsr;
+ case 0x41: return 0;
+ case 0x42: return s->swivr;
+ case 0x50:
+ /* DRAM mask register. */
+ /* FIXME: currently hardcoded to 128Mb. */
+ {
+ uint32_t mask = ~0;
+ while (mask > current_machine->ram_size) {
+ mask >>= 1;
+ }
+ return mask & 0x0ffe0000;
+ }
+ case 0x5c: return 1; /* DRAM bank 1 empty. */
+ case 0xcb: return s->par;
+ case 0x170: return s->uivr[0];
+ case 0x1b0: return s->uivr[1];
+ }
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad MBAR offset 0x%"PRIx16"\n",
+ __func__, offset);
+ return 0;
+}
+
+static void m5206_mbar_write(m5206_mbar_state *s, uint16_t offset,
+ uint64_t value, unsigned size)
+{
+ if (offset >= 0x100 && offset < 0x120) {
+ m5206_timer_write(s->timer[0], offset - 0x100, value);
+ return;
+ } else if (offset >= 0x120 && offset < 0x140) {
+ m5206_timer_write(s->timer[1], offset - 0x120, value);
+ return;
+ } else if (offset >= 0x140 && offset < 0x160) {
+ mcf_uart_write(s->uart[0], offset - 0x140, value, size);
+ return;
+ } else if (offset >= 0x180 && offset < 0x1a0) {
+ mcf_uart_write(s->uart[1], offset - 0x180, value, size);
+ return;
+ }
+ switch (offset) {
+ case 0x03:
+ s->scr = value;
+ break;
+ case 0x14 ... 0x20:
+ s->icr[offset - 0x13] = value;
+ m5206_mbar_update(s);
+ break;
+ case 0x36:
+ s->imr = value;
+ m5206_mbar_update(s);
+ break;
+ case 0x40:
+ s->rsr &= ~value;
+ break;
+ case 0x41:
+ /* TODO: implement watchdog. */
+ break;
+ case 0x42:
+ s->swivr = value;
+ break;
+ case 0xcb:
+ s->par = value;
+ break;
+ case 0x170:
+ s->uivr[0] = value;
+ break;
+ case 0x178: case 0x17c: case 0x1c8: case 0x1bc:
+ /* Not implemented: UART Output port bits. */
+ break;
+ case 0x1b0:
+ s->uivr[1] = value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad MBAR offset 0x%"PRIx16"\n",
+ __func__, offset);
+ break;
+ }
+}
+
+/* Internal peripherals use a variety of register widths.
+ This lookup table allows a single routine to handle all of them. */
+static const uint8_t m5206_mbar_width[] =
+{
+ /* 000-040 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ /* 040-080 */ 1, 2, 2, 2, 4, 1, 2, 4, 1, 2, 4, 2, 2, 4, 2, 2,
+ /* 080-0c0 */ 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4,
+ /* 0c0-100 */ 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 100-140 */ 2, 2, 2, 2, 1, 0, 0, 0, 2, 2, 2, 2, 1, 0, 0, 0,
+ /* 140-180 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 180-1c0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 1c0-200 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+};
+
+static uint32_t m5206_mbar_readw(void *opaque, hwaddr offset);
+static uint32_t m5206_mbar_readl(void *opaque, hwaddr offset);
+
+static uint32_t m5206_mbar_readb(void *opaque, hwaddr offset)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ offset &= 0x3ff;
+ if (offset >= 0x200) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR read offset 0x%" HWADDR_PRIX,
+ offset);
+ return 0;
+ }
+ if (m5206_mbar_width[offset >> 2] > 1) {
+ uint16_t val;
+ val = m5206_mbar_readw(opaque, offset & ~1);
+ if ((offset & 1) == 0) {
+ val >>= 8;
+ }
+ return val & 0xff;
+ }
+ return m5206_mbar_read(s, offset, 1);
+}
+
+static uint32_t m5206_mbar_readw(void *opaque, hwaddr offset)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ int width;
+ offset &= 0x3ff;
+ if (offset >= 0x200) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR read offset 0x%" HWADDR_PRIX,
+ offset);
+ return 0;
+ }
+ width = m5206_mbar_width[offset >> 2];
+ if (width > 2) {
+ uint32_t val;
+ val = m5206_mbar_readl(opaque, offset & ~3);
+ if ((offset & 3) == 0)
+ val >>= 16;
+ return val & 0xffff;
+ } else if (width < 2) {
+ uint16_t val;
+ val = m5206_mbar_readb(opaque, offset) << 8;
+ val |= m5206_mbar_readb(opaque, offset + 1);
+ return val;
+ }
+ return m5206_mbar_read(s, offset, 2);
+}
+
+static uint32_t m5206_mbar_readl(void *opaque, hwaddr offset)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ int width;
+ offset &= 0x3ff;
+ if (offset >= 0x200) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR read offset 0x%" HWADDR_PRIX,
+ offset);
+ return 0;
+ }
+ width = m5206_mbar_width[offset >> 2];
+ if (width < 4) {
+ uint32_t val;
+ val = m5206_mbar_readw(opaque, offset) << 16;
+ val |= m5206_mbar_readw(opaque, offset + 2);
+ return val;
+ }
+ return m5206_mbar_read(s, offset, 4);
+}
+
+static void m5206_mbar_writew(void *opaque, hwaddr offset,
+ uint32_t value);
+static void m5206_mbar_writel(void *opaque, hwaddr offset,
+ uint32_t value);
+
+static void m5206_mbar_writeb(void *opaque, hwaddr offset,
+ uint32_t value)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ int width;
+ offset &= 0x3ff;
+ if (offset >= 0x200) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR write offset 0x%" HWADDR_PRIX,
+ offset);
+ return;
+ }
+ width = m5206_mbar_width[offset >> 2];
+ if (width > 1) {
+ uint32_t tmp;
+ tmp = m5206_mbar_readw(opaque, offset & ~1);
+ if (offset & 1) {
+ tmp = (tmp & 0xff00) | value;
+ } else {
+ tmp = (tmp & 0x00ff) | (value << 8);
+ }
+ m5206_mbar_writew(opaque, offset & ~1, tmp);
+ return;
+ }
+ m5206_mbar_write(s, offset, value, 1);
+}
+
+static void m5206_mbar_writew(void *opaque, hwaddr offset,
+ uint32_t value)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ int width;
+ offset &= 0x3ff;
+ if (offset >= 0x200) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR write offset 0x%" HWADDR_PRIX,
+ offset);
+ return;
+ }
+ width = m5206_mbar_width[offset >> 2];
+ if (width > 2) {
+ uint32_t tmp;
+ tmp = m5206_mbar_readl(opaque, offset & ~3);
+ if (offset & 3) {
+ tmp = (tmp & 0xffff0000) | value;
+ } else {
+ tmp = (tmp & 0x0000ffff) | (value << 16);
+ }
+ m5206_mbar_writel(opaque, offset & ~3, tmp);
+ return;
+ } else if (width < 2) {
+ m5206_mbar_writeb(opaque, offset, value >> 8);
+ m5206_mbar_writeb(opaque, offset + 1, value & 0xff);
+ return;
+ }
+ m5206_mbar_write(s, offset, value, 2);
+}
+
+static void m5206_mbar_writel(void *opaque, hwaddr offset,
+ uint32_t value)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ int width;
+ offset &= 0x3ff;
+ if (offset >= 0x200) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Bad MBAR write offset 0x%" HWADDR_PRIX,
+ offset);
+ return;
+ }
+ width = m5206_mbar_width[offset >> 2];
+ if (width < 4) {
+ m5206_mbar_writew(opaque, offset, value >> 16);
+ m5206_mbar_writew(opaque, offset + 2, value & 0xffff);
+ return;
+ }
+ m5206_mbar_write(s, offset, value, 4);
+}
+
+static uint64_t m5206_mbar_readfn(void *opaque, hwaddr addr, unsigned size)
+{
+ switch (size) {
+ case 1:
+ return m5206_mbar_readb(opaque, addr);
+ case 2:
+ return m5206_mbar_readw(opaque, addr);
+ case 4:
+ return m5206_mbar_readl(opaque, addr);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void m5206_mbar_writefn(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ switch (size) {
+ case 1:
+ m5206_mbar_writeb(opaque, addr, value);
+ break;
+ case 2:
+ m5206_mbar_writew(opaque, addr, value);
+ break;
+ case 4:
+ m5206_mbar_writel(opaque, addr, value);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static const MemoryRegionOps m5206_mbar_ops = {
+ .read = m5206_mbar_readfn,
+ .write = m5206_mbar_writefn,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void mcf5206_mbar_realize(DeviceState *dev, Error **errp)
+{
+ m5206_mbar_state *s = MCF5206_MBAR(dev);
+
+ memory_region_init_io(&s->iomem, NULL, &m5206_mbar_ops, s,
+ "mbar", 0x00001000);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+
+ s->pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14);
+ s->timer[0] = m5206_timer_init(s->pic[9]);
+ s->timer[1] = m5206_timer_init(s->pic[10]);
+ s->uart[0] = mcf_uart_init(s->pic[12], serial_hd(0));
+ s->uart[1] = mcf_uart_init(s->pic[13], serial_hd(1));
+ s->cpu = M68K_CPU(qemu_get_cpu(0));
+}
+
+static void mcf5206_mbar_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->desc = "MCF5206 system integration module";
+ dc->realize = mcf5206_mbar_realize;
+ dc->reset = m5206_mbar_reset;
+}
+
+static const TypeInfo mcf5206_mbar_info = {
+ .name = TYPE_MCF5206_MBAR,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(m5206_mbar_state),
+ .class_init = mcf5206_mbar_class_init,
+};
+
+static void mcf5206_mbar_register_types(void)
+{
+ type_register_static(&mcf5206_mbar_info);
+}
+
+type_init(mcf5206_mbar_register_types)
diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c
new file mode 100644
index 000000000..93812ee20
--- /dev/null
+++ b/hw/m68k/mcf5208.c
@@ -0,0 +1,363 @@
+/*
+ * Motorola ColdFire MCF5208 SoC emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licensed under the GPL
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "qemu/datadir.h"
+#include "cpu.h"
+#include "hw/irq.h"
+#include "hw/m68k/mcf.h"
+#include "hw/m68k/mcf_fec.h"
+#include "qemu/timer.h"
+#include "hw/ptimer.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/qtest.h"
+#include "net/net.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "elf.h"
+
+#define SYS_FREQ 166666666
+
+#define ROM_SIZE 0x200000
+
+#define PCSR_EN 0x0001
+#define PCSR_RLD 0x0002
+#define PCSR_PIF 0x0004
+#define PCSR_PIE 0x0008
+#define PCSR_OVW 0x0010
+#define PCSR_DBG 0x0020
+#define PCSR_DOZE 0x0040
+#define PCSR_PRE_SHIFT 8
+#define PCSR_PRE_MASK 0x0f00
+
+typedef struct {
+ MemoryRegion iomem;
+ qemu_irq irq;
+ ptimer_state *timer;
+ uint16_t pcsr;
+ uint16_t pmr;
+ uint16_t pcntr;
+} m5208_timer_state;
+
+static void m5208_timer_update(m5208_timer_state *s)
+{
+ if ((s->pcsr & (PCSR_PIE | PCSR_PIF)) == (PCSR_PIE | PCSR_PIF))
+ qemu_irq_raise(s->irq);
+ else
+ qemu_irq_lower(s->irq);
+}
+
+static void m5208_timer_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ m5208_timer_state *s = (m5208_timer_state *)opaque;
+ int prescale;
+ int limit;
+ switch (offset) {
+ case 0:
+ /* The PIF bit is set-to-clear. */
+ if (value & PCSR_PIF) {
+ s->pcsr &= ~PCSR_PIF;
+ value &= ~PCSR_PIF;
+ }
+ /* Avoid frobbing the timer if we're just twiddling IRQ bits. */
+ if (((s->pcsr ^ value) & ~PCSR_PIE) == 0) {
+ s->pcsr = value;
+ m5208_timer_update(s);
+ return;
+ }
+
+ ptimer_transaction_begin(s->timer);
+ if (s->pcsr & PCSR_EN)
+ ptimer_stop(s->timer);
+
+ s->pcsr = value;
+
+ prescale = 1 << ((s->pcsr & PCSR_PRE_MASK) >> PCSR_PRE_SHIFT);
+ ptimer_set_freq(s->timer, (SYS_FREQ / 2) / prescale);
+ if (s->pcsr & PCSR_RLD)
+ limit = s->pmr;
+ else
+ limit = 0xffff;
+ ptimer_set_limit(s->timer, limit, 0);
+
+ if (s->pcsr & PCSR_EN)
+ ptimer_run(s->timer, 0);
+ ptimer_transaction_commit(s->timer);
+ break;
+ case 2:
+ ptimer_transaction_begin(s->timer);
+ s->pmr = value;
+ s->pcsr &= ~PCSR_PIF;
+ if ((s->pcsr & PCSR_RLD) == 0) {
+ if (s->pcsr & PCSR_OVW)
+ ptimer_set_count(s->timer, value);
+ } else {
+ ptimer_set_limit(s->timer, value, s->pcsr & PCSR_OVW);
+ }
+ ptimer_transaction_commit(s->timer);
+ break;
+ case 4:
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIX "\n",
+ __func__, offset);
+ return;
+ }
+ m5208_timer_update(s);
+}
+
+static void m5208_timer_trigger(void *opaque)
+{
+ m5208_timer_state *s = (m5208_timer_state *)opaque;
+ s->pcsr |= PCSR_PIF;
+ m5208_timer_update(s);
+}
+
+static uint64_t m5208_timer_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ m5208_timer_state *s = (m5208_timer_state *)opaque;
+ switch (addr) {
+ case 0:
+ return s->pcsr;
+ case 2:
+ return s->pmr;
+ case 4:
+ return ptimer_get_count(s->timer);
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIX "\n",
+ __func__, addr);
+ return 0;
+ }
+}
+
+static const MemoryRegionOps m5208_timer_ops = {
+ .read = m5208_timer_read,
+ .write = m5208_timer_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static uint64_t m5208_sys_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ switch (addr) {
+ case 0x110: /* SDCS0 */
+ {
+ int n;
+ for (n = 0; n < 32; n++) {
+ if (current_machine->ram_size < (2u << n)) {
+ break;
+ }
+ }
+ return (n - 1) | 0x40000000;
+ }
+ case 0x114: /* SDCS1 */
+ return 0;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIX "\n",
+ __func__, addr);
+ return 0;
+ }
+}
+
+static void m5208_sys_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIX "\n",
+ __func__, addr);
+}
+
+static const MemoryRegionOps m5208_sys_ops = {
+ .read = m5208_sys_read,
+ .write = m5208_sys_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void mcf5208_sys_init(MemoryRegion *address_space, qemu_irq *pic)
+{
+ MemoryRegion *iomem = g_new(MemoryRegion, 1);
+ m5208_timer_state *s;
+ int i;
+
+ /* SDRAMC. */
+ memory_region_init_io(iomem, NULL, &m5208_sys_ops, NULL, "m5208-sys", 0x00004000);
+ memory_region_add_subregion(address_space, 0xfc0a8000, iomem);
+ /* Timers. */
+ for (i = 0; i < 2; i++) {
+ s = g_new0(m5208_timer_state, 1);
+ s->timer = ptimer_init(m5208_timer_trigger, s, PTIMER_POLICY_DEFAULT);
+ memory_region_init_io(&s->iomem, NULL, &m5208_timer_ops, s,
+ "m5208-timer", 0x00004000);
+ memory_region_add_subregion(address_space, 0xfc080000 + 0x4000 * i,
+ &s->iomem);
+ s->irq = pic[4 + i];
+ }
+}
+
+static void mcf_fec_init(MemoryRegion *sysmem, NICInfo *nd, hwaddr base,
+ qemu_irq *irqs)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ int i;
+
+ qemu_check_nic_model(nd, TYPE_MCF_FEC_NET);
+ dev = qdev_new(TYPE_MCF_FEC_NET);
+ qdev_set_nic_properties(dev, nd);
+
+ s = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(s, &error_fatal);
+ for (i = 0; i < FEC_NUM_IRQ; i++) {
+ sysbus_connect_irq(s, i, irqs[i]);
+ }
+
+ memory_region_add_subregion(sysmem, base, sysbus_mmio_get_region(s, 0));
+}
+
+static void mcf5208evb_init(MachineState *machine)
+{
+ ram_addr_t ram_size = machine->ram_size;
+ const char *kernel_filename = machine->kernel_filename;
+ M68kCPU *cpu;
+ CPUM68KState *env;
+ int kernel_size;
+ uint64_t elf_entry;
+ hwaddr entry;
+ qemu_irq *pic;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *rom = g_new(MemoryRegion, 1);
+ MemoryRegion *sram = g_new(MemoryRegion, 1);
+
+ cpu = M68K_CPU(cpu_create(machine->cpu_type));
+ env = &cpu->env;
+
+ /* Initialize CPU registers. */
+ env->vbr = 0;
+ /* TODO: Configure BARs. */
+
+ /* ROM at 0x00000000 */
+ memory_region_init_rom(rom, NULL, "mcf5208.rom", ROM_SIZE, &error_fatal);
+ memory_region_add_subregion(address_space_mem, 0x00000000, rom);
+
+ /* DRAM at 0x40000000 */
+ memory_region_add_subregion(address_space_mem, 0x40000000, machine->ram);
+
+ /* Internal SRAM. */
+ memory_region_init_ram(sram, NULL, "mcf5208.sram", 16 * KiB, &error_fatal);
+ memory_region_add_subregion(address_space_mem, 0x80000000, sram);
+
+ /* Internal peripherals. */
+ pic = mcf_intc_init(address_space_mem, 0xfc048000, cpu);
+
+ mcf_uart_mm_init(0xfc060000, pic[26], serial_hd(0));
+ mcf_uart_mm_init(0xfc064000, pic[27], serial_hd(1));
+ mcf_uart_mm_init(0xfc068000, pic[28], serial_hd(2));
+
+ mcf5208_sys_init(address_space_mem, pic);
+
+ if (nb_nics > 1) {
+ error_report("Too many NICs");
+ exit(1);
+ }
+ if (nd_table[0].used) {
+ mcf_fec_init(address_space_mem, &nd_table[0],
+ 0xfc030000, pic + 36);
+ }
+
+ g_free(pic);
+
+ /* 0xfc000000 SCM. */
+ /* 0xfc004000 XBS. */
+ /* 0xfc008000 FlexBus CS. */
+ /* 0xfc030000 FEC. */
+ /* 0xfc040000 SCM + Power management. */
+ /* 0xfc044000 eDMA. */
+ /* 0xfc048000 INTC. */
+ /* 0xfc058000 I2C. */
+ /* 0xfc05c000 QSPI. */
+ /* 0xfc060000 UART0. */
+ /* 0xfc064000 UART0. */
+ /* 0xfc068000 UART0. */
+ /* 0xfc070000 DMA timers. */
+ /* 0xfc080000 PIT0. */
+ /* 0xfc084000 PIT1. */
+ /* 0xfc088000 EPORT. */
+ /* 0xfc08c000 Watchdog. */
+ /* 0xfc090000 clock module. */
+ /* 0xfc0a0000 CCM + reset. */
+ /* 0xfc0a4000 GPIO. */
+ /* 0xfc0a8000 SDRAM controller. */
+
+ /* Load firmware */
+ if (machine->firmware) {
+ char *fn;
+ uint8_t *ptr;
+
+ fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware);
+ if (!fn) {
+ error_report("Could not find ROM image '%s'", machine->firmware);
+ exit(1);
+ }
+ if (load_image_targphys(fn, 0x0, ROM_SIZE) < 8) {
+ error_report("Could not load ROM image '%s'", machine->firmware);
+ exit(1);
+ }
+ g_free(fn);
+ /* Initial PC is always at offset 4 in firmware binaries */
+ ptr = rom_ptr(0x4, 4);
+ assert(ptr != NULL);
+ env->pc = ldl_p(ptr);
+ }
+
+ /* Load kernel. */
+ if (!kernel_filename) {
+ if (qtest_enabled() || machine->firmware) {
+ return;
+ }
+ error_report("Kernel image must be specified");
+ exit(1);
+ }
+
+ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry,
+ NULL, NULL, NULL, 1, EM_68K, 0, 0);
+ entry = elf_entry;
+ if (kernel_size < 0) {
+ kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
+ NULL, NULL);
+ }
+ if (kernel_size < 0) {
+ kernel_size = load_image_targphys(kernel_filename, 0x40000000,
+ ram_size);
+ entry = 0x40000000;
+ }
+ if (kernel_size < 0) {
+ error_report("Could not load kernel '%s'", kernel_filename);
+ exit(1);
+ }
+
+ env->pc = entry;
+}
+
+static void mcf5208evb_machine_init(MachineClass *mc)
+{
+ mc->desc = "MCF5208EVB";
+ mc->init = mcf5208evb_init;
+ mc->is_default = true;
+ mc->default_cpu_type = M68K_CPU_TYPE_NAME("m5208");
+ mc->default_ram_id = "mcf5208.ram";
+}
+
+DEFINE_MACHINE("mcf5208evb", mcf5208evb_machine_init)
diff --git a/hw/m68k/mcf_intc.c b/hw/m68k/mcf_intc.c
new file mode 100644
index 000000000..4cd30188c
--- /dev/null
+++ b/hw/m68k/mcf_intc.c
@@ -0,0 +1,217 @@
+/*
+ * ColdFire Interrupt Controller emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licensed under the GPL
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "qemu/log.h"
+#include "cpu.h"
+#include "hw/irq.h"
+#include "hw/sysbus.h"
+#include "hw/m68k/mcf.h"
+#include "qom/object.h"
+
+#define TYPE_MCF_INTC "mcf-intc"
+OBJECT_DECLARE_SIMPLE_TYPE(mcf_intc_state, MCF_INTC)
+
+struct mcf_intc_state {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ uint64_t ipr;
+ uint64_t imr;
+ uint64_t ifr;
+ uint64_t enabled;
+ uint8_t icr[64];
+ M68kCPU *cpu;
+ int active_vector;
+};
+
+static void mcf_intc_update(mcf_intc_state *s)
+{
+ uint64_t active;
+ int i;
+ int best;
+ int best_level;
+
+ active = (s->ipr | s->ifr) & s->enabled & ~s->imr;
+ best_level = 0;
+ best = 64;
+ if (active) {
+ for (i = 0; i < 64; i++) {
+ if ((active & 1) != 0 && s->icr[i] >= best_level) {
+ best_level = s->icr[i];
+ best = i;
+ }
+ active >>= 1;
+ }
+ }
+ s->active_vector = ((best == 64) ? 24 : (best + 64));
+ m68k_set_irq_level(s->cpu, best_level, s->active_vector);
+}
+
+static uint64_t mcf_intc_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ int offset;
+ mcf_intc_state *s = (mcf_intc_state *)opaque;
+ offset = addr & 0xff;
+ if (offset >= 0x40 && offset < 0x80) {
+ return s->icr[offset - 0x40];
+ }
+ switch (offset) {
+ case 0x00:
+ return (uint32_t)(s->ipr >> 32);
+ case 0x04:
+ return (uint32_t)s->ipr;
+ case 0x08:
+ return (uint32_t)(s->imr >> 32);
+ case 0x0c:
+ return (uint32_t)s->imr;
+ case 0x10:
+ return (uint32_t)(s->ifr >> 32);
+ case 0x14:
+ return (uint32_t)s->ifr;
+ case 0xe0: /* SWIACK. */
+ return s->active_vector;
+ case 0xe1: case 0xe2: case 0xe3: case 0xe4:
+ case 0xe5: case 0xe6: case 0xe7:
+ /* LnIACK */
+ qemu_log_mask(LOG_UNIMP, "%s: LnIACK not implemented (offset 0x%02x)\n",
+ __func__, offset);
+ /* fallthru */
+ default:
+ return 0;
+ }
+}
+
+static void mcf_intc_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ int offset;
+ mcf_intc_state *s = (mcf_intc_state *)opaque;
+ offset = addr & 0xff;
+ if (offset >= 0x40 && offset < 0x80) {
+ int n = offset - 0x40;
+ s->icr[n] = val;
+ if (val == 0)
+ s->enabled &= ~(1ull << n);
+ else
+ s->enabled |= (1ull << n);
+ mcf_intc_update(s);
+ return;
+ }
+ switch (offset) {
+ case 0x00: case 0x04:
+ /* Ignore IPR writes. */
+ return;
+ case 0x08:
+ s->imr = (s->imr & 0xffffffff) | ((uint64_t)val << 32);
+ break;
+ case 0x0c:
+ s->imr = (s->imr & 0xffffffff00000000ull) | (uint32_t)val;
+ break;
+ case 0x1c:
+ if (val & 0x40) {
+ s->imr = ~0ull;
+ } else {
+ s->imr |= (0x1ull << (val & 0x3f));
+ }
+ break;
+ case 0x1d:
+ if (val & 0x40) {
+ s->imr = 0ull;
+ } else {
+ s->imr &= ~(0x1ull << (val & 0x3f));
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%02x\n",
+ __func__, offset);
+ return;
+ }
+ mcf_intc_update(s);
+}
+
+static void mcf_intc_set_irq(void *opaque, int irq, int level)
+{
+ mcf_intc_state *s = (mcf_intc_state *)opaque;
+ if (irq >= 64)
+ return;
+ if (level)
+ s->ipr |= 1ull << irq;
+ else
+ s->ipr &= ~(1ull << irq);
+ mcf_intc_update(s);
+}
+
+static void mcf_intc_reset(DeviceState *dev)
+{
+ mcf_intc_state *s = MCF_INTC(dev);
+
+ s->imr = ~0ull;
+ s->ipr = 0;
+ s->ifr = 0;
+ s->enabled = 0;
+ memset(s->icr, 0, 64);
+ s->active_vector = 24;
+}
+
+static const MemoryRegionOps mcf_intc_ops = {
+ .read = mcf_intc_read,
+ .write = mcf_intc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void mcf_intc_instance_init(Object *obj)
+{
+ mcf_intc_state *s = MCF_INTC(obj);
+
+ memory_region_init_io(&s->iomem, obj, &mcf_intc_ops, s, "mcf", 0x100);
+}
+
+static void mcf_intc_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ dc->reset = mcf_intc_reset;
+}
+
+static const TypeInfo mcf_intc_gate_info = {
+ .name = TYPE_MCF_INTC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mcf_intc_state),
+ .instance_init = mcf_intc_instance_init,
+ .class_init = mcf_intc_class_init,
+};
+
+static void mcf_intc_register_types(void)
+{
+ type_register_static(&mcf_intc_gate_info);
+}
+
+type_init(mcf_intc_register_types)
+
+qemu_irq *mcf_intc_init(MemoryRegion *sysmem,
+ hwaddr base,
+ M68kCPU *cpu)
+{
+ DeviceState *dev;
+ mcf_intc_state *s;
+
+ dev = qdev_new(TYPE_MCF_INTC);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+ s = MCF_INTC(dev);
+ s->cpu = cpu;
+
+ memory_region_add_subregion(sysmem, base, &s->iomem);
+
+ return qemu_allocate_irqs(mcf_intc_set_irq, s, 64);
+}
diff --git a/hw/m68k/meson.build b/hw/m68k/meson.build
new file mode 100644
index 000000000..31248641d
--- /dev/null
+++ b/hw/m68k/meson.build
@@ -0,0 +1,8 @@
+m68k_ss = ss.source_set()
+m68k_ss.add(when: 'CONFIG_AN5206', if_true: files('an5206.c', 'mcf5206.c'))
+m68k_ss.add(when: 'CONFIG_MCF5208', if_true: files('mcf5208.c', 'mcf_intc.c'))
+m68k_ss.add(when: 'CONFIG_NEXTCUBE', if_true: files('next-kbd.c', 'next-cube.c'))
+m68k_ss.add(when: 'CONFIG_Q800', if_true: files('q800.c'))
+m68k_ss.add(when: 'CONFIG_M68K_VIRT', if_true: files('virt.c'))
+
+hw_arch += {'m68k': m68k_ss}
diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c
new file mode 100644
index 000000000..e0d4a94f9
--- /dev/null
+++ b/hw/m68k/next-cube.c
@@ -0,0 +1,1056 @@
+/*
+ * NeXT Cube System Driver
+ *
+ * Copyright (c) 2011 Bryce Lanham
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "exec/hwaddr.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/qtest.h"
+#include "hw/irq.h"
+#include "hw/m68k/next-cube.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/scsi/esp.h"
+#include "hw/sysbus.h"
+#include "qom/object.h"
+#include "hw/char/escc.h" /* ZILOG 8530 Serial Emulation */
+#include "hw/block/fdc.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "ui/console.h"
+#include "target/m68k/cpu.h"
+#include "migration/vmstate.h"
+
+/* #define DEBUG_NEXT */
+#ifdef DEBUG_NEXT
+#define DPRINTF(fmt, ...) \
+ do { printf("NeXT: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+#define TYPE_NEXT_MACHINE MACHINE_TYPE_NAME("next-cube")
+OBJECT_DECLARE_SIMPLE_TYPE(NeXTState, NEXT_MACHINE)
+
+#define ENTRY 0x0100001e
+#define RAM_SIZE 0x4000000
+#define ROM_FILE "Rev_2.5_v66.bin"
+
+typedef struct next_dma {
+ uint32_t csr;
+
+ uint32_t saved_next;
+ uint32_t saved_limit;
+ uint32_t saved_start;
+ uint32_t saved_stop;
+
+ uint32_t next;
+ uint32_t limit;
+ uint32_t start;
+ uint32_t stop;
+
+ uint32_t next_initbuf;
+ uint32_t size;
+} next_dma;
+
+typedef struct NextRtc {
+ uint8_t ram[32];
+ uint8_t command;
+ uint8_t value;
+ uint8_t status;
+ uint8_t control;
+ uint8_t retval;
+} NextRtc;
+
+struct NeXTState {
+ MachineState parent;
+
+ next_dma dma[10];
+};
+
+#define TYPE_NEXT_PC "next-pc"
+OBJECT_DECLARE_SIMPLE_TYPE(NeXTPC, NEXT_PC)
+
+/* NeXT Peripheral Controller */
+struct NeXTPC {
+ SysBusDevice parent_obj;
+
+ M68kCPU *cpu;
+
+ MemoryRegion mmiomem;
+ MemoryRegion scrmem;
+
+ uint32_t scr1;
+ uint32_t scr2;
+ uint8_t scsi_csr_1;
+ uint8_t scsi_csr_2;
+ uint32_t int_mask;
+ uint32_t int_status;
+
+ NextRtc rtc;
+};
+
+/* Thanks to NeXT forums for this */
+/*
+static const uint8_t rtc_ram3[32] = {
+ 0x94, 0x0f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfb, 0x6d, 0x00, 0x00, 0x7B, 0x00,
+ 0x00, 0x00, 0x65, 0x6e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x13
+};
+*/
+static const uint8_t rtc_ram2[32] = {
+ 0x94, 0x0f, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfb, 0x6d, 0x00, 0x00, 0x4b, 0x00,
+ 0x41, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x7e,
+};
+
+#define SCR2_RTCLK 0x2
+#define SCR2_RTDATA 0x4
+#define SCR2_TOBCD(x) (((x / 10) << 4) + (x % 10))
+
+static void nextscr2_write(NeXTPC *s, uint32_t val, int size)
+{
+ static int led;
+ static int phase;
+ static uint8_t old_scr2;
+ uint8_t scr2_2;
+ NextRtc *rtc = &s->rtc;
+
+ if (size == 4) {
+ scr2_2 = (val >> 8) & 0xFF;
+ } else {
+ scr2_2 = val & 0xFF;
+ }
+
+ if (val & 0x1) {
+ DPRINTF("fault!\n");
+ led++;
+ if (led == 10) {
+ DPRINTF("LED flashing, possible fault!\n");
+ led = 0;
+ }
+ }
+
+ if (scr2_2 & 0x1) {
+ /* DPRINTF("RTC %x phase %i\n", scr2_2, phase); */
+ if (phase == -1) {
+ phase = 0;
+ }
+ /* If we are in going down clock... do something */
+ if (((old_scr2 & SCR2_RTCLK) != (scr2_2 & SCR2_RTCLK)) &&
+ ((scr2_2 & SCR2_RTCLK) == 0)) {
+ if (phase < 8) {
+ rtc->command = (rtc->command << 1) |
+ ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
+ }
+ if (phase >= 8 && phase < 16) {
+ rtc->value = (rtc->value << 1) |
+ ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
+
+ /* if we read RAM register, output RT_DATA bit */
+ if (rtc->command <= 0x1F) {
+ scr2_2 = scr2_2 & (~SCR2_RTDATA);
+ if (rtc->ram[rtc->command] & (0x80 >> (phase - 8))) {
+ scr2_2 |= SCR2_RTDATA;
+ }
+
+ rtc->retval = (rtc->retval << 1) |
+ ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
+ }
+ /* read the status 0x30 */
+ if (rtc->command == 0x30) {
+ scr2_2 = scr2_2 & (~SCR2_RTDATA);
+ /* for now status = 0x98 (new rtc + FTU) */
+ if (rtc->status & (0x80 >> (phase - 8))) {
+ scr2_2 |= SCR2_RTDATA;
+ }
+
+ rtc->retval = (rtc->retval << 1) |
+ ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
+ }
+ /* read the status 0x31 */
+ if (rtc->command == 0x31) {
+ scr2_2 = scr2_2 & (~SCR2_RTDATA);
+ if (rtc->control & (0x80 >> (phase - 8))) {
+ scr2_2 |= SCR2_RTDATA;
+ }
+ rtc->retval = (rtc->retval << 1) |
+ ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
+ }
+
+ if ((rtc->command >= 0x20) && (rtc->command <= 0x2F)) {
+ scr2_2 = scr2_2 & (~SCR2_RTDATA);
+ /* for now 0x00 */
+ time_t time_h = time(NULL);
+ struct tm *info = localtime(&time_h);
+ int ret = 0;
+
+ switch (rtc->command) {
+ case 0x20:
+ ret = SCR2_TOBCD(info->tm_sec);
+ break;
+ case 0x21:
+ ret = SCR2_TOBCD(info->tm_min);
+ break;
+ case 0x22:
+ ret = SCR2_TOBCD(info->tm_hour);
+ break;
+ case 0x24:
+ ret = SCR2_TOBCD(info->tm_mday);
+ break;
+ case 0x25:
+ ret = SCR2_TOBCD((info->tm_mon + 1));
+ break;
+ case 0x26:
+ ret = SCR2_TOBCD((info->tm_year - 100));
+ break;
+
+ }
+
+ if (ret & (0x80 >> (phase - 8))) {
+ scr2_2 |= SCR2_RTDATA;
+ }
+ rtc->retval = (rtc->retval << 1) |
+ ((scr2_2 & SCR2_RTDATA) ? 1 : 0);
+ }
+
+ }
+
+ phase++;
+ if (phase == 16) {
+ if (rtc->command >= 0x80 && rtc->command <= 0x9F) {
+ rtc->ram[rtc->command - 0x80] = rtc->value;
+ }
+ /* write to x30 register */
+ if (rtc->command == 0xB1) {
+ /* clear FTU */
+ if (rtc->value & 0x04) {
+ rtc->status = rtc->status & (~0x18);
+ s->int_status = s->int_status & (~0x04);
+ }
+ }
+ }
+ }
+ } else {
+ /* else end or abort */
+ phase = -1;
+ rtc->command = 0;
+ rtc->value = 0;
+ }
+ s->scr2 = val & 0xFFFF00FF;
+ s->scr2 |= scr2_2 << 8;
+ old_scr2 = scr2_2;
+}
+
+static uint32_t mmio_readb(NeXTPC *s, hwaddr addr)
+{
+ switch (addr) {
+ case 0xc000:
+ return (s->scr1 >> 24) & 0xFF;
+ case 0xc001:
+ return (s->scr1 >> 16) & 0xFF;
+ case 0xc002:
+ return (s->scr1 >> 8) & 0xFF;
+ case 0xc003:
+ return (s->scr1 >> 0) & 0xFF;
+
+ case 0xd000:
+ return (s->scr2 >> 24) & 0xFF;
+ case 0xd001:
+ return (s->scr2 >> 16) & 0xFF;
+ case 0xd002:
+ return (s->scr2 >> 8) & 0xFF;
+ case 0xd003:
+ return (s->scr2 >> 0) & 0xFF;
+ case 0x14020:
+ DPRINTF("MMIO Read 0x4020\n");
+ return 0x7f;
+
+ default:
+ DPRINTF("MMIO Read B @ %"HWADDR_PRIx"\n", addr);
+ return 0x0;
+ }
+}
+
+static uint32_t mmio_readw(NeXTPC *s, hwaddr addr)
+{
+ switch (addr) {
+ default:
+ DPRINTF("MMIO Read W @ %"HWADDR_PRIx"\n", addr);
+ return 0x0;
+ }
+}
+
+static uint32_t mmio_readl(NeXTPC *s, hwaddr addr)
+{
+ switch (addr) {
+ case 0x7000:
+ /* DPRINTF("Read INT status: %x\n", s->int_status); */
+ return s->int_status;
+
+ case 0x7800:
+ DPRINTF("MMIO Read INT mask: %x\n", s->int_mask);
+ return s->int_mask;
+
+ case 0xc000:
+ return s->scr1;
+
+ case 0xd000:
+ return s->scr2;
+
+ default:
+ DPRINTF("MMIO Read L @ %"HWADDR_PRIx"\n", addr);
+ return 0x0;
+ }
+}
+
+static void mmio_writeb(NeXTPC *s, hwaddr addr, uint32_t val)
+{
+ switch (addr) {
+ case 0xd003:
+ nextscr2_write(s, val, 1);
+ break;
+ default:
+ DPRINTF("MMIO Write B @ %x with %x\n", (unsigned int)addr, val);
+ }
+
+}
+
+static void mmio_writew(NeXTPC *s, hwaddr addr, uint32_t val)
+{
+ DPRINTF("MMIO Write W\n");
+}
+
+static void mmio_writel(NeXTPC *s, hwaddr addr, uint32_t val)
+{
+ switch (addr) {
+ case 0x7000:
+ DPRINTF("INT Status old: %x new: %x\n", s->int_status, val);
+ s->int_status = val;
+ break;
+ case 0x7800:
+ DPRINTF("INT Mask old: %x new: %x\n", s->int_mask, val);
+ s->int_mask = val;
+ break;
+ case 0xc000:
+ DPRINTF("SCR1 Write: %x\n", val);
+ break;
+ case 0xd000:
+ nextscr2_write(s, val, 4);
+ break;
+
+ default:
+ DPRINTF("MMIO Write l @ %x with %x\n", (unsigned int)addr, val);
+ }
+}
+
+static uint64_t mmio_readfn(void *opaque, hwaddr addr, unsigned size)
+{
+ NeXTPC *s = NEXT_PC(opaque);
+
+ switch (size) {
+ case 1:
+ return mmio_readb(s, addr);
+ case 2:
+ return mmio_readw(s, addr);
+ case 4:
+ return mmio_readl(s, addr);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void mmio_writefn(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ NeXTPC *s = NEXT_PC(opaque);
+
+ switch (size) {
+ case 1:
+ mmio_writeb(s, addr, value);
+ break;
+ case 2:
+ mmio_writew(s, addr, value);
+ break;
+ case 4:
+ mmio_writel(s, addr, value);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static const MemoryRegionOps mmio_ops = {
+ .read = mmio_readfn,
+ .write = mmio_writefn,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static uint32_t scr_readb(NeXTPC *s, hwaddr addr)
+{
+ switch (addr) {
+ case 0x14108:
+ DPRINTF("FD read @ %x\n", (unsigned int)addr);
+ return 0x40 | 0x04 | 0x2 | 0x1;
+ case 0x14020:
+ DPRINTF("SCSI 4020 STATUS READ %X\n", s->scsi_csr_1);
+ return s->scsi_csr_1;
+
+ case 0x14021:
+ DPRINTF("SCSI 4021 STATUS READ %X\n", s->scsi_csr_2);
+ return 0x40;
+
+ /*
+ * These 4 registers are the hardware timer, not sure which register
+ * is the latch instead of data, but no problems so far
+ */
+ case 0x1a000:
+ return 0xff & (clock() >> 24);
+ case 0x1a001:
+ return 0xff & (clock() >> 16);
+ case 0x1a002:
+ return 0xff & (clock() >> 8);
+ case 0x1a003:
+ /* Hack: We need to have this change consistently to make it work */
+ return 0xFF & clock();
+
+ default:
+ DPRINTF("BMAP Read B @ %x\n", (unsigned int)addr);
+ return 0;
+ }
+}
+
+static uint32_t scr_readw(NeXTPC *s, hwaddr addr)
+{
+ DPRINTF("BMAP Read W @ %x\n", (unsigned int)addr);
+ return 0;
+}
+
+static uint32_t scr_readl(NeXTPC *s, hwaddr addr)
+{
+ DPRINTF("BMAP Read L @ %x\n", (unsigned int)addr);
+ return 0;
+}
+
+#define SCSICSR_ENABLE 0x01
+#define SCSICSR_RESET 0x02 /* reset scsi dma */
+#define SCSICSR_FIFOFL 0x04
+#define SCSICSR_DMADIR 0x08 /* if set, scsi to mem */
+#define SCSICSR_CPUDMA 0x10 /* if set, dma enabled */
+#define SCSICSR_INTMASK 0x20 /* if set, interrupt enabled */
+
+static void scr_writeb(NeXTPC *s, hwaddr addr, uint32_t value)
+{
+ switch (addr) {
+ case 0x14108:
+ DPRINTF("FDCSR Write: %x\n", value);
+
+ if (value == 0x0) {
+ /* qemu_irq_raise(s->fd_irq[0]); */
+ }
+ break;
+ case 0x14020: /* SCSI Control Register */
+ if (value & SCSICSR_FIFOFL) {
+ DPRINTF("SCSICSR FIFO Flush\n");
+ /* will have to add another irq to the esp if this is needed */
+ /* esp_puflush_fifo(esp_g); */
+ /* qemu_irq_pulse(s->scsi_dma); */
+ }
+
+ if (value & SCSICSR_ENABLE) {
+ DPRINTF("SCSICSR Enable\n");
+ /*
+ * qemu_irq_raise(s->scsi_dma);
+ * s->scsi_csr_1 = 0xc0;
+ * s->scsi_csr_1 |= 0x1;
+ * qemu_irq_pulse(s->scsi_dma);
+ */
+ }
+ /*
+ * else
+ * s->scsi_csr_1 &= ~SCSICSR_ENABLE;
+ */
+
+ if (value & SCSICSR_RESET) {
+ DPRINTF("SCSICSR Reset\n");
+ /* I think this should set DMADIR. CPUDMA and INTMASK to 0 */
+ /* qemu_irq_raise(s->scsi_reset); */
+ /* s->scsi_csr_1 &= ~(SCSICSR_INTMASK |0x80|0x1); */
+
+ }
+ if (value & SCSICSR_DMADIR) {
+ DPRINTF("SCSICSR DMAdir\n");
+ }
+ if (value & SCSICSR_CPUDMA) {
+ DPRINTF("SCSICSR CPUDMA\n");
+ /* qemu_irq_raise(s->scsi_dma); */
+
+ s->int_status |= 0x4000000;
+ } else {
+ s->int_status &= ~(0x4000000);
+ }
+ if (value & SCSICSR_INTMASK) {
+ DPRINTF("SCSICSR INTMASK\n");
+ /*
+ * int_mask &= ~0x1000;
+ * s->scsi_csr_1 |= value;
+ * s->scsi_csr_1 &= ~SCSICSR_INTMASK;
+ * if (s->scsi_queued) {
+ * s->scsi_queued = 0;
+ * next_irq(s, NEXT_SCSI_I, level);
+ * }
+ */
+ } else {
+ /* int_mask |= 0x1000; */
+ }
+ if (value & 0x80) {
+ /* int_mask |= 0x1000; */
+ /* s->scsi_csr_1 |= 0x80; */
+ }
+ DPRINTF("SCSICSR Write: %x\n", value);
+ /* s->scsi_csr_1 = value; */
+ return;
+ /* Hardware timer latch - not implemented yet */
+ case 0x1a000:
+ default:
+ DPRINTF("BMAP Write B @ %x with %x\n", (unsigned int)addr, value);
+ }
+}
+
+static void scr_writew(NeXTPC *s, hwaddr addr, uint32_t value)
+{
+ DPRINTF("BMAP Write W @ %x with %x\n", (unsigned int)addr, value);
+}
+
+static void scr_writel(NeXTPC *s, hwaddr addr, uint32_t value)
+{
+ DPRINTF("BMAP Write L @ %x with %x\n", (unsigned int)addr, value);
+}
+
+static uint64_t scr_readfn(void *opaque, hwaddr addr, unsigned size)
+{
+ NeXTPC *s = NEXT_PC(opaque);
+
+ switch (size) {
+ case 1:
+ return scr_readb(s, addr);
+ case 2:
+ return scr_readw(s, addr);
+ case 4:
+ return scr_readl(s, addr);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void scr_writefn(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ NeXTPC *s = NEXT_PC(opaque);
+
+ switch (size) {
+ case 1:
+ scr_writeb(s, addr, value);
+ break;
+ case 2:
+ scr_writew(s, addr, value);
+ break;
+ case 4:
+ scr_writel(s, addr, value);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static const MemoryRegionOps scr_ops = {
+ .read = scr_readfn,
+ .write = scr_writefn,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+#define NEXTDMA_SCSI(x) (0x10 + x)
+#define NEXTDMA_FD(x) (0x10 + x)
+#define NEXTDMA_ENTX(x) (0x110 + x)
+#define NEXTDMA_ENRX(x) (0x150 + x)
+#define NEXTDMA_CSR 0x0
+#define NEXTDMA_NEXT 0x4000
+#define NEXTDMA_LIMIT 0x4004
+#define NEXTDMA_START 0x4008
+#define NEXTDMA_STOP 0x400c
+#define NEXTDMA_NEXT_INIT 0x4200
+#define NEXTDMA_SIZE 0x4204
+
+static void dma_writel(void *opaque, hwaddr addr, uint64_t value,
+ unsigned int size)
+{
+ NeXTState *next_state = NEXT_MACHINE(opaque);
+
+ switch (addr) {
+ case NEXTDMA_ENRX(NEXTDMA_CSR):
+ if (value & DMA_DEV2M) {
+ next_state->dma[NEXTDMA_ENRX].csr |= DMA_DEV2M;
+ }
+
+ if (value & DMA_SETENABLE) {
+ /* DPRINTF("SCSI DMA ENABLE\n"); */
+ next_state->dma[NEXTDMA_ENRX].csr |= DMA_ENABLE;
+ }
+ if (value & DMA_SETSUPDATE) {
+ next_state->dma[NEXTDMA_ENRX].csr |= DMA_SUPDATE;
+ }
+ if (value & DMA_CLRCOMPLETE) {
+ next_state->dma[NEXTDMA_ENRX].csr &= ~DMA_COMPLETE;
+ }
+
+ if (value & DMA_RESET) {
+ next_state->dma[NEXTDMA_ENRX].csr &= ~(DMA_COMPLETE | DMA_SUPDATE |
+ DMA_ENABLE | DMA_DEV2M);
+ }
+ /* DPRINTF("RXCSR \tWrite: %x\n",value); */
+ break;
+ case NEXTDMA_ENRX(NEXTDMA_NEXT_INIT):
+ next_state->dma[NEXTDMA_ENRX].next_initbuf = value;
+ break;
+ case NEXTDMA_ENRX(NEXTDMA_NEXT):
+ next_state->dma[NEXTDMA_ENRX].next = value;
+ break;
+ case NEXTDMA_ENRX(NEXTDMA_LIMIT):
+ next_state->dma[NEXTDMA_ENRX].limit = value;
+ break;
+ case NEXTDMA_SCSI(NEXTDMA_CSR):
+ if (value & DMA_DEV2M) {
+ next_state->dma[NEXTDMA_SCSI].csr |= DMA_DEV2M;
+ }
+ if (value & DMA_SETENABLE) {
+ /* DPRINTF("SCSI DMA ENABLE\n"); */
+ next_state->dma[NEXTDMA_SCSI].csr |= DMA_ENABLE;
+ }
+ if (value & DMA_SETSUPDATE) {
+ next_state->dma[NEXTDMA_SCSI].csr |= DMA_SUPDATE;
+ }
+ if (value & DMA_CLRCOMPLETE) {
+ next_state->dma[NEXTDMA_SCSI].csr &= ~DMA_COMPLETE;
+ }
+
+ if (value & DMA_RESET) {
+ next_state->dma[NEXTDMA_SCSI].csr &= ~(DMA_COMPLETE | DMA_SUPDATE |
+ DMA_ENABLE | DMA_DEV2M);
+ /* DPRINTF("SCSI DMA RESET\n"); */
+ }
+ /* DPRINTF("RXCSR \tWrite: %x\n",value); */
+ break;
+
+ case NEXTDMA_SCSI(NEXTDMA_NEXT):
+ next_state->dma[NEXTDMA_SCSI].next = value;
+ break;
+
+ case NEXTDMA_SCSI(NEXTDMA_LIMIT):
+ next_state->dma[NEXTDMA_SCSI].limit = value;
+ break;
+
+ case NEXTDMA_SCSI(NEXTDMA_START):
+ next_state->dma[NEXTDMA_SCSI].start = value;
+ break;
+
+ case NEXTDMA_SCSI(NEXTDMA_STOP):
+ next_state->dma[NEXTDMA_SCSI].stop = value;
+ break;
+
+ case NEXTDMA_SCSI(NEXTDMA_NEXT_INIT):
+ next_state->dma[NEXTDMA_SCSI].next_initbuf = value;
+ break;
+
+ default:
+ DPRINTF("DMA write @ %x w/ %x\n", (unsigned)addr, (unsigned)value);
+ }
+}
+
+static uint64_t dma_readl(void *opaque, hwaddr addr, unsigned int size)
+{
+ NeXTState *next_state = NEXT_MACHINE(opaque);
+
+ switch (addr) {
+ case NEXTDMA_SCSI(NEXTDMA_CSR):
+ DPRINTF("SCSI DMA CSR READ\n");
+ return next_state->dma[NEXTDMA_SCSI].csr;
+ case NEXTDMA_ENRX(NEXTDMA_CSR):
+ return next_state->dma[NEXTDMA_ENRX].csr;
+ case NEXTDMA_ENRX(NEXTDMA_NEXT_INIT):
+ return next_state->dma[NEXTDMA_ENRX].next_initbuf;
+ case NEXTDMA_ENRX(NEXTDMA_NEXT):
+ return next_state->dma[NEXTDMA_ENRX].next;
+ case NEXTDMA_ENRX(NEXTDMA_LIMIT):
+ return next_state->dma[NEXTDMA_ENRX].limit;
+
+ case NEXTDMA_SCSI(NEXTDMA_NEXT):
+ return next_state->dma[NEXTDMA_SCSI].next;
+ case NEXTDMA_SCSI(NEXTDMA_NEXT_INIT):
+ return next_state->dma[NEXTDMA_SCSI].next_initbuf;
+ case NEXTDMA_SCSI(NEXTDMA_LIMIT):
+ return next_state->dma[NEXTDMA_SCSI].limit;
+ case NEXTDMA_SCSI(NEXTDMA_START):
+ return next_state->dma[NEXTDMA_SCSI].start;
+ case NEXTDMA_SCSI(NEXTDMA_STOP):
+ return next_state->dma[NEXTDMA_SCSI].stop;
+
+ default:
+ DPRINTF("DMA read @ %x\n", (unsigned int)addr);
+ return 0;
+ }
+
+ /*
+ * once the csr's are done, subtract 0x3FEC from the addr, and that will
+ * normalize the upper registers
+ */
+}
+
+static const MemoryRegionOps dma_ops = {
+ .read = dma_readl,
+ .write = dma_writel,
+ .impl.min_access_size = 4,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void next_irq(void *opaque, int number, int level)
+{
+ NeXTPC *s = NEXT_PC(opaque);
+ M68kCPU *cpu = s->cpu;
+ int shift = 0;
+
+ /* first switch sets interupt status */
+ /* DPRINTF("IRQ %i\n",number); */
+ switch (number) {
+ /* level 3 - floppy, kbd/mouse, power, ether rx/tx, scsi, clock */
+ case NEXT_FD_I:
+ shift = 7;
+ break;
+ case NEXT_KBD_I:
+ shift = 3;
+ break;
+ case NEXT_PWR_I:
+ shift = 2;
+ break;
+ case NEXT_ENRX_I:
+ shift = 9;
+ break;
+ case NEXT_ENTX_I:
+ shift = 10;
+ break;
+ case NEXT_SCSI_I:
+ shift = 12;
+ break;
+ case NEXT_CLK_I:
+ shift = 5;
+ break;
+
+ /* level 5 - scc (serial) */
+ case NEXT_SCC_I:
+ shift = 17;
+ break;
+
+ /* level 6 - audio etherrx/tx dma */
+ case NEXT_ENTX_DMA_I:
+ shift = 28;
+ break;
+ case NEXT_ENRX_DMA_I:
+ shift = 27;
+ break;
+ case NEXT_SCSI_DMA_I:
+ shift = 26;
+ break;
+ case NEXT_SND_I:
+ shift = 23;
+ break;
+ case NEXT_SCC_DMA_I:
+ shift = 21;
+ break;
+
+ }
+ /*
+ * this HAS to be wrong, the interrupt handlers in mach and together
+ * int_status and int_mask and return if there is a hit
+ */
+ if (s->int_mask & (1 << shift)) {
+ DPRINTF("%x interrupt masked @ %x\n", 1 << shift, cpu->env.pc);
+ /* return; */
+ }
+
+ /* second switch triggers the correct interrupt */
+ if (level) {
+ s->int_status |= 1 << shift;
+
+ switch (number) {
+ /* level 3 - floppy, kbd/mouse, power, ether rx/tx, scsi, clock */
+ case NEXT_FD_I:
+ case NEXT_KBD_I:
+ case NEXT_PWR_I:
+ case NEXT_ENRX_I:
+ case NEXT_ENTX_I:
+ case NEXT_SCSI_I:
+ case NEXT_CLK_I:
+ m68k_set_irq_level(cpu, 3, 27);
+ break;
+
+ /* level 5 - scc (serial) */
+ case NEXT_SCC_I:
+ m68k_set_irq_level(cpu, 5, 29);
+ break;
+
+ /* level 6 - audio etherrx/tx dma */
+ case NEXT_ENTX_DMA_I:
+ case NEXT_ENRX_DMA_I:
+ case NEXT_SCSI_DMA_I:
+ case NEXT_SND_I:
+ case NEXT_SCC_DMA_I:
+ m68k_set_irq_level(cpu, 6, 30);
+ break;
+ }
+ } else {
+ s->int_status &= ~(1 << shift);
+ cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
+ }
+}
+
+static void next_escc_init(DeviceState *pcdev)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_new(TYPE_ESCC);
+ qdev_prop_set_uint32(dev, "disabled", 0);
+ qdev_prop_set_uint32(dev, "frequency", 9600 * 384);
+ qdev_prop_set_uint32(dev, "it_shift", 0);
+ qdev_prop_set_bit(dev, "bit_swap", true);
+ qdev_prop_set_chr(dev, "chrB", serial_hd(1));
+ qdev_prop_set_chr(dev, "chrA", serial_hd(0));
+ qdev_prop_set_uint32(dev, "chnBtype", escc_serial);
+ qdev_prop_set_uint32(dev, "chnAtype", escc_serial);
+
+ s = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(s, &error_fatal);
+ sysbus_connect_irq(s, 0, qdev_get_gpio_in(pcdev, NEXT_SCC_I));
+ sysbus_connect_irq(s, 1, qdev_get_gpio_in(pcdev, NEXT_SCC_DMA_I));
+ sysbus_mmio_map(s, 0, 0x2118000);
+}
+
+static void next_pc_reset(DeviceState *dev)
+{
+ NeXTPC *s = NEXT_PC(dev);
+
+ /* Set internal registers to initial values */
+ /* 0x0000XX00 << vital bits */
+ s->scr1 = 0x00011102;
+ s->scr2 = 0x00ff0c80;
+
+ s->rtc.status = 0x90;
+
+ /* Load RTC RAM - TODO: provide possibility to load contents from file */
+ memcpy(s->rtc.ram, rtc_ram2, 32);
+}
+
+static void next_pc_realize(DeviceState *dev, Error **errp)
+{
+ NeXTPC *s = NEXT_PC(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ qdev_init_gpio_in(dev, next_irq, NEXT_NUM_IRQS);
+
+ memory_region_init_io(&s->mmiomem, OBJECT(s), &mmio_ops, s,
+ "next.mmio", 0xD0000);
+ memory_region_init_io(&s->scrmem, OBJECT(s), &scr_ops, s,
+ "next.scr", 0x20000);
+ sysbus_init_mmio(sbd, &s->mmiomem);
+ sysbus_init_mmio(sbd, &s->scrmem);
+}
+
+/*
+ * If the m68k CPU implemented its inbound irq lines as GPIO lines
+ * rather than via the m68k_set_irq_level() function we would not need
+ * this cpu link property and could instead provide outbound IRQ lines
+ * that the board could wire up to the CPU.
+ */
+static Property next_pc_properties[] = {
+ DEFINE_PROP_LINK("cpu", NeXTPC, cpu, TYPE_M68K_CPU, M68kCPU *),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription next_rtc_vmstate = {
+ .name = "next-rtc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8_ARRAY(ram, NextRtc, 32),
+ VMSTATE_UINT8(command, NextRtc),
+ VMSTATE_UINT8(value, NextRtc),
+ VMSTATE_UINT8(status, NextRtc),
+ VMSTATE_UINT8(control, NextRtc),
+ VMSTATE_UINT8(retval, NextRtc),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static const VMStateDescription next_pc_vmstate = {
+ .name = "next-pc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(scr1, NeXTPC),
+ VMSTATE_UINT32(scr2, NeXTPC),
+ VMSTATE_UINT32(int_mask, NeXTPC),
+ VMSTATE_UINT32(int_status, NeXTPC),
+ VMSTATE_UINT8(scsi_csr_1, NeXTPC),
+ VMSTATE_UINT8(scsi_csr_2, NeXTPC),
+ VMSTATE_STRUCT(rtc, NeXTPC, 0, next_rtc_vmstate, NextRtc),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static void next_pc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->desc = "NeXT Peripheral Controller";
+ dc->realize = next_pc_realize;
+ dc->reset = next_pc_reset;
+ device_class_set_props(dc, next_pc_properties);
+ dc->vmsd = &next_pc_vmstate;
+}
+
+static const TypeInfo next_pc_info = {
+ .name = TYPE_NEXT_PC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(NeXTPC),
+ .class_init = next_pc_class_init,
+};
+
+static void next_cube_init(MachineState *machine)
+{
+ M68kCPU *cpu;
+ CPUM68KState *env;
+ MemoryRegion *rom = g_new(MemoryRegion, 1);
+ MemoryRegion *dmamem = g_new(MemoryRegion, 1);
+ MemoryRegion *bmapm1 = g_new(MemoryRegion, 1);
+ MemoryRegion *bmapm2 = g_new(MemoryRegion, 1);
+ MemoryRegion *sysmem = get_system_memory();
+ const char *bios_name = machine->firmware ?: ROM_FILE;
+ DeviceState *dev;
+ DeviceState *pcdev;
+
+ /* Initialize the cpu core */
+ cpu = M68K_CPU(cpu_create(machine->cpu_type));
+ if (!cpu) {
+ error_report("Unable to find m68k CPU definition");
+ exit(1);
+ }
+ env = &cpu->env;
+
+ /* Initialize CPU registers. */
+ env->vbr = 0;
+ env->sr = 0x2700;
+
+ /* Peripheral Controller */
+ pcdev = qdev_new(TYPE_NEXT_PC);
+ object_property_set_link(OBJECT(pcdev), "cpu", OBJECT(cpu), &error_abort);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(pcdev), &error_fatal);
+
+ /* 64MB RAM starting at 0x04000000 */
+ memory_region_add_subregion(sysmem, 0x04000000, machine->ram);
+
+ /* Framebuffer */
+ dev = qdev_new(TYPE_NEXTFB);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0B000000);
+
+ /* MMIO */
+ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 0, 0x02000000);
+
+ /* BMAP IO - acts as a catch-all for now */
+ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 1, 0x02100000);
+
+ /* BMAP memory */
+ memory_region_init_ram_flags_nomigrate(bmapm1, NULL, "next.bmapmem", 64,
+ RAM_SHARED, &error_fatal);
+ memory_region_add_subregion(sysmem, 0x020c0000, bmapm1);
+ /* The Rev_2.5_v66.bin firmware accesses it at 0x820c0020, too */
+ memory_region_init_alias(bmapm2, NULL, "next.bmapmem2", bmapm1, 0x0, 64);
+ memory_region_add_subregion(sysmem, 0x820c0000, bmapm2);
+
+ /* KBD */
+ dev = qdev_new(TYPE_NEXTKBD);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0200e000);
+
+ /* Load ROM here */
+ /* still not sure if the rom should also be mapped at 0x0*/
+ memory_region_init_rom(rom, NULL, "next.rom", 0x20000, &error_fatal);
+ memory_region_add_subregion(sysmem, 0x01000000, rom);
+ if (load_image_targphys(bios_name, 0x01000000, 0x20000) < 8) {
+ if (!qtest_enabled()) {
+ error_report("Failed to load firmware '%s'.", bios_name);
+ }
+ } else {
+ uint8_t *ptr;
+ /* Initial PC is always at offset 4 in firmware binaries */
+ ptr = rom_ptr(0x01000004, 4);
+ g_assert(ptr != NULL);
+ env->pc = ldl_p(ptr);
+ if (env->pc >= 0x01020000) {
+ error_report("'%s' does not seem to be a valid firmware image.",
+ bios_name);
+ exit(1);
+ }
+ }
+
+ /* Serial */
+ next_escc_init(pcdev);
+
+ /* TODO: */
+ /* Network */
+ /* SCSI */
+
+ /* DMA */
+ memory_region_init_io(dmamem, NULL, &dma_ops, machine, "next.dma", 0x5000);
+ memory_region_add_subregion(sysmem, 0x02000000, dmamem);
+}
+
+static void next_machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "NeXT Cube";
+ mc->init = next_cube_init;
+ mc->default_ram_size = RAM_SIZE;
+ mc->default_ram_id = "next.ram";
+ mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040");
+}
+
+static const TypeInfo next_typeinfo = {
+ .name = TYPE_NEXT_MACHINE,
+ .parent = TYPE_MACHINE,
+ .class_init = next_machine_class_init,
+ .instance_size = sizeof(NeXTState),
+};
+
+static void next_register_type(void)
+{
+ type_register_static(&next_typeinfo);
+ type_register_static(&next_pc_info);
+}
+
+type_init(next_register_type)
diff --git a/hw/m68k/next-kbd.c b/hw/m68k/next-kbd.c
new file mode 100644
index 000000000..0544160e9
--- /dev/null
+++ b/hw/m68k/next-kbd.c
@@ -0,0 +1,289 @@
+/*
+ * QEMU NeXT Keyboard/Mouse emulation
+ *
+ * Copyright (c) 2011 Bryce Lanham
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * This is admittedly hackish, but works well enough for basic input. Mouse
+ * support will be added once we can boot something that needs the mouse.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/sysbus.h"
+#include "hw/m68k/next-cube.h"
+#include "ui/console.h"
+#include "migration/vmstate.h"
+#include "qom/object.h"
+
+OBJECT_DECLARE_SIMPLE_TYPE(NextKBDState, NEXTKBD)
+
+/* following defintions from next68k netbsd */
+#define CSR_INT 0x00800000
+#define CSR_DATA 0x00400000
+
+#define KD_KEYMASK 0x007f
+#define KD_DIRECTION 0x0080 /* pressed or released */
+#define KD_CNTL 0x0100
+#define KD_LSHIFT 0x0200
+#define KD_RSHIFT 0x0400
+#define KD_LCOMM 0x0800
+#define KD_RCOMM 0x1000
+#define KD_LALT 0x2000
+#define KD_RALT 0x4000
+#define KD_VALID 0x8000 /* only set for scancode keys ? */
+#define KD_MODS 0x4f00
+
+#define KBD_QUEUE_SIZE 256
+
+typedef struct {
+ uint8_t data[KBD_QUEUE_SIZE];
+ int rptr, wptr, count;
+} KBDQueue;
+
+
+struct NextKBDState {
+ SysBusDevice sbd;
+ MemoryRegion mr;
+ KBDQueue queue;
+ uint16_t shift;
+};
+
+static void queue_code(void *opaque, int code);
+
+/* lots of magic numbers here */
+static uint32_t kbd_read_byte(void *opaque, hwaddr addr)
+{
+ switch (addr & 0x3) {
+ case 0x0: /* 0xe000 */
+ return 0x80 | 0x20;
+
+ case 0x1: /* 0xe001 */
+ return 0x80 | 0x40 | 0x20 | 0x10;
+
+ case 0x2: /* 0xe002 */
+ /* returning 0x40 caused mach to hang */
+ return 0x10 | 0x2 | 0x1;
+
+ default:
+ qemu_log_mask(LOG_UNIMP, "NeXT kbd read byte %"HWADDR_PRIx"\n", addr);
+ }
+
+ return 0;
+}
+
+static uint32_t kbd_read_word(void *opaque, hwaddr addr)
+{
+ qemu_log_mask(LOG_UNIMP, "NeXT kbd read word %"HWADDR_PRIx"\n", addr);
+ return 0;
+}
+
+/* even more magic numbers */
+static uint32_t kbd_read_long(void *opaque, hwaddr addr)
+{
+ int key = 0;
+ NextKBDState *s = NEXTKBD(opaque);
+ KBDQueue *q = &s->queue;
+
+ switch (addr & 0xf) {
+ case 0x0: /* 0xe000 */
+ return 0xA0F09300;
+
+ case 0x8: /* 0xe008 */
+ /* get keycode from buffer */
+ if (q->count > 0) {
+ key = q->data[q->rptr];
+ if (++q->rptr == KBD_QUEUE_SIZE) {
+ q->rptr = 0;
+ }
+
+ q->count--;
+
+ if (s->shift) {
+ key |= s->shift;
+ }
+
+ if (key & 0x80) {
+ return 0;
+ } else {
+ return 0x10000000 | KD_VALID | key;
+ }
+ } else {
+ return 0;
+ }
+
+ default:
+ qemu_log_mask(LOG_UNIMP, "NeXT kbd read long %"HWADDR_PRIx"\n", addr);
+ return 0;
+ }
+}
+
+static uint64_t kbd_readfn(void *opaque, hwaddr addr, unsigned size)
+{
+ switch (size) {
+ case 1:
+ return kbd_read_byte(opaque, addr);
+ case 2:
+ return kbd_read_word(opaque, addr);
+ case 4:
+ return kbd_read_long(opaque, addr);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void kbd_writefn(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ qemu_log_mask(LOG_UNIMP, "NeXT kbd write: size=%u addr=0x%"HWADDR_PRIx
+ "val=0x%"PRIx64"\n", size, addr, value);
+}
+
+static const MemoryRegionOps kbd_ops = {
+ .read = kbd_readfn,
+ .write = kbd_writefn,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void nextkbd_event(void *opaque, int ch)
+{
+ /*
+ * Will want to set vars for caps/num lock
+ * if (ch & 0x80) -> key release
+ * there's also e0 escaped scancodes that might need to be handled
+ */
+ queue_code(opaque, ch);
+}
+
+static const unsigned char next_keycodes[128] = {
+ 0x00, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x50, 0x4F,
+ 0x4E, 0x1E, 0x1F, 0x20, 0x1D, 0x1C, 0x1B, 0x00,
+ 0x42, 0x43, 0x44, 0x45, 0x48, 0x47, 0x46, 0x06,
+ 0x07, 0x08, 0x00, 0x00, 0x2A, 0x00, 0x39, 0x3A,
+ 0x3B, 0x3C, 0x3D, 0x40, 0x3F, 0x3E, 0x2D, 0x2C,
+ 0x2B, 0x26, 0x00, 0x00, 0x31, 0x32, 0x33, 0x34,
+ 0x35, 0x37, 0x36, 0x2e, 0x2f, 0x30, 0x00, 0x00,
+ 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static void queue_code(void *opaque, int code)
+{
+ NextKBDState *s = NEXTKBD(opaque);
+ KBDQueue *q = &s->queue;
+ int key = code & KD_KEYMASK;
+ int release = code & 0x80;
+ static int ext;
+
+ if (code == 0xE0) {
+ ext = 1;
+ }
+
+ if (code == 0x2A || code == 0x1D || code == 0x36) {
+ if (code == 0x2A) {
+ s->shift = KD_LSHIFT;
+ } else if (code == 0x36) {
+ s->shift = KD_RSHIFT;
+ ext = 0;
+ } else if (code == 0x1D && !ext) {
+ s->shift = KD_LCOMM;
+ } else if (code == 0x1D && ext) {
+ ext = 0;
+ s->shift = KD_RCOMM;
+ }
+ return;
+ } else if (code == (0x2A | 0x80) || code == (0x1D | 0x80) ||
+ code == (0x36 | 0x80)) {
+ s->shift = 0;
+ return;
+ }
+
+ if (q->count >= KBD_QUEUE_SIZE) {
+ return;
+ }
+
+ q->data[q->wptr] = next_keycodes[key] | release;
+
+ if (++q->wptr == KBD_QUEUE_SIZE) {
+ q->wptr = 0;
+ }
+
+ q->count++;
+
+ /*
+ * might need to actually trigger the NeXT irq, but as the keyboard works
+ * at the moment, I'll worry about it later
+ */
+ /* s->update_irq(s->update_arg, 1); */
+}
+
+static void nextkbd_reset(DeviceState *dev)
+{
+ NextKBDState *nks = NEXTKBD(dev);
+
+ memset(&nks->queue, 0, sizeof(KBDQueue));
+ nks->shift = 0;
+}
+
+static void nextkbd_realize(DeviceState *dev, Error **errp)
+{
+ NextKBDState *s = NEXTKBD(dev);
+
+ memory_region_init_io(&s->mr, OBJECT(dev), &kbd_ops, s, "next.kbd", 0x1000);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr);
+
+ qemu_add_kbd_event_handler(nextkbd_event, s);
+}
+
+static const VMStateDescription nextkbd_vmstate = {
+ .name = TYPE_NEXTKBD,
+ .unmigratable = 1, /* TODO: Implement this when m68k CPU is migratable */
+};
+
+static void nextkbd_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+ dc->vmsd = &nextkbd_vmstate;
+ dc->realize = nextkbd_realize;
+ dc->reset = nextkbd_reset;
+}
+
+static const TypeInfo nextkbd_info = {
+ .name = TYPE_NEXTKBD,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(NextKBDState),
+ .class_init = nextkbd_class_init,
+};
+
+static void nextkbd_register_types(void)
+{
+ type_register_static(&nextkbd_info);
+}
+
+type_init(nextkbd_register_types)
diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c
new file mode 100644
index 000000000..e4c7c9b88
--- /dev/null
+++ b/hw/m68k/q800.c
@@ -0,0 +1,711 @@
+/*
+ * QEMU Motorla 680x0 Macintosh hardware System Emulator
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu-common.h"
+#include "qemu/datadir.h"
+#include "sysemu/sysemu.h"
+#include "cpu.h"
+#include "hw/boards.h"
+#include "hw/or-irq.h"
+#include "hw/nmi.h"
+#include "elf.h"
+#include "hw/loader.h"
+#include "ui/console.h"
+#include "hw/char/escc.h"
+#include "hw/sysbus.h"
+#include "hw/scsi/esp.h"
+#include "standard-headers/asm-m68k/bootinfo.h"
+#include "standard-headers/asm-m68k/bootinfo-mac.h"
+#include "bootinfo.h"
+#include "hw/misc/mac_via.h"
+#include "hw/input/adb.h"
+#include "hw/nubus/mac-nubus-bridge.h"
+#include "hw/display/macfb.h"
+#include "hw/block/swim.h"
+#include "net/net.h"
+#include "qapi/error.h"
+#include "sysemu/qtest.h"
+#include "sysemu/runstate.h"
+#include "sysemu/reset.h"
+#include "migration/vmstate.h"
+
+#define MACROM_ADDR 0x40800000
+#define MACROM_SIZE 0x00100000
+
+#define MACROM_FILENAME "MacROM.bin"
+
+#define IO_BASE 0x50000000
+#define IO_SLICE 0x00040000
+#define IO_SIZE 0x04000000
+
+#define VIA_BASE (IO_BASE + 0x00000)
+#define SONIC_PROM_BASE (IO_BASE + 0x08000)
+#define SONIC_BASE (IO_BASE + 0x0a000)
+#define SCC_BASE (IO_BASE + 0x0c020)
+#define ESP_BASE (IO_BASE + 0x10000)
+#define ESP_PDMA (IO_BASE + 0x10100)
+#define ASC_BASE (IO_BASE + 0x14000)
+#define SWIM_BASE (IO_BASE + 0x1E000)
+
+#define SONIC_PROM_SIZE 0x1000
+
+/*
+ * the video base, whereas it a Nubus address,
+ * is needed by the kernel to have early display and
+ * thus provided by the bootloader
+ */
+#define VIDEO_BASE 0xf9000000
+
+#define MAC_CLOCK 3686418
+
+/*
+ * Slot 0x9 is reserved for use by the in-built framebuffer whilst only
+ * slots 0xc, 0xd and 0xe physically exist on the Quadra 800
+ */
+#define Q800_NUBUS_SLOTS_AVAILABLE (BIT(0x9) | BIT(0xc) | BIT(0xd) | \
+ BIT(0xe))
+
+/*
+ * The GLUE (General Logic Unit) is an Apple custom integrated circuit chip
+ * that performs a variety of functions (RAM management, clock generation, ...).
+ * The GLUE chip receives interrupt requests from various devices,
+ * assign priority to each, and asserts one or more interrupt line to the
+ * CPU.
+ */
+
+#define TYPE_GLUE "q800-glue"
+OBJECT_DECLARE_SIMPLE_TYPE(GLUEState, GLUE)
+
+struct GLUEState {
+ SysBusDevice parent_obj;
+ M68kCPU *cpu;
+ uint8_t ipr;
+ uint8_t auxmode;
+ qemu_irq irqs[1];
+ QEMUTimer *nmi_release;
+};
+
+#define GLUE_IRQ_IN_VIA1 0
+#define GLUE_IRQ_IN_VIA2 1
+#define GLUE_IRQ_IN_SONIC 2
+#define GLUE_IRQ_IN_ESCC 3
+#define GLUE_IRQ_IN_NMI 4
+
+#define GLUE_IRQ_NUBUS_9 0
+
+/*
+ * The GLUE logic on the Quadra 800 supports 2 different IRQ routing modes
+ * controlled from the VIA1 auxmode GPIO (port B bit 6) which are documented
+ * in NetBSD as follows:
+ *
+ * A/UX mode (Linux, NetBSD, auxmode GPIO low)
+ *
+ * Level 0: Spurious: ignored
+ * Level 1: Software
+ * Level 2: VIA2 (except ethernet, sound)
+ * Level 3: Ethernet
+ * Level 4: Serial (SCC)
+ * Level 5: Sound
+ * Level 6: VIA1
+ * Level 7: NMIs: parity errors, RESET button, YANCC error
+ *
+ * Classic mode (default: used by MacOS, A/UX 3.0.1, auxmode GPIO high)
+ *
+ * Level 0: Spurious: ignored
+ * Level 1: VIA1 (clock, ADB)
+ * Level 2: VIA2 (NuBus, SCSI)
+ * Level 3:
+ * Level 4: Serial (SCC)
+ * Level 5:
+ * Level 6:
+ * Level 7: Non-maskable: parity errors, RESET button
+ *
+ * Note that despite references to A/UX mode in Linux and NetBSD, at least
+ * A/UX 3.0.1 still uses Classic mode.
+ */
+
+static void GLUE_set_irq(void *opaque, int irq, int level)
+{
+ GLUEState *s = opaque;
+ int i;
+
+ if (s->auxmode) {
+ /* Classic mode */
+ switch (irq) {
+ case GLUE_IRQ_IN_VIA1:
+ irq = 0;
+ break;
+
+ case GLUE_IRQ_IN_VIA2:
+ irq = 1;
+ break;
+
+ case GLUE_IRQ_IN_SONIC:
+ /* Route to VIA2 instead */
+ qemu_set_irq(s->irqs[GLUE_IRQ_NUBUS_9], level);
+ return;
+
+ case GLUE_IRQ_IN_ESCC:
+ irq = 3;
+ break;
+
+ case GLUE_IRQ_IN_NMI:
+ irq = 6;
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+ } else {
+ /* A/UX mode */
+ switch (irq) {
+ case GLUE_IRQ_IN_VIA1:
+ irq = 5;
+ break;
+
+ case GLUE_IRQ_IN_VIA2:
+ irq = 1;
+ break;
+
+ case GLUE_IRQ_IN_SONIC:
+ irq = 2;
+ break;
+
+ case GLUE_IRQ_IN_ESCC:
+ irq = 3;
+ break;
+
+ case GLUE_IRQ_IN_NMI:
+ irq = 6;
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ if (level) {
+ s->ipr |= 1 << irq;
+ } else {
+ s->ipr &= ~(1 << irq);
+ }
+
+ for (i = 7; i >= 0; i--) {
+ if ((s->ipr >> i) & 1) {
+ m68k_set_irq_level(s->cpu, i + 1, i + 25);
+ return;
+ }
+ }
+ m68k_set_irq_level(s->cpu, 0, 0);
+}
+
+static void glue_auxmode_set_irq(void *opaque, int irq, int level)
+{
+ GLUEState *s = GLUE(opaque);
+
+ s->auxmode = level;
+}
+
+static void glue_nmi(NMIState *n, int cpu_index, Error **errp)
+{
+ GLUEState *s = GLUE(n);
+
+ /* Hold NMI active for 100ms */
+ GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 1);
+ timer_mod(s->nmi_release, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 100);
+}
+
+static void glue_nmi_release(void *opaque)
+{
+ GLUEState *s = GLUE(opaque);
+
+ GLUE_set_irq(s, GLUE_IRQ_IN_NMI, 0);
+}
+
+static void glue_reset(DeviceState *dev)
+{
+ GLUEState *s = GLUE(dev);
+
+ s->ipr = 0;
+ s->auxmode = 0;
+
+ timer_del(s->nmi_release);
+}
+
+static const VMStateDescription vmstate_glue = {
+ .name = "q800-glue",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(ipr, GLUEState),
+ VMSTATE_UINT8(auxmode, GLUEState),
+ VMSTATE_TIMER_PTR(nmi_release, GLUEState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+/*
+ * If the m68k CPU implemented its inbound irq lines as GPIO lines
+ * rather than via the m68k_set_irq_level() function we would not need
+ * this cpu link property and could instead provide outbound IRQ lines
+ * that the board could wire up to the CPU.
+ */
+static Property glue_properties[] = {
+ DEFINE_PROP_LINK("cpu", GLUEState, cpu, TYPE_M68K_CPU, M68kCPU *),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void glue_finalize(Object *obj)
+{
+ GLUEState *s = GLUE(obj);
+
+ timer_free(s->nmi_release);
+}
+
+static void glue_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ GLUEState *s = GLUE(dev);
+
+ qdev_init_gpio_in(dev, GLUE_set_irq, 8);
+ qdev_init_gpio_in_named(dev, glue_auxmode_set_irq, "auxmode", 1);
+
+ qdev_init_gpio_out(dev, s->irqs, 1);
+
+ /* NMI release timer */
+ s->nmi_release = timer_new_ms(QEMU_CLOCK_VIRTUAL, glue_nmi_release, s);
+}
+
+static void glue_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ NMIClass *nc = NMI_CLASS(klass);
+
+ dc->vmsd = &vmstate_glue;
+ dc->reset = glue_reset;
+ device_class_set_props(dc, glue_properties);
+ nc->nmi_monitor_handler = glue_nmi;
+}
+
+static const TypeInfo glue_info = {
+ .name = TYPE_GLUE,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(GLUEState),
+ .instance_init = glue_init,
+ .instance_finalize = glue_finalize,
+ .class_init = glue_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_NMI },
+ { }
+ },
+};
+
+static void main_cpu_reset(void *opaque)
+{
+ M68kCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
+
+ cpu_reset(cs);
+ cpu->env.aregs[7] = ldl_phys(cs->as, 0);
+ cpu->env.pc = ldl_phys(cs->as, 4);
+}
+
+static uint8_t fake_mac_rom[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+ /* offset: 0xa - mac_reset */
+
+ /* via2[vDirB] |= VIA2B_vPower */
+ 0x20, 0x7C, 0x50, 0xF0, 0x24, 0x00, /* moveal VIA2_BASE+vDirB,%a0 */
+ 0x10, 0x10, /* moveb %a0@,%d0 */
+ 0x00, 0x00, 0x00, 0x04, /* orib #4,%d0 */
+ 0x10, 0x80, /* moveb %d0,%a0@ */
+
+ /* via2[vBufB] &= ~VIA2B_vPower */
+ 0x20, 0x7C, 0x50, 0xF0, 0x20, 0x00, /* moveal VIA2_BASE+vBufB,%a0 */
+ 0x10, 0x10, /* moveb %a0@,%d0 */
+ 0x02, 0x00, 0xFF, 0xFB, /* andib #-5,%d0 */
+ 0x10, 0x80, /* moveb %d0,%a0@ */
+
+ /* while (true) ; */
+ 0x60, 0xFE /* bras [self] */
+};
+
+static void q800_init(MachineState *machine)
+{
+ M68kCPU *cpu = NULL;
+ int linux_boot;
+ int32_t kernel_size;
+ uint64_t elf_entry;
+ char *filename;
+ int bios_size;
+ ram_addr_t initrd_base;
+ int32_t initrd_size;
+ MemoryRegion *rom;
+ MemoryRegion *io;
+ MemoryRegion *dp8393x_prom = g_new(MemoryRegion, 1);
+ uint8_t *prom;
+ const int io_slice_nb = (IO_SIZE / IO_SLICE) - 1;
+ int i, checksum;
+ MacFbMode *macfb_mode;
+ ram_addr_t ram_size = machine->ram_size;
+ const char *kernel_filename = machine->kernel_filename;
+ const char *initrd_filename = machine->initrd_filename;
+ const char *kernel_cmdline = machine->kernel_cmdline;
+ const char *bios_name = machine->firmware ?: MACROM_FILENAME;
+ hwaddr parameters_base;
+ CPUState *cs;
+ DeviceState *dev;
+ DeviceState *via1_dev, *via2_dev;
+ DeviceState *escc_orgate;
+ SysBusESPState *sysbus_esp;
+ ESPState *esp;
+ SysBusDevice *sysbus;
+ BusState *adb_bus;
+ NubusBus *nubus;
+ DeviceState *glue;
+ DriveInfo *dinfo;
+
+ linux_boot = (kernel_filename != NULL);
+
+ if (ram_size > 1 * GiB) {
+ error_report("Too much memory for this machine: %" PRId64 " MiB, "
+ "maximum 1024 MiB", ram_size / MiB);
+ exit(1);
+ }
+
+ /* init CPUs */
+ cpu = M68K_CPU(cpu_create(machine->cpu_type));
+ qemu_register_reset(main_cpu_reset, cpu);
+
+ /* RAM */
+ memory_region_add_subregion(get_system_memory(), 0, machine->ram);
+
+ /*
+ * Memory from IO_BASE to IO_BASE + IO_SLICE is repeated
+ * from IO_BASE + IO_SLICE to IO_BASE + IO_SIZE
+ */
+ io = g_new(MemoryRegion, io_slice_nb);
+ for (i = 0; i < io_slice_nb; i++) {
+ char *name = g_strdup_printf("mac_m68k.io[%d]", i + 1);
+
+ memory_region_init_alias(&io[i], NULL, name, get_system_memory(),
+ IO_BASE, IO_SLICE);
+ memory_region_add_subregion(get_system_memory(),
+ IO_BASE + (i + 1) * IO_SLICE, &io[i]);
+ g_free(name);
+ }
+
+ /* IRQ Glue */
+ glue = qdev_new(TYPE_GLUE);
+ object_property_set_link(OBJECT(glue), "cpu", OBJECT(cpu), &error_abort);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(glue), &error_fatal);
+
+ /* VIA 1 */
+ via1_dev = qdev_new(TYPE_MOS6522_Q800_VIA1);
+ dinfo = drive_get(IF_MTD, 0, 0);
+ if (dinfo) {
+ qdev_prop_set_drive(via1_dev, "drive", blk_by_legacy_dinfo(dinfo));
+ }
+ sysbus = SYS_BUS_DEVICE(via1_dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_mmio_map(sysbus, 1, VIA_BASE);
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, GLUE_IRQ_IN_VIA1));
+ /* A/UX mode */
+ qdev_connect_gpio_out(via1_dev, 0,
+ qdev_get_gpio_in_named(glue, "auxmode", 0));
+
+ adb_bus = qdev_get_child_bus(via1_dev, "adb.0");
+ dev = qdev_new(TYPE_ADB_KEYBOARD);
+ qdev_realize_and_unref(dev, adb_bus, &error_fatal);
+ dev = qdev_new(TYPE_ADB_MOUSE);
+ qdev_realize_and_unref(dev, adb_bus, &error_fatal);
+
+ /* VIA 2 */
+ via2_dev = qdev_new(TYPE_MOS6522_Q800_VIA2);
+ sysbus = SYS_BUS_DEVICE(via2_dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_mmio_map(sysbus, 1, VIA_BASE + VIA_SIZE);
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, GLUE_IRQ_IN_VIA2));
+
+ /* MACSONIC */
+
+ if (nb_nics > 1) {
+ error_report("q800 can only have one ethernet interface");
+ exit(1);
+ }
+
+ qemu_check_nic_model(&nd_table[0], "dp83932");
+
+ /*
+ * MacSonic driver needs an Apple MAC address
+ * Valid prefix are:
+ * 00:05:02 Apple
+ * 00:80:19 Dayna Communications, Inc.
+ * 00:A0:40 Apple
+ * 08:00:07 Apple
+ * (Q800 use the last one)
+ */
+ nd_table[0].macaddr.a[0] = 0x08;
+ nd_table[0].macaddr.a[1] = 0x00;
+ nd_table[0].macaddr.a[2] = 0x07;
+
+ dev = qdev_new("dp8393x");
+ qdev_set_nic_properties(dev, &nd_table[0]);
+ qdev_prop_set_uint8(dev, "it_shift", 2);
+ qdev_prop_set_bit(dev, "big_endian", true);
+ object_property_set_link(OBJECT(dev), "dma_mr",
+ OBJECT(get_system_memory()), &error_abort);
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_mmio_map(sysbus, 0, SONIC_BASE);
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(glue, GLUE_IRQ_IN_SONIC));
+
+ memory_region_init_rom(dp8393x_prom, NULL, "dp8393x-q800.prom",
+ SONIC_PROM_SIZE, &error_fatal);
+ memory_region_add_subregion(get_system_memory(), SONIC_PROM_BASE,
+ dp8393x_prom);
+
+ /* Add MAC address with valid checksum to PROM */
+ prom = memory_region_get_ram_ptr(dp8393x_prom);
+ checksum = 0;
+ for (i = 0; i < 6; i++) {
+ prom[i] = revbit8(nd_table[0].macaddr.a[i]);
+ checksum ^= prom[i];
+ }
+ prom[7] = 0xff - checksum;
+
+ /* SCC */
+
+ dev = qdev_new(TYPE_ESCC);
+ qdev_prop_set_uint32(dev, "disabled", 0);
+ qdev_prop_set_uint32(dev, "frequency", MAC_CLOCK);
+ qdev_prop_set_uint32(dev, "it_shift", 1);
+ qdev_prop_set_bit(dev, "bit_swap", true);
+ qdev_prop_set_chr(dev, "chrA", serial_hd(0));
+ qdev_prop_set_chr(dev, "chrB", serial_hd(1));
+ qdev_prop_set_uint32(dev, "chnBtype", 0);
+ qdev_prop_set_uint32(dev, "chnAtype", 0);
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+
+ /* Logically OR both its IRQs together */
+ escc_orgate = DEVICE(object_new(TYPE_OR_IRQ));
+ object_property_set_int(OBJECT(escc_orgate), "num-lines", 2, &error_fatal);
+ qdev_realize_and_unref(escc_orgate, NULL, &error_fatal);
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(escc_orgate, 0));
+ sysbus_connect_irq(sysbus, 1, qdev_get_gpio_in(escc_orgate, 1));
+ qdev_connect_gpio_out(DEVICE(escc_orgate), 0,
+ qdev_get_gpio_in(glue, GLUE_IRQ_IN_ESCC));
+ sysbus_mmio_map(sysbus, 0, SCC_BASE);
+
+ /* SCSI */
+
+ dev = qdev_new(TYPE_SYSBUS_ESP);
+ sysbus_esp = SYSBUS_ESP(dev);
+ esp = &sysbus_esp->esp;
+ esp->dma_memory_read = NULL;
+ esp->dma_memory_write = NULL;
+ esp->dma_opaque = NULL;
+ sysbus_esp->it_shift = 4;
+ esp->dma_enabled = 1;
+
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(via2_dev,
+ VIA2_IRQ_SCSI_BIT));
+ sysbus_connect_irq(sysbus, 1, qdev_get_gpio_in(via2_dev,
+ VIA2_IRQ_SCSI_DATA_BIT));
+ sysbus_mmio_map(sysbus, 0, ESP_BASE);
+ sysbus_mmio_map(sysbus, 1, ESP_PDMA);
+
+ scsi_bus_legacy_handle_cmdline(&esp->bus);
+
+ /* SWIM floppy controller */
+
+ dev = qdev_new(TYPE_SWIM);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, SWIM_BASE);
+
+ /* NuBus */
+
+ dev = qdev_new(TYPE_MAC_NUBUS_BRIDGE);
+ qdev_prop_set_uint32(dev, "slot-available-mask",
+ Q800_NUBUS_SLOTS_AVAILABLE);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0,
+ MAC_NUBUS_FIRST_SLOT * NUBUS_SUPER_SLOT_SIZE);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, NUBUS_SLOT_BASE +
+ MAC_NUBUS_FIRST_SLOT * NUBUS_SLOT_SIZE);
+ qdev_connect_gpio_out(dev, 9,
+ qdev_get_gpio_in_named(via2_dev, "nubus-irq",
+ VIA2_NUBUS_IRQ_INTVIDEO));
+ for (i = 1; i < VIA2_NUBUS_IRQ_NB; i++) {
+ qdev_connect_gpio_out(dev, 9 + i,
+ qdev_get_gpio_in_named(via2_dev, "nubus-irq",
+ VIA2_NUBUS_IRQ_9 + i));
+ }
+
+ /*
+ * Since the framebuffer in slot 0x9 uses a separate IRQ, wire the unused
+ * IRQ via GLUE for use by SONIC Ethernet in classic mode
+ */
+ qdev_connect_gpio_out(glue, GLUE_IRQ_NUBUS_9,
+ qdev_get_gpio_in_named(via2_dev, "nubus-irq",
+ VIA2_NUBUS_IRQ_9));
+
+ nubus = &NUBUS_BRIDGE(dev)->bus;
+
+ /* framebuffer in nubus slot #9 */
+
+ dev = qdev_new(TYPE_NUBUS_MACFB);
+ qdev_prop_set_uint32(dev, "slot", 9);
+ qdev_prop_set_uint32(dev, "width", graphic_width);
+ qdev_prop_set_uint32(dev, "height", graphic_height);
+ qdev_prop_set_uint8(dev, "depth", graphic_depth);
+ if (graphic_width == 1152 && graphic_height == 870) {
+ qdev_prop_set_uint8(dev, "display", MACFB_DISPLAY_APPLE_21_COLOR);
+ } else {
+ qdev_prop_set_uint8(dev, "display", MACFB_DISPLAY_VGA);
+ }
+ qdev_realize_and_unref(dev, BUS(nubus), &error_fatal);
+
+ macfb_mode = (NUBUS_MACFB(dev)->macfb).mode;
+
+ cs = CPU(cpu);
+ if (linux_boot) {
+ uint64_t high;
+ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL,
+ &elf_entry, NULL, &high, NULL, 1,
+ EM_68K, 0, 0);
+ if (kernel_size < 0) {
+ error_report("could not load kernel '%s'", kernel_filename);
+ exit(1);
+ }
+ stl_phys(cs->as, 4, elf_entry); /* reset initial PC */
+ parameters_base = (high + 1) & ~1;
+
+ BOOTINFO1(cs->as, parameters_base, BI_MACHTYPE, MACH_MAC);
+ BOOTINFO1(cs->as, parameters_base, BI_FPUTYPE, FPU_68040);
+ BOOTINFO1(cs->as, parameters_base, BI_MMUTYPE, MMU_68040);
+ BOOTINFO1(cs->as, parameters_base, BI_CPUTYPE, CPU_68040);
+ BOOTINFO1(cs->as, parameters_base, BI_MAC_CPUID, CPUB_68040);
+ BOOTINFO1(cs->as, parameters_base, BI_MAC_MODEL, MAC_MODEL_Q800);
+ BOOTINFO1(cs->as, parameters_base,
+ BI_MAC_MEMSIZE, ram_size >> 20); /* in MB */
+ BOOTINFO2(cs->as, parameters_base, BI_MEMCHUNK, 0, ram_size);
+ BOOTINFO1(cs->as, parameters_base, BI_MAC_VADDR,
+ VIDEO_BASE + macfb_mode->offset);
+ BOOTINFO1(cs->as, parameters_base, BI_MAC_VDEPTH, graphic_depth);
+ BOOTINFO1(cs->as, parameters_base, BI_MAC_VDIM,
+ (graphic_height << 16) | graphic_width);
+ BOOTINFO1(cs->as, parameters_base, BI_MAC_VROW, macfb_mode->stride);
+ BOOTINFO1(cs->as, parameters_base, BI_MAC_SCCBASE, SCC_BASE);
+
+ rom = g_malloc(sizeof(*rom));
+ memory_region_init_ram_ptr(rom, NULL, "m68k_fake_mac.rom",
+ sizeof(fake_mac_rom), fake_mac_rom);
+ memory_region_set_readonly(rom, true);
+ memory_region_add_subregion(get_system_memory(), MACROM_ADDR, rom);
+
+ if (kernel_cmdline) {
+ BOOTINFOSTR(cs->as, parameters_base, BI_COMMAND_LINE,
+ kernel_cmdline);
+ }
+
+ /* load initrd */
+ if (initrd_filename) {
+ initrd_size = get_image_size(initrd_filename);
+ if (initrd_size < 0) {
+ error_report("could not load initial ram disk '%s'",
+ initrd_filename);
+ exit(1);
+ }
+
+ initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK;
+ load_image_targphys(initrd_filename, initrd_base,
+ ram_size - initrd_base);
+ BOOTINFO2(cs->as, parameters_base, BI_RAMDISK, initrd_base,
+ initrd_size);
+ } else {
+ initrd_base = 0;
+ initrd_size = 0;
+ }
+ BOOTINFO0(cs->as, parameters_base, BI_LAST);
+ } else {
+ uint8_t *ptr;
+ /* allocate and load BIOS */
+ rom = g_malloc(sizeof(*rom));
+ memory_region_init_rom(rom, NULL, "m68k_mac.rom", MACROM_SIZE,
+ &error_abort);
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ memory_region_add_subregion(get_system_memory(), MACROM_ADDR, rom);
+
+ /* Load MacROM binary */
+ if (filename) {
+ bios_size = load_image_targphys(filename, MACROM_ADDR, MACROM_SIZE);
+ g_free(filename);
+ } else {
+ bios_size = -1;
+ }
+
+ /* Remove qtest_enabled() check once firmware files are in the tree */
+ if (!qtest_enabled()) {
+ if (bios_size < 0 || bios_size > MACROM_SIZE) {
+ error_report("could not load MacROM '%s'", bios_name);
+ exit(1);
+ }
+
+ ptr = rom_ptr(MACROM_ADDR, MACROM_SIZE);
+ stl_phys(cs->as, 0, ldl_p(ptr)); /* reset initial SP */
+ stl_phys(cs->as, 4,
+ MACROM_ADDR + ldl_p(ptr + 4)); /* reset initial PC */
+ }
+ }
+}
+
+static void q800_machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ mc->desc = "Macintosh Quadra 800";
+ mc->init = q800_init;
+ mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040");
+ mc->max_cpus = 1;
+ mc->block_default_type = IF_SCSI;
+ mc->default_ram_id = "m68k_mac.ram";
+}
+
+static const TypeInfo q800_machine_typeinfo = {
+ .name = MACHINE_TYPE_NAME("q800"),
+ .parent = TYPE_MACHINE,
+ .class_init = q800_machine_class_init,
+};
+
+static void q800_machine_register_types(void)
+{
+ type_register_static(&q800_machine_typeinfo);
+ type_register_static(&glue_info);
+}
+
+type_init(q800_machine_register_types)
diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c
new file mode 100644
index 000000000..0efa4a45c
--- /dev/null
+++ b/hw/m68k/virt.c
@@ -0,0 +1,324 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QEMU Vitual M68K Machine
+ *
+ * (c) 2020 Laurent Vivier <laurent@vivier.eu>
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu-common.h"
+#include "sysemu/sysemu.h"
+#include "cpu.h"
+#include "hw/boards.h"
+#include "hw/qdev-properties.h"
+#include "elf.h"
+#include "hw/loader.h"
+#include "ui/console.h"
+#include "hw/sysbus.h"
+#include "standard-headers/asm-m68k/bootinfo.h"
+#include "standard-headers/asm-m68k/bootinfo-virt.h"
+#include "bootinfo.h"
+#include "net/net.h"
+#include "qapi/error.h"
+#include "sysemu/qtest.h"
+#include "sysemu/runstate.h"
+#include "sysemu/reset.h"
+
+#include "hw/intc/m68k_irqc.h"
+#include "hw/misc/virt_ctrl.h"
+#include "hw/char/goldfish_tty.h"
+#include "hw/rtc/goldfish_rtc.h"
+#include "hw/intc/goldfish_pic.h"
+#include "hw/virtio/virtio-mmio.h"
+#include "hw/virtio/virtio-blk.h"
+
+/*
+ * 6 goldfish-pic for CPU IRQ #1 to IRQ #6
+ * CPU IRQ #1 -> PIC #1
+ * IRQ #1 to IRQ #31 -> unused
+ * IRQ #32 -> goldfish-tty
+ * CPU IRQ #2 -> PIC #2
+ * IRQ #1 to IRQ #32 -> virtio-mmio from 1 to 32
+ * CPU IRQ #3 -> PIC #3
+ * IRQ #1 to IRQ #32 -> virtio-mmio from 33 to 64
+ * CPU IRQ #4 -> PIC #4
+ * IRQ #1 to IRQ #32 -> virtio-mmio from 65 to 96
+ * CPU IRQ #5 -> PIC #5
+ * IRQ #1 to IRQ #32 -> virtio-mmio from 97 to 128
+ * CPU IRQ #6 -> PIC #6
+ * IRQ #1 -> goldfish-rtc
+ * IRQ #2 to IRQ #32 -> unused
+ * CPU IRQ #7 -> NMI
+ */
+
+#define PIC_IRQ_BASE(num) (8 + (num - 1) * 32)
+#define PIC_IRQ(num, irq) (PIC_IRQ_BASE(num) + irq - 1)
+#define PIC_GPIO(pic_irq) (qdev_get_gpio_in(pic_dev[(pic_irq - 8) / 32], \
+ (pic_irq - 8) % 32))
+
+#define VIRT_GF_PIC_MMIO_BASE 0xff000000 /* MMIO: 0xff000000 - 0xff005fff */
+#define VIRT_GF_PIC_IRQ_BASE 1 /* IRQ: #1 -> #6 */
+#define VIRT_GF_PIC_NB 6
+
+/* 2 goldfish-rtc (and timer) */
+#define VIRT_GF_RTC_MMIO_BASE 0xff006000 /* MMIO: 0xff006000 - 0xff007fff */
+#define VIRT_GF_RTC_IRQ_BASE PIC_IRQ(6, 1) /* PIC: #6, IRQ: #1 */
+#define VIRT_GF_RTC_NB 2
+
+/* 1 goldfish-tty */
+#define VIRT_GF_TTY_MMIO_BASE 0xff008000 /* MMIO: 0xff008000 - 0xff008fff */
+#define VIRT_GF_TTY_IRQ_BASE PIC_IRQ(1, 32) /* PIC: #1, IRQ: #32 */
+
+/* 1 virt-ctrl */
+#define VIRT_CTRL_MMIO_BASE 0xff009000 /* MMIO: 0xff009000 - 0xff009fff */
+#define VIRT_CTRL_IRQ_BASE PIC_IRQ(1, 1) /* PIC: #1, IRQ: #1 */
+
+/*
+ * virtio-mmio size is 0x200 bytes
+ * we use 4 goldfish-pic to attach them,
+ * we can attach 32 virtio devices / goldfish-pic
+ * -> we can manage 32 * 4 = 128 virtio devices
+ */
+#define VIRT_VIRTIO_MMIO_BASE 0xff010000 /* MMIO: 0xff010000 - 0xff01ffff */
+#define VIRT_VIRTIO_IRQ_BASE PIC_IRQ(2, 1) /* PIC: 2, 3, 4, 5, IRQ: ALL */
+
+static void main_cpu_reset(void *opaque)
+{
+ M68kCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
+
+ cpu_reset(cs);
+ cpu->env.aregs[7] = ldl_phys(cs->as, 0);
+ cpu->env.pc = ldl_phys(cs->as, 4);
+}
+
+static void virt_init(MachineState *machine)
+{
+ M68kCPU *cpu = NULL;
+ int32_t kernel_size;
+ uint64_t elf_entry;
+ ram_addr_t initrd_base;
+ int32_t initrd_size;
+ ram_addr_t ram_size = machine->ram_size;
+ const char *kernel_filename = machine->kernel_filename;
+ const char *initrd_filename = machine->initrd_filename;
+ const char *kernel_cmdline = machine->kernel_cmdline;
+ hwaddr parameters_base;
+ DeviceState *dev;
+ DeviceState *irqc_dev;
+ DeviceState *pic_dev[VIRT_GF_PIC_NB];
+ SysBusDevice *sysbus;
+ hwaddr io_base;
+ int i;
+
+ if (ram_size > 3399672 * KiB) {
+ /*
+ * The physical memory can be up to 4 GiB - 16 MiB, but linux
+ * kernel crashes after this limit (~ 3.2 GiB)
+ */
+ error_report("Too much memory for this machine: %" PRId64 " KiB, "
+ "maximum 3399672 KiB", ram_size / KiB);
+ exit(1);
+ }
+
+ /* init CPUs */
+ cpu = M68K_CPU(cpu_create(machine->cpu_type));
+ qemu_register_reset(main_cpu_reset, cpu);
+
+ /* RAM */
+ memory_region_add_subregion(get_system_memory(), 0, machine->ram);
+
+ /* IRQ Controller */
+
+ irqc_dev = qdev_new(TYPE_M68K_IRQC);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(irqc_dev), &error_fatal);
+
+ /*
+ * 6 goldfish-pic
+ *
+ * map: 0xff000000 - 0xff006fff = 28 KiB
+ * IRQ: #1 (lower priority) -> #6 (higher priority)
+ *
+ */
+ io_base = VIRT_GF_PIC_MMIO_BASE;
+ for (i = 0; i < VIRT_GF_PIC_NB; i++) {
+ pic_dev[i] = qdev_new(TYPE_GOLDFISH_PIC);
+ sysbus = SYS_BUS_DEVICE(pic_dev[i]);
+ qdev_prop_set_uint8(pic_dev[i], "index", i);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+
+ sysbus_mmio_map(sysbus, 0, io_base);
+ sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(irqc_dev, i));
+
+ io_base += 0x1000;
+ }
+
+ /* goldfish-rtc */
+ io_base = VIRT_GF_RTC_MMIO_BASE;
+ for (i = 0; i < VIRT_GF_RTC_NB; i++) {
+ dev = qdev_new(TYPE_GOLDFISH_RTC);
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_mmio_map(sysbus, 0, io_base);
+ sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_GF_RTC_IRQ_BASE + i));
+
+ io_base += 0x1000;
+ }
+
+ /* goldfish-tty */
+ dev = qdev_new(TYPE_GOLDFISH_TTY);
+ sysbus = SYS_BUS_DEVICE(dev);
+ qdev_prop_set_chr(dev, "chardev", serial_hd(0));
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_mmio_map(sysbus, 0, VIRT_GF_TTY_MMIO_BASE);
+ sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_GF_TTY_IRQ_BASE));
+
+ /* virt controller */
+ dev = qdev_new(TYPE_VIRT_CTRL);
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_mmio_map(sysbus, 0, VIRT_CTRL_MMIO_BASE);
+ sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_CTRL_IRQ_BASE));
+
+ /* virtio-mmio */
+ io_base = VIRT_VIRTIO_MMIO_BASE;
+ for (i = 0; i < 128; i++) {
+ dev = qdev_new(TYPE_VIRTIO_MMIO);
+ qdev_prop_set_bit(dev, "force-legacy", false);
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_VIRTIO_IRQ_BASE + i));
+ sysbus_mmio_map(sysbus, 0, io_base);
+ io_base += 0x200;
+ }
+
+ if (kernel_filename) {
+ CPUState *cs = CPU(cpu);
+ uint64_t high;
+
+ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL,
+ &elf_entry, NULL, &high, NULL, 1,
+ EM_68K, 0, 0);
+ if (kernel_size < 0) {
+ error_report("could not load kernel '%s'", kernel_filename);
+ exit(1);
+ }
+ stl_phys(cs->as, 4, elf_entry); /* reset initial PC */
+ parameters_base = (high + 1) & ~1;
+
+ BOOTINFO1(cs->as, parameters_base, BI_MACHTYPE, MACH_VIRT);
+ BOOTINFO1(cs->as, parameters_base, BI_FPUTYPE, FPU_68040);
+ BOOTINFO1(cs->as, parameters_base, BI_MMUTYPE, MMU_68040);
+ BOOTINFO1(cs->as, parameters_base, BI_CPUTYPE, CPU_68040);
+ BOOTINFO2(cs->as, parameters_base, BI_MEMCHUNK, 0, ram_size);
+
+ BOOTINFO1(cs->as, parameters_base, BI_VIRT_QEMU_VERSION,
+ ((QEMU_VERSION_MAJOR << 24) | (QEMU_VERSION_MINOR << 16) |
+ (QEMU_VERSION_MICRO << 8)));
+ BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_PIC_BASE,
+ VIRT_GF_PIC_MMIO_BASE, VIRT_GF_PIC_IRQ_BASE);
+ BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_RTC_BASE,
+ VIRT_GF_RTC_MMIO_BASE, VIRT_GF_RTC_IRQ_BASE);
+ BOOTINFO2(cs->as, parameters_base, BI_VIRT_GF_TTY_BASE,
+ VIRT_GF_TTY_MMIO_BASE, VIRT_GF_TTY_IRQ_BASE);
+ BOOTINFO2(cs->as, parameters_base, BI_VIRT_CTRL_BASE,
+ VIRT_CTRL_MMIO_BASE, VIRT_CTRL_IRQ_BASE);
+ BOOTINFO2(cs->as, parameters_base, BI_VIRT_VIRTIO_BASE,
+ VIRT_VIRTIO_MMIO_BASE, VIRT_VIRTIO_IRQ_BASE);
+
+ if (kernel_cmdline) {
+ BOOTINFOSTR(cs->as, parameters_base, BI_COMMAND_LINE,
+ kernel_cmdline);
+ }
+
+ /* load initrd */
+ if (initrd_filename) {
+ initrd_size = get_image_size(initrd_filename);
+ if (initrd_size < 0) {
+ error_report("could not load initial ram disk '%s'",
+ initrd_filename);
+ exit(1);
+ }
+
+ initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK;
+ load_image_targphys(initrd_filename, initrd_base,
+ ram_size - initrd_base);
+ BOOTINFO2(cs->as, parameters_base, BI_RAMDISK, initrd_base,
+ initrd_size);
+ } else {
+ initrd_base = 0;
+ initrd_size = 0;
+ }
+ BOOTINFO0(cs->as, parameters_base, BI_LAST);
+ }
+}
+
+static void virt_machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ mc->desc = "QEMU M68K Virtual Machine";
+ mc->init = virt_init;
+ mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040");
+ mc->max_cpus = 1;
+ mc->no_floppy = 1;
+ mc->no_parallel = 1;
+ mc->default_ram_id = "m68k_virt.ram";
+}
+
+static const TypeInfo virt_machine_info = {
+ .name = MACHINE_TYPE_NAME("virt"),
+ .parent = TYPE_MACHINE,
+ .abstract = true,
+ .class_init = virt_machine_class_init,
+};
+
+static void virt_machine_register_types(void)
+{
+ type_register_static(&virt_machine_info);
+}
+
+type_init(virt_machine_register_types)
+
+#define DEFINE_VIRT_MACHINE(major, minor, latest) \
+ static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
+ void *data) \
+ { \
+ MachineClass *mc = MACHINE_CLASS(oc); \
+ virt_machine_##major##_##minor##_options(mc); \
+ mc->desc = "QEMU " # major "." # minor " M68K Virtual Machine"; \
+ if (latest) { \
+ mc->alias = "virt"; \
+ } \
+ } \
+ static const TypeInfo machvirt_##major##_##minor##_info = { \
+ .name = MACHINE_TYPE_NAME("virt-" # major "." # minor), \
+ .parent = MACHINE_TYPE_NAME("virt"), \
+ .class_init = virt_##major##_##minor##_class_init, \
+ }; \
+ static void machvirt_machine_##major##_##minor##_init(void) \
+ { \
+ type_register_static(&machvirt_##major##_##minor##_info); \
+ } \
+ type_init(machvirt_machine_##major##_##minor##_init);
+
+static void virt_machine_6_2_options(MachineClass *mc)
+{
+}
+DEFINE_VIRT_MACHINE(6, 2, true)
+
+static void virt_machine_6_1_options(MachineClass *mc)
+{
+ virt_machine_6_2_options(mc);
+ compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
+}
+DEFINE_VIRT_MACHINE(6, 1, false)
+
+static void virt_machine_6_0_options(MachineClass *mc)
+{
+ virt_machine_6_1_options(mc);
+ compat_props_add(mc->compat_props, hw_compat_6_0, hw_compat_6_0_len);
+}
+DEFINE_VIRT_MACHINE(6, 0, false)