aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBlue Swirl <blauwirbel@gmail.com>2011-10-08 16:03:58 +0000
committerBlue Swirl <blauwirbel@gmail.com>2011-10-08 16:03:58 +0000
commit9a7242f7867e1f1e70013aa70d148c65a1980aec (patch)
tree95c6a1112e4781329d3c5c6010033e093886686d
parent53e621704c9734d68fcae52d89ff00680df53044 (diff)
parentc781cf96e298b9134b05ed1e7ca981a929e08e77 (diff)
Merge branch 'axp-system-7' of git://repo.or.cz/qemu/rth
* 'axp-system-7' of git://repo.or.cz/qemu/rth: target-alpha: Add high-resolution access to wall clock and an alarm. target-alpha: Implement HALT IPR. target-alpha: Implement WAIT IPR. target-alpha: Add CLIPPER emulation. target-alpha: Add custom PALcode image for CLIPPER emulation. target-alpha: Honor icount for RPCC instruction.
-rw-r--r--.gitmodules3
-rw-r--r--Makefile3
-rw-r--r--Makefile.target1
-rwxr-xr-xconfigure8
-rw-r--r--default-configs/alpha-softmmu.mak2
-rw-r--r--hw/alpha_dp264.c177
-rw-r--r--hw/alpha_pci.c134
-rw-r--r--hw/alpha_sys.h24
-rw-r--r--hw/alpha_typhoon.c813
-rw-r--r--pc-bios/README3
-rwxr-xr-xpc-bios/palcode-clipperbin0 -> 185703 bytes
m---------roms/qemu-palcode0
-rw-r--r--target-alpha/cpu.h4
-rw-r--r--target-alpha/helper.h5
-rw-r--r--target-alpha/op_helper.c25
-rw-r--r--target-alpha/translate.c77
16 files changed, 1261 insertions, 18 deletions
diff --git a/.gitmodules b/.gitmodules
index c3faa38790..2a43dbcd7c 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -13,3 +13,6 @@
[submodule "roms/openbios"]
path = roms/openbios
url = git://git.qemu.org/openbios.git
+[submodule "roms/qemu-palcode"]
+ path = roms/qemu-palcode
+ url = git://repo.or.cz/qemu-palcode.git
diff --git a/Makefile b/Makefile
index 6ed3194fd7..8b6c19a0a5 100644
--- a/Makefile
+++ b/Makefile
@@ -251,7 +251,8 @@ bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
mpc8544ds.dtb \
multiboot.bin linuxboot.bin \
s390-zipl.rom \
-spapr-rtas.bin slof.bin
+spapr-rtas.bin slof.bin \
+palcode-clipper
else
BLOBS=
endif
diff --git a/Makefile.target b/Makefile.target
index 1e9815cb2d..1aa6fce690 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -367,6 +367,7 @@ obj-s390x-y = s390-virtio-bus.o s390-virtio.o
obj-alpha-y = i8259.o mc146818rtc.o
obj-alpha-y += vga.o cirrus_vga.o
+obj-alpha-y += alpha_pci.o alpha_dp264.o alpha_typhoon.o
obj-xtensa-y += xtensa_pic.o
obj-xtensa-y += xtensa_sample.o
diff --git a/configure b/configure
index 29cdd752a9..9b4fe34fce 100755
--- a/configure
+++ b/configure
@@ -3618,7 +3618,13 @@ FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit"
FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps"
FILES="$FILES pc-bios/spapr-rtas/Makefile"
FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile"
-for bios_file in $source_path/pc-bios/*.bin $source_path/pc-bios/*.rom $source_path/pc-bios/*.dtb $source_path/pc-bios/openbios-*; do
+for bios_file in \
+ $source_path/pc-bios/*.bin \
+ $source_path/pc-bios/*.rom \
+ $source_path/pc-bios/*.dtb \
+ $source_path/pc-bios/openbios-* \
+ $source_path/pc-bios/palcode-*
+do
FILES="$FILES pc-bios/`basename $bios_file`"
done
mkdir -p $DIRS
diff --git a/default-configs/alpha-softmmu.mak b/default-configs/alpha-softmmu.mak
index abadcffec9..be86d0c4e6 100644
--- a/default-configs/alpha-softmmu.mak
+++ b/default-configs/alpha-softmmu.mak
@@ -3,7 +3,9 @@
include pci.mak
CONFIG_SERIAL=y
CONFIG_I8254=y
+CONFIG_PCKBD=y
CONFIG_VGA_PCI=y
CONFIG_IDE_CORE=y
CONFIG_IDE_QDEV=y
CONFIG_VMWARE_VGA=y
+CONFIG_IDE_CMD646=y
diff --git a/hw/alpha_dp264.c b/hw/alpha_dp264.c
new file mode 100644
index 0000000000..7c36b21f0c
--- /dev/null
+++ b/hw/alpha_dp264.c
@@ -0,0 +1,177 @@
+/*
+ * QEMU Alpha DP264/CLIPPER hardware system emulator.
+ *
+ * Choose CLIPPER IRQ mappings over, say, DP264, MONET, or WEBBRICK
+ * variants because CLIPPER doesn't have an SMC669 SuperIO controler
+ * that we need to emulate as well.
+ */
+
+#include "hw.h"
+#include "elf.h"
+#include "loader.h"
+#include "boards.h"
+#include "alpha_sys.h"
+#include "sysemu.h"
+#include "mc146818rtc.h"
+#include "ide.h"
+
+#define MAX_IDE_BUS 2
+
+static uint64_t cpu_alpha_superpage_to_phys(void *opaque, uint64_t addr)
+{
+ if (((addr >> 41) & 3) == 2) {
+ addr &= 0xffffffffffull;
+ }
+ return addr;
+}
+
+/* Note that there are at least 3 viewpoints of IRQ numbers on Alpha systems.
+ (0) The dev_irq_n lines into the cpu, which we totally ignore,
+ (1) The DRIR lines in the typhoon chipset,
+ (2) The "vector" aka mangled interrupt number reported by SRM PALcode,
+ (3) The interrupt number assigned by the kernel.
+ The following function is concerned with (1) only. */
+
+static int clipper_pci_map_irq(PCIDevice *d, int irq_num)
+{
+ int slot = d->devfn >> 3;
+
+ assert(irq_num >= 0 && irq_num <= 3);
+
+ return (slot + 1) * 4 + irq_num;
+}
+
+static void clipper_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ CPUState *cpus[4];
+ PCIBus *pci_bus;
+ qemu_irq rtc_irq;
+ long size, i;
+ const char *palcode_filename;
+ uint64_t palcode_entry, palcode_low, palcode_high;
+ uint64_t kernel_entry, kernel_low, kernel_high;
+
+ /* Create up to 4 cpus. */
+ memset(cpus, 0, sizeof(cpus));
+ for (i = 0; i < smp_cpus; ++i) {
+ cpus[i] = cpu_init(cpu_model ? cpu_model : "ev67");
+ }
+
+ cpus[0]->trap_arg0 = ram_size;
+ cpus[0]->trap_arg1 = 0;
+ cpus[0]->trap_arg2 = smp_cpus;
+
+ /* Init the chipset. */
+ pci_bus = typhoon_init(ram_size, &rtc_irq, cpus, clipper_pci_map_irq);
+
+ rtc_init(1980, rtc_irq);
+ pit_init(0x40, 0);
+ isa_create_simple("i8042");
+
+ /* VGA setup. Don't bother loading the bios. */
+ alpha_pci_vga_setup(pci_bus);
+
+ /* Serial code setup. */
+ for (i = 0; i < MAX_SERIAL_PORTS; ++i) {
+ if (serial_hds[i]) {
+ serial_isa_init(i, serial_hds[i]);
+ }
+ }
+
+ /* Network setup. e1000 is good enough, failing Tulip support. */
+ for (i = 0; i < nb_nics; i++) {
+ pci_nic_init_nofail(&nd_table[i], "e1000", NULL);
+ }
+
+ /* IDE disk setup. */
+ {
+ DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+ ide_drive_get(hd, MAX_IDE_BUS);
+
+ pci_cmd646_ide_init(pci_bus, hd, 0);
+ }
+
+ /* Load PALcode. Given that this is not "real" cpu palcode,
+ but one explicitly written for the emulation, we might as
+ well load it directly from and ELF image. */
+ palcode_filename = (bios_name ? bios_name : "palcode-clipper");
+ palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, palcode_filename);
+ if (palcode_filename == NULL) {
+ hw_error("no palcode provided\n");
+ exit(1);
+ }
+ size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys,
+ NULL, &palcode_entry, &palcode_low, &palcode_high,
+ 0, EM_ALPHA, 0);
+ if (size < 0) {
+ hw_error("could not load palcode '%s'\n", palcode_filename);
+ exit(1);
+ }
+
+ /* Start all cpus at the PALcode RESET entry point. */
+ for (i = 0; i < smp_cpus; ++i) {
+ cpus[i]->pal_mode = 1;
+ cpus[i]->pc = palcode_entry;
+ cpus[i]->palbr = palcode_entry;
+ }
+
+ /* Load a kernel. */
+ if (kernel_filename) {
+ uint64_t param_offset;
+
+ size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys,
+ NULL, &kernel_entry, &kernel_low, &kernel_high,
+ 0, EM_ALPHA, 0);
+ if (size < 0) {
+ hw_error("could not load kernel '%s'\n", kernel_filename);
+ exit(1);
+ }
+
+ cpus[0]->trap_arg1 = kernel_entry;
+
+ param_offset = kernel_low - 0x6000;
+
+ if (kernel_cmdline) {
+ pstrcpy_targphys("cmdline", param_offset, 0x100, kernel_cmdline);
+ }
+
+ if (initrd_filename) {
+ long initrd_base, initrd_size;
+
+ initrd_size = get_image_size(initrd_filename);
+ if (initrd_size < 0) {
+ hw_error("could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+
+ /* Put the initrd image as high in memory as possible. */
+ initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK;
+ load_image_targphys(initrd_filename, initrd_base,
+ ram_size - initrd_base);
+
+ stq_phys(param_offset + 0x100, initrd_base + 0xfffffc0000000000UL);
+ stq_phys(param_offset + 0x108, initrd_size);
+ }
+ }
+}
+
+static QEMUMachine clipper_machine = {
+ .name = "clipper",
+ .desc = "Alpha DP264/CLIPPER",
+ .init = clipper_init,
+ .max_cpus = 4,
+ .is_default = 1,
+};
+
+static void clipper_machine_init(void)
+{
+ qemu_register_machine(&clipper_machine);
+}
+
+machine_init(clipper_machine_init);
diff --git a/hw/alpha_pci.c b/hw/alpha_pci.c
new file mode 100644
index 0000000000..e9757028af
--- /dev/null
+++ b/hw/alpha_pci.c
@@ -0,0 +1,134 @@
+/*
+ * QEMU Alpha PCI support functions.
+ *
+ * Some of this isn't very Alpha specific at all.
+ *
+ * ??? Sparse memory access not implemented.
+ */
+
+#include "config.h"
+#include "alpha_sys.h"
+#include "qemu-log.h"
+#include "sysemu.h"
+#include "vmware_vga.h"
+
+
+/* PCI IO reads/writes, to byte-word addressable memory. */
+/* ??? Doesn't handle multiple PCI busses. */
+
+static uint64_t bw_io_read(void *opaque, target_phys_addr_t addr, unsigned size)
+{
+ switch (size) {
+ case 1:
+ return cpu_inb(addr);
+ case 2:
+ return cpu_inw(addr);
+ case 4:
+ return cpu_inl(addr);
+ }
+ abort();
+}
+
+static void bw_io_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val, unsigned size)
+{
+ switch (size) {
+ case 1:
+ cpu_outb(addr, val);
+ break;
+ case 2:
+ cpu_outw(addr, val);
+ break;
+ case 4:
+ cpu_outl(addr, val);
+ break;
+ default:
+ abort();
+ }
+}
+
+const MemoryRegionOps alpha_pci_bw_io_ops = {
+ .read = bw_io_read,
+ .write = bw_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+};
+
+/* PCI config space reads/writes, to byte-word addressable memory. */
+static uint64_t bw_conf1_read(void *opaque, target_phys_addr_t addr,
+ unsigned size)
+{
+ PCIBus *b = opaque;
+ return pci_data_read(b, addr, size);
+}
+
+static void bw_conf1_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val, unsigned size)
+{
+ PCIBus *b = opaque;
+ pci_data_write(b, addr, val, size);
+}
+
+const MemoryRegionOps alpha_pci_conf1_ops = {
+ .read = bw_conf1_read,
+ .write = bw_conf1_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+};
+
+/* PCI/EISA Interrupt Acknowledge Cycle. */
+
+static uint64_t iack_read(void *opaque, target_phys_addr_t addr, unsigned size)
+{
+ return pic_read_irq(isa_pic);
+}
+
+static void special_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val, unsigned size)
+{
+ qemu_log("pci: special write cycle");
+}
+
+const MemoryRegionOps alpha_pci_iack_ops = {
+ .read = iack_read,
+ .write = special_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+void alpha_pci_vga_setup(PCIBus *pci_bus)
+{
+ switch (vga_interface_type) {
+#ifdef CONFIG_SPICE
+ case VGA_QXL:
+ pci_create_simple(pci_bus, -1, "qxl-vga");
+ return;
+#endif
+ case VGA_CIRRUS:
+ pci_cirrus_vga_init(pci_bus);
+ return;
+ case VGA_VMWARE:
+ if (pci_vmsvga_init(pci_bus)) {
+ return;
+ }
+ break;
+ }
+ /* If VGA is enabled at all, and one of the above didn't work, then
+ fallback to Standard VGA. */
+ if (vga_interface_type != VGA_NONE) {
+ pci_vga_init(pci_bus);
+ }
+}
diff --git a/hw/alpha_sys.h b/hw/alpha_sys.h
new file mode 100644
index 0000000000..13f017733b
--- /dev/null
+++ b/hw/alpha_sys.h
@@ -0,0 +1,24 @@
+/* Alpha cores and system support chips. */
+
+#ifndef HW_ALPHA_H
+#define HW_ALPHA_H 1
+
+#include "pci.h"
+#include "pci_host.h"
+#include "ide.h"
+#include "net.h"
+#include "pc.h"
+#include "usb-ohci.h"
+#include "irq.h"
+
+
+PCIBus *typhoon_init(ram_addr_t, qemu_irq *, CPUState *[4], pci_map_irq_fn);
+
+/* alpha_pci.c. */
+extern const MemoryRegionOps alpha_pci_bw_io_ops;
+extern const MemoryRegionOps alpha_pci_conf1_ops;
+extern const MemoryRegionOps alpha_pci_iack_ops;
+
+void alpha_pci_vga_setup(PCIBus *pci_bus);
+
+#endif
diff --git a/hw/alpha_typhoon.c b/hw/alpha_typhoon.c
new file mode 100644
index 0000000000..c769fcc138
--- /dev/null
+++ b/hw/alpha_typhoon.c
@@ -0,0 +1,813 @@
+/*
+ * DEC 21272 (TSUNAMI/TYPHOON) chipset emulation.
+ *
+ * Written by Richard Henderson.
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
+ */
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "hw.h"
+#include "devices.h"
+#include "sysemu.h"
+#include "alpha_sys.h"
+#include "exec-memory.h"
+
+
+typedef struct TyphoonCchip {
+ MemoryRegion region;
+ uint64_t misc;
+ uint64_t drir;
+ uint64_t dim[4];
+ uint32_t iic[4];
+ CPUState *cpu[4];
+} TyphoonCchip;
+
+typedef struct TyphoonWindow {
+ uint32_t base_addr;
+ uint32_t mask;
+ uint32_t translated_base_pfn;
+} TyphoonWindow;
+
+typedef struct TyphoonPchip {
+ MemoryRegion region;
+ MemoryRegion reg_iack;
+ MemoryRegion reg_mem;
+ MemoryRegion reg_io;
+ MemoryRegion reg_conf;
+ uint64_t ctl;
+ TyphoonWindow win[4];
+} TyphoonPchip;
+
+typedef struct TyphoonState {
+ PCIHostState host;
+ TyphoonCchip cchip;
+ TyphoonPchip pchip;
+ MemoryRegion dchip_region;
+ MemoryRegion ram_region;
+
+ /* QEMU emulation state. */
+ uint32_t latch_tmp;
+} TyphoonState;
+
+/* Called when one of DRIR or DIM changes. */
+static void cpu_irq_change(CPUState *env, uint64_t req)
+{
+ /* If there are any non-masked interrupts, tell the cpu. */
+ if (env) {
+ if (req) {
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ } else {
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+ }
+ }
+}
+
+static uint64_t cchip_read(void *opaque, target_phys_addr_t addr, unsigned size)
+{
+ CPUState *env = cpu_single_env;
+ TyphoonState *s = opaque;
+ uint64_t ret = 0;
+
+ if (addr & 4) {
+ return s->latch_tmp;
+ }
+
+ switch (addr) {
+ case 0x0000:
+ /* CSC: Cchip System Configuration Register. */
+ /* All sorts of data here; probably the only thing relevant is
+ PIP<14> Pchip 1 Present = 0. */
+ break;
+
+ case 0x0040:
+ /* MTR: Memory Timing Register. */
+ /* All sorts of stuff related to real DRAM. */
+ break;
+
+ case 0x0080:
+ /* MISC: Miscellaneous Register. */
+ ret = s->cchip.misc | (env->cpu_index & 3);
+ break;
+
+ case 0x00c0:
+ /* MPD: Memory Presence Detect Register. */
+ break;
+
+ case 0x0100: /* AAR0 */
+ case 0x0140: /* AAR1 */
+ case 0x0180: /* AAR2 */
+ case 0x01c0: /* AAR3 */
+ /* AAR: Array Address Register. */
+ /* All sorts of information about DRAM. */
+ break;
+
+ case 0x0200:
+ /* DIM0: Device Interrupt Mask Register, CPU0. */
+ ret = s->cchip.dim[0];
+ break;
+ case 0x0240:
+ /* DIM1: Device Interrupt Mask Register, CPU1. */
+ ret = s->cchip.dim[1];
+ break;
+ case 0x0280:
+ /* DIR0: Device Interrupt Request Register, CPU0. */
+ ret = s->cchip.dim[0] & s->cchip.drir;
+ break;
+ case 0x02c0:
+ /* DIR1: Device Interrupt Request Register, CPU1. */
+ ret = s->cchip.dim[1] & s->cchip.drir;
+ break;
+ case 0x0300:
+ /* DRIR: Device Raw Interrupt Request Register. */
+ ret = s->cchip.drir;
+ break;
+
+ case 0x0340:
+ /* PRBEN: Probe Enable Register. */
+ break;
+
+ case 0x0380:
+ /* IIC0: Interval Ignore Count Register, CPU0. */
+ ret = s->cchip.iic[0];
+ break;
+ case 0x03c0:
+ /* IIC1: Interval Ignore Count Register, CPU1. */
+ ret = s->cchip.iic[1];
+ break;
+
+ case 0x0400: /* MPR0 */
+ case 0x0440: /* MPR1 */
+ case 0x0480: /* MPR2 */
+ case 0x04c0: /* MPR3 */
+ /* MPR: Memory Programming Register. */
+ break;
+
+ case 0x0580:
+ /* TTR: TIGbus Timing Register. */
+ /* All sorts of stuff related to interrupt delivery timings. */
+ break;
+ case 0x05c0:
+ /* TDR: TIGbug Device Timing Register. */
+ break;
+
+ case 0x0600:
+ /* DIM2: Device Interrupt Mask Register, CPU2. */
+ ret = s->cchip.dim[2];
+ break;
+ case 0x0640:
+ /* DIM3: Device Interrupt Mask Register, CPU3. */
+ ret = s->cchip.dim[3];
+ break;
+ case 0x0680:
+ /* DIR2: Device Interrupt Request Register, CPU2. */
+ ret = s->cchip.dim[2] & s->cchip.drir;
+ break;
+ case 0x06c0:
+ /* DIR3: Device Interrupt Request Register, CPU3. */
+ ret = s->cchip.dim[3] & s->cchip.drir;
+ break;
+
+ case 0x0700:
+ /* IIC2: Interval Ignore Count Register, CPU2. */
+ ret = s->cchip.iic[2];
+ break;
+ case 0x0740:
+ /* IIC3: Interval Ignore Count Register, CPU3. */
+ ret = s->cchip.iic[3];
+ break;
+
+ case 0x0780:
+ /* PWR: Power Management Control. */
+ break;
+
+ case 0x0c00: /* CMONCTLA */
+ case 0x0c40: /* CMONCTLB */
+ case 0x0c80: /* CMONCNT01 */
+ case 0x0cc0: /* CMONCNT23 */
+ break;
+
+ default:
+ cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size);
+ return -1;
+ }
+
+ s->latch_tmp = ret >> 32;
+ return ret;
+}
+
+static uint64_t dchip_read(void *opaque, target_phys_addr_t addr, unsigned size)
+{
+ /* Skip this. It's all related to DRAM timing and setup. */
+ return 0;
+}
+
+static uint64_t pchip_read(void *opaque, target_phys_addr_t addr, unsigned size)
+{
+ TyphoonState *s = opaque;
+ uint64_t ret = 0;
+
+ if (addr & 4) {
+ return s->latch_tmp;
+ }
+
+ switch (addr) {
+ case 0x0000:
+ /* WSBA0: Window Space Base Address Register. */
+ ret = s->pchip.win[0].base_addr;
+ break;
+ case 0x0040:
+ /* WSBA1 */
+ ret = s->pchip.win[1].base_addr;
+ break;
+ case 0x0080:
+ /* WSBA2 */
+ ret = s->pchip.win[2].base_addr;
+ break;
+ case 0x00c0:
+ /* WSBA3 */
+ ret = s->pchip.win[3].base_addr;
+ break;
+
+ case 0x0100:
+ /* WSM0: Window Space Mask Register. */
+ ret = s->pchip.win[0].mask;
+ break;
+ case 0x0140:
+ /* WSM1 */
+ ret = s->pchip.win[1].mask;
+ break;
+ case 0x0180:
+ /* WSM2 */
+ ret = s->pchip.win[2].mask;
+ break;
+ case 0x01c0:
+ /* WSM3 */
+ ret = s->pchip.win[3].mask;
+ break;
+
+ case 0x0200:
+ /* TBA0: Translated Base Address Register. */
+ ret = (uint64_t)s->pchip.win[0].translated_base_pfn << 10;
+ break;
+ case 0x0240:
+ /* TBA1 */
+ ret = (uint64_t)s->pchip.win[1].translated_base_pfn << 10;
+ break;
+ case 0x0280:
+ /* TBA2 */
+ ret = (uint64_t)s->pchip.win[2].translated_base_pfn << 10;
+ break;
+ case 0x02c0:
+ /* TBA3 */
+ ret = (uint64_t)s->pchip.win[3].translated_base_pfn << 10;
+ break;
+
+ case 0x0300:
+ /* PCTL: Pchip Control Register. */
+ ret = s->pchip.ctl;
+ break;
+ case 0x0340:
+ /* PLAT: Pchip Master Latency Register. */
+ break;
+ case 0x03c0:
+ /* PERROR: Pchip Error Register. */
+ break;
+ case 0x0400:
+ /* PERRMASK: Pchip Error Mask Register. */
+ break;
+ case 0x0440:
+ /* PERRSET: Pchip Error Set Register. */
+ break;
+ case 0x0480:
+ /* TLBIV: Translation Buffer Invalidate Virtual Register (WO). */
+ break;
+ case 0x04c0:
+ /* TLBIA: Translation Buffer Invalidate All Register (WO). */
+ break;
+ case 0x0500: /* PMONCTL */
+ case 0x0540: /* PMONCNT */
+ case 0x0800: /* SPRST */
+ break;
+
+ default:
+ cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size);
+ return -1;
+ }
+
+ s->latch_tmp = ret >> 32;
+ return ret;
+}
+
+static void cchip_write(void *opaque, target_phys_addr_t addr,
+ uint64_t v32, unsigned size)
+{
+ TyphoonState *s = opaque;
+ uint64_t val, oldval, newval;
+
+ if (addr & 4) {
+ val = v32 << 32 | s->latch_tmp;
+ addr ^= 4;
+ } else {
+ s->latch_tmp = v32;
+ return;
+ }
+
+ switch (addr) {
+ case 0x0000:
+ /* CSC: Cchip System Configuration Register. */
+ /* All sorts of data here; nothing relevant RW. */
+ break;
+
+ case 0x0040:
+ /* MTR: Memory Timing Register. */
+ /* All sorts of stuff related to real DRAM. */
+ break;
+
+ case 0x0080:
+ /* MISC: Miscellaneous Register. */
+ newval = oldval = s->cchip.misc;
+ newval &= ~(val & 0x10000ff0); /* W1C fields */
+ if (val & 0x100000) {
+ newval &= ~0xff0000ull; /* ACL clears ABT and ABW */
+ } else {
+ newval |= val & 0x00f00000; /* ABT field is W1S */
+ if ((newval & 0xf0000) == 0) {
+ newval |= val & 0xf0000; /* ABW field is W1S iff zero */
+ }
+ }
+ newval |= (val & 0xf000) >> 4; /* IPREQ field sets IPINTR. */
+
+ newval &= ~0xf0000000000ull; /* WO and RW fields */
+ newval |= val & 0xf0000000000ull;
+ s->cchip.misc = newval;
+
+ /* Pass on changes to IPI and ITI state. */
+ if ((newval ^ oldval) & 0xff0) {
+ int i;
+ for (i = 0; i < 4; ++i) {
+ CPUState *env = s->cchip.cpu[i];
+ if (env) {
+ /* IPI can be either cleared or set by the write. */
+ if (newval & (1 << (i + 8))) {
+ cpu_interrupt(env, CPU_INTERRUPT_SMP);
+ } else {
+ cpu_reset_interrupt(env, CPU_INTERRUPT_SMP);
+ }
+
+ /* ITI can only be cleared by the write. */
+ if ((newval & (1 << (i + 4))) == 0) {
+ cpu_reset_interrupt(env, CPU_INTERRUPT_TIMER);
+ }
+ }
+ }
+ }
+ break;
+
+ case 0x00c0:
+ /* MPD: Memory Presence Detect Register. */
+ break;
+
+ case 0x0100: /* AAR0 */
+ case 0x0140: /* AAR1 */
+ case 0x0180: /* AAR2 */
+ case 0x01c0: /* AAR3 */
+ /* AAR: Array Address Register. */
+ /* All sorts of information about DRAM. */
+ break;
+
+ case 0x0200: /* DIM0 */
+ /* DIM: Device Interrupt Mask Register, CPU0. */
+ s->cchip.dim[0] = val;
+ cpu_irq_change(s->cchip.cpu[0], val & s->cchip.drir);
+ break;
+ case 0x0240: /* DIM1 */
+ /* DIM: Device Interrupt Mask Register, CPU1. */
+ s->cchip.dim[0] = val;
+ cpu_irq_change(s->cchip.cpu[1], val & s->cchip.drir);
+ break;
+
+ case 0x0280: /* DIR0 (RO) */
+ case 0x02c0: /* DIR1 (RO) */
+ case 0x0300: /* DRIR (RO) */
+ break;
+
+ case 0x0340:
+ /* PRBEN: Probe Enable Register. */
+ break;
+
+ case 0x0380: /* IIC0 */
+ s->cchip.iic[0] = val & 0xffffff;
+ break;
+ case 0x03c0: /* IIC1 */
+ s->cchip.iic[1] = val & 0xffffff;
+ break;
+
+ case 0x0400: /* MPR0 */
+ case 0x0440: /* MPR1 */
+ case 0x0480: /* MPR2 */
+ case 0x04c0: /* MPR3 */
+ /* MPR: Memory Programming Register. */
+ break;
+
+ case 0x0580:
+ /* TTR: TIGbus Timing Register. */
+ /* All sorts of stuff related to interrupt delivery timings. */
+ break;
+ case 0x05c0:
+ /* TDR: TIGbug Device Timing Register. */
+ break;
+
+ case 0x0600:
+ /* DIM2: Device Interrupt Mask Register, CPU2. */
+ s->cchip.dim[2] = val;
+ cpu_irq_change(s->cchip.cpu[2], val & s->cchip.drir);
+ break;
+ case 0x0640:
+ /* DIM3: Device Interrupt Mask Register, CPU3. */
+ s->cchip.dim[3] = val;
+ cpu_irq_change(s->cchip.cpu[3], val & s->cchip.drir);
+ break;
+
+ case 0x0680: /* DIR2 (RO) */
+ case 0x06c0: /* DIR3 (RO) */
+ break;
+
+ case 0x0700: /* IIC2 */
+ s->cchip.iic[2] = val & 0xffffff;
+ break;
+ case 0x0740: /* IIC3 */
+ s->cchip.iic[3] = val & 0xffffff;
+ break;
+
+ case 0x0780:
+ /* PWR: Power Management Control. */
+ break;
+
+ case 0x0c00: /* CMONCTLA */
+ case 0x0c40: /* CMONCTLB */
+ case 0x0c80: /* CMONCNT01 */
+ case 0x0cc0: /* CMONCNT23 */
+ break;
+
+ default:
+ cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size);
+ return;
+ }
+}
+
+static void dchip_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val, unsigned size)
+{
+ /* Skip this. It's all related to DRAM timing and setup. */
+}
+
+static void pchip_write(void *opaque, target_phys_addr_t addr,
+ uint64_t v32, unsigned size)
+{
+ TyphoonState *s = opaque;
+ uint64_t val, oldval;
+
+ if (addr & 4) {
+ val = v32 << 32 | s->latch_tmp;
+ addr ^= 4;
+ } else {
+ s->latch_tmp = v32;
+ return;
+ }
+
+ switch (addr) {
+ case 0x0000:
+ /* WSBA0: Window Space Base Address Register. */
+ s->pchip.win[0].base_addr = val;
+ break;
+ case 0x0040:
+ /* WSBA1 */
+ s->pchip.win[1].base_addr = val;
+ break;
+ case 0x0080:
+ /* WSBA2 */
+ s->pchip.win[2].base_addr = val;
+ break;
+ case 0x00c0:
+ /* WSBA3 */
+ s->pchip.win[3].base_addr = val;
+ break;
+
+ case 0x0100:
+ /* WSM0: Window Space Mask Register. */
+ s->pchip.win[0].mask = val;
+ break;
+ case 0x0140:
+ /* WSM1 */
+ s->pchip.win[1].mask = val;
+ break;
+ case 0x0180:
+ /* WSM2 */
+ s->pchip.win[2].mask = val;
+ break;
+ case 0x01c0:
+ /* WSM3 */
+ s->pchip.win[3].mask = val;
+ break;
+
+ case 0x0200:
+ /* TBA0: Translated Base Address Register. */
+ s->pchip.win[0].translated_base_pfn = val >> 10;
+ break;
+ case 0x0240:
+ /* TBA1 */
+ s->pchip.win[1].translated_base_pfn = val >> 10;
+ break;
+ case 0x0280:
+ /* TBA2 */
+ s->pchip.win[2].translated_base_pfn = val >> 10;
+ break;
+ case 0x02c0:
+ /* TBA3 */
+ s->pchip.win[3].translated_base_pfn = val >> 10;
+ break;
+
+ case 0x0300:
+ /* PCTL: Pchip Control Register. */
+ oldval = s->pchip.ctl;
+ oldval &= ~0x00001cff0fc7ffull; /* RW fields */
+ oldval |= val & 0x00001cff0fc7ffull;
+
+ s->pchip.ctl = oldval;
+ break;
+
+ case 0x0340:
+ /* PLAT: Pchip Master Latency Register. */
+ break;
+ case 0x03c0:
+ /* PERROR: Pchip Error Register. */
+ break;
+ case 0x0400:
+ /* PERRMASK: Pchip Error Mask Register. */
+ break;
+ case 0x0440:
+ /* PERRSET: Pchip Error Set Register. */
+ break;
+
+ case 0x0480:
+ /* TLBIV: Translation Buffer Invalidate Virtual Register. */
+ break;
+
+ case 0x04c0:
+ /* TLBIA: Translation Buffer Invalidate All Register (WO). */
+ break;
+
+ case 0x0500:
+ /* PMONCTL */
+ case 0x0540:
+ /* PMONCNT */
+ case 0x0800:
+ /* SPRST */
+ break;
+
+ default:
+ cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size);
+ return;
+ }
+}
+
+static const MemoryRegionOps cchip_ops = {
+ .read = cchip_read,
+ .write = cchip_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4, /* ??? Should be 8. */
+ .max_access_size = 8,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static const MemoryRegionOps dchip_ops = {
+ .read = dchip_read,
+ .write = dchip_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4, /* ??? Should be 8. */
+ .max_access_size = 8,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+};
+
+static const MemoryRegionOps pchip_ops = {
+ .read = pchip_read,
+ .write = pchip_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4, /* ??? Should be 8. */
+ .max_access_size = 8,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void typhoon_set_irq(void *opaque, int irq, int level)
+{
+ TyphoonState *s = opaque;
+ uint64_t drir;
+ int i;
+
+ /* Set/Reset the bit in CCHIP.DRIR based on IRQ+LEVEL. */
+ drir = s->cchip.drir;
+ if (level) {
+ drir |= 1ull << irq;
+ } else {
+ drir &= ~(1ull << irq);
+ }
+ s->cchip.drir = drir;
+
+ for (i = 0; i < 4; ++i) {
+ cpu_irq_change(s->cchip.cpu[i], s->cchip.dim[i] & drir);
+ }
+}
+
+static void typhoon_set_isa_irq(void *opaque, int irq, int level)
+{
+ typhoon_set_irq(opaque, 55, level);
+}
+
+static void typhoon_set_timer_irq(void *opaque, int irq, int level)
+{
+ TyphoonState *s = opaque;
+ int i;
+
+ /* Thankfully, the mc146818rtc code doesn't track the IRQ state,
+ and so we don't have to worry about missing interrupts just
+ because we never actually ACK the interrupt. Just ignore any
+ case of the interrupt level going low. */
+ if (level == 0) {
+ return;
+ }
+
+ /* Deliver the interrupt to each CPU, considering each CPU's IIC. */
+ for (i = 0; i < 4; ++i) {
+ CPUState *env = s->cchip.cpu[i];
+ if (env) {
+ uint32_t iic = s->cchip.iic[i];
+
+ /* ??? The verbage in Section 10.2.2.10 isn't 100% clear.
+ Bit 24 is the OverFlow bit, RO, and set when the count
+ decrements past 0. When is OF cleared? My guess is that
+ OF is actually cleared when the IIC is written, and that
+ the ICNT field always decrements. At least, that's an
+ interpretation that makes sense, and "allows the CPU to
+ determine exactly how mant interval timer ticks were
+ skipped". At least within the next 4M ticks... */
+
+ iic = ((iic - 1) & 0x1ffffff) | (iic & 0x1000000);
+ s->cchip.iic[i] = iic;
+
+ if (iic & 0x1000000) {
+ /* Set the ITI bit for this cpu. */
+ s->cchip.misc |= 1 << (i + 4);
+ /* And signal the interrupt. */
+ cpu_interrupt(env, CPU_INTERRUPT_TIMER);
+ }
+ }
+ }
+}
+
+static void typhoon_alarm_timer(void *opaque)
+{
+ TyphoonState *s = (TyphoonState *)((uintptr_t)opaque & ~3);
+ int cpu = (uintptr_t)opaque & 3;
+
+ /* Set the ITI bit for this cpu. */
+ s->cchip.misc |= 1 << (cpu + 4);
+ cpu_interrupt(s->cchip.cpu[cpu], CPU_INTERRUPT_TIMER);
+}
+
+PCIBus *typhoon_init(ram_addr_t ram_size, qemu_irq *p_rtc_irq,
+ CPUState *cpus[4], pci_map_irq_fn sys_map_irq)
+{
+ const uint64_t MB = 1024 * 1024;
+ const uint64_t GB = 1024 * MB;
+ MemoryRegion *addr_space = get_system_memory();
+ MemoryRegion *addr_space_io = get_system_io();
+ DeviceState *dev;
+ PCIHostState *p;
+ TyphoonState *s;
+ PCIBus *b;
+ int i;
+
+ dev = qdev_create(NULL, "typhoon-pcihost");
+ qdev_init_nofail(dev);
+
+ p = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev));
+ s = container_of(p, TyphoonState, host);
+
+ /* Remember the CPUs so that we can deliver interrupts to them. */
+ for (i = 0; i < 4; i++) {
+ CPUState *env = cpus[i];
+ s->cchip.cpu[i] = env;
+ if (env) {
+ env->alarm_timer = qemu_new_timer_ns(rtc_clock,
+ typhoon_alarm_timer,
+ (void *)((uintptr_t)s + i));
+ }
+ }
+
+ *p_rtc_irq = *qemu_allocate_irqs(typhoon_set_timer_irq, s, 1);
+
+ /* Main memory region, 0x00.0000.0000. Real hardware supports 32GB,
+ but the address space hole reserved at this point is 8TB. */
+ memory_region_init_ram(&s->ram_region, NULL, "ram", ram_size);
+ memory_region_add_subregion(addr_space, 0, &s->ram_region);
+
+ /* TIGbus, 0x801.0000.0000, 1GB. */
+ /* ??? The TIGbus is used for delivering interrupts, and access to
+ the flash ROM. I'm not sure that we need to implement it at all. */
+
+ /* Pchip0 CSRs, 0x801.8000.0000, 256MB. */
+ memory_region_init_io(&s->pchip.region, &pchip_ops, s, "pchip0", 256*MB);
+ memory_region_add_subregion(addr_space, 0x80180000000, &s->pchip.region);
+
+ /* Cchip CSRs, 0x801.A000.0000, 256MB. */
+ memory_region_init_io(&s->cchip.region, &cchip_ops, s, "cchip0", 256*MB);
+ memory_region_add_subregion(addr_space, 0x801a0000000, &s->cchip.region);
+
+ /* Dchip CSRs, 0x801.B000.0000, 256MB. */
+ memory_region_init_io(&s->dchip_region, &dchip_ops, s, "dchip0", 256*MB);
+ memory_region_add_subregion(addr_space, 0x801b0000000, &s->dchip_region);
+
+ /* Pchip0 PCI memory, 0x800.0000.0000, 4GB. */
+ memory_region_init(&s->pchip.reg_mem, "pci0-mem", 4*GB);
+ memory_region_add_subregion(addr_space, 0x80000000000, &s->pchip.reg_mem);
+
+ /* Pchip0 PCI I/O, 0x801.FC00.0000, 32MB. */
+ /* ??? Ideally we drop the "system" i/o space on the floor and give the
+ PCI subsystem the full address space reserved by the chipset.
+ We can't do that until the MEM and IO paths in memory.c are unified. */
+ memory_region_init_io(&s->pchip.reg_io, &alpha_pci_bw_io_ops, NULL,
+ "pci0-io", 32*MB);
+ memory_region_add_subregion(addr_space, 0x801fc000000, &s->pchip.reg_io);
+
+ b = pci_register_bus(&s->host.busdev.qdev, "pci",
+ typhoon_set_irq, sys_map_irq, s,
+ &s->pchip.reg_mem, addr_space_io, 0, 64);
+ s->host.bus = b;
+
+ /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */
+ memory_region_init_io(&s->pchip.reg_iack, &alpha_pci_iack_ops, b,
+ "pci0-iack", 64*MB);
+ memory_region_add_subregion(addr_space, 0x801f8000000, &s->pchip.reg_iack);
+
+ /* Pchip0 PCI configuration, 0x801.FE00.0000, 16MB. */
+ memory_region_init_io(&s->pchip.reg_conf, &alpha_pci_conf1_ops, b,
+ "pci0-conf", 16*MB);
+ memory_region_add_subregion(addr_space, 0x801fe000000, &s->pchip.reg_conf);
+
+ /* For the record, these are the mappings for the second PCI bus.
+ We can get away with not implementing them because we indicate
+ via the Cchip.CSC<PIP> bit that Pchip1 is not present. */
+ /* Pchip1 PCI memory, 0x802.0000.0000, 4GB. */
+ /* Pchip1 CSRs, 0x802.8000.0000, 256MB. */
+ /* Pchip1 PCI special/interrupt acknowledge, 0x802.F800.0000, 64MB. */
+ /* Pchip1 PCI I/O, 0x802.FC00.0000, 32MB. */
+ /* Pchip1 PCI configuration, 0x802.FE00.0000, 16MB. */
+
+ /* Init the ISA bus. */
+ /* ??? Technically there should be a cy82c693ub pci-isa bridge. */
+ {
+ qemu_irq isa_pci_irq, *isa_irqs;
+
+ isa_bus_new(NULL, addr_space_io);
+ isa_pci_irq = *qemu_allocate_irqs(typhoon_set_isa_irq, s, 1);
+ isa_irqs = i8259_init(isa_pci_irq);
+ isa_bus_irqs(isa_irqs);
+ }
+
+ return b;
+}
+
+static int typhoon_pcihost_init(SysBusDevice *dev)
+{
+ return 0;
+}
+
+static SysBusDeviceInfo typhoon_pcihost_info = {
+ .init = typhoon_pcihost_init,
+ .qdev.name = "typhoon-pcihost",
+ .qdev.size = sizeof(TyphoonState),
+ .qdev.no_user = 1
+};
+
+static void typhoon_register(void)
+{
+ sysbus_register_withprop(&typhoon_pcihost_info);
+}
+device_init(typhoon_register);
diff --git a/pc-bios/README b/pc-bios/README
index 02651fe224..4d1d816fdb 100644
--- a/pc-bios/README
+++ b/pc-bios/README
@@ -32,3 +32,6 @@
- The S390 zipl loader is an addition to the official IBM s390-tools
package. That fork is maintained in its own git repository at:
git://repo.or.cz/s390-tools.git
+
+- The sources for the Alpha palcode image is available from:
+ git://repo.or.cz/qemu-palcode.git
diff --git a/pc-bios/palcode-clipper b/pc-bios/palcode-clipper
new file mode 100755
index 0000000000..a92372c107
--- /dev/null
+++ b/pc-bios/palcode-clipper
Binary files differ
diff --git a/roms/qemu-palcode b/roms/qemu-palcode
new file mode 160000
+Subproject 7abb12f60eb3069019e9497e193733d77d8f072
diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h
index c2e7bb31ef..9d61d45ab6 100644
--- a/target-alpha/cpu.h
+++ b/target-alpha/cpu.h
@@ -265,6 +265,10 @@ struct CPUAlphaState {
uint64_t scratch[24];
#endif
+ /* This alarm doesn't exist in real hardware; we wish it did. */
+ struct QEMUTimer *alarm_timer;
+ uint64_t alarm_expire;
+
#if TARGET_LONG_BITS > HOST_LONG_BITS
/* temporary fixed-point registers
* used to emulate 64 bits target on 32 bits hosts
diff --git a/target-alpha/helper.h b/target-alpha/helper.h
index 2dec57e44b..b693ceea97 100644
--- a/target-alpha/helper.h
+++ b/target-alpha/helper.h
@@ -113,6 +113,11 @@ DEF_HELPER_2(stq_c_phys, i64, i64, i64)
DEF_HELPER_FLAGS_0(tbia, TCG_CALL_CONST, void)
DEF_HELPER_FLAGS_1(tbis, TCG_CALL_CONST, void, i64)
+
+DEF_HELPER_1(halt, void, i64);
+
+DEF_HELPER_FLAGS_0(get_time, TCG_CALL_CONST, i64)
+DEF_HELPER_FLAGS_1(set_alarm, TCG_CALL_CONST, void, i64)
#endif
#include "def-helper.h"
diff --git a/target-alpha/op_helper.c b/target-alpha/op_helper.c
index d8945dcbca..cc102dbd63 100644
--- a/target-alpha/op_helper.c
+++ b/target-alpha/op_helper.c
@@ -22,6 +22,7 @@
#include "host-utils.h"
#include "softfloat.h"
#include "helper.h"
+#include "sysemu.h"
#include "qemu-timer.h"
#define FP_STATUS (env->fp_status)
@@ -1218,6 +1219,30 @@ void helper_tbis(uint64_t p)
{
tlb_flush_page(env, p);
}
+
+void helper_halt(uint64_t restart)
+{
+ if (restart) {
+ qemu_system_reset_request();
+ } else {
+ qemu_system_shutdown_request();
+ }
+}
+
+uint64_t helper_get_time(void)
+{
+ return qemu_get_clock_ns(rtc_clock);
+}
+
+void helper_set_alarm(uint64_t expire)
+{
+ if (expire) {
+ env->alarm_expire = expire;
+ qemu_mod_timer(env->alarm_timer, expire);
+ } else {
+ qemu_del_timer(env->alarm_timer);
+ }
+}
#endif
/*****************************************************************************/
diff --git a/target-alpha/translate.c b/target-alpha/translate.c
index 1e224a2152..a961159d5d 100644
--- a/target-alpha/translate.c
+++ b/target-alpha/translate.c
@@ -1590,18 +1590,34 @@ static int cpu_pr_data(int pr)
return offsetof(CPUAlphaState, shadow[pr - 32]);
case 40 ... 63:
return offsetof(CPUAlphaState, scratch[pr - 40]);
+
+ case 251:
+ return offsetof(CPUAlphaState, alarm_expire);
}
return 0;
}
-static void gen_mfpr(int ra, int regno)
+static ExitStatus gen_mfpr(int ra, int regno)
{
int data = cpu_pr_data(regno);
/* In our emulated PALcode, these processor registers have no
side effects from reading. */
if (ra == 31) {
- return;
+ return NO_EXIT;
+ }
+
+ if (regno == 250) {
+ /* WALL_TIME */
+ if (use_icount) {
+ gen_io_start();
+ gen_helper_get_time(cpu_ir[ra]);
+ gen_io_end();
+ return EXIT_PC_STALE;
+ } else {
+ gen_helper_get_time(cpu_ir[ra]);
+ return NO_EXIT;
+ }
}
/* The basic registers are data only, and unknown registers
@@ -1615,11 +1631,13 @@ static void gen_mfpr(int ra, int regno)
} else {
tcg_gen_ld_i64(cpu_ir[ra], cpu_env, data);
}
+ return NO_EXIT;
}
-static void gen_mtpr(int rb, int regno)
+static ExitStatus gen_mtpr(DisasContext *ctx, int rb, int regno)
{
TCGv tmp;
+ int data;
if (rb == 31) {
tmp = tcg_const_i64(0);
@@ -1627,19 +1645,37 @@ static void gen_mtpr(int rb, int regno)
tmp = cpu_ir[rb];
}
- /* These two register numbers perform a TLB cache flush. Thankfully we
- can only do this inside PALmode, which means that the current basic
- block cannot be affected by the change in mappings. */
- if (regno == 255) {
+ switch (regno) {
+ case 255:
/* TBIA */
gen_helper_tbia();
- } else if (regno == 254) {
+ break;
+
+ case 254:
/* TBIS */
gen_helper_tbis(tmp);
- } else {
+ break;
+
+ case 253:
+ /* WAIT */
+ tmp = tcg_const_i64(1);
+ tcg_gen_st32_i64(tmp, cpu_env, offsetof(CPUState, halted));
+ return gen_excp(ctx, EXCP_HLT, 0);
+
+ case 252:
+ /* HALT */
+ gen_helper_halt(tmp);
+ return EXIT_PC_STALE;
+
+ case 251:
+ /* ALARM */
+ gen_helper_set_alarm(tmp);
+ break;
+
+ default:
/* The basic registers are data only, and unknown registers
are read-zero, write-ignore. */
- int data = cpu_pr_data(regno);
+ data = cpu_pr_data(regno);
if (data != 0) {
if (data & PR_BYTE) {
tcg_gen_st8_i64(tmp, cpu_env, data & ~PR_BYTE);
@@ -1649,11 +1685,14 @@ static void gen_mtpr(int rb, int regno)
tcg_gen_st_i64(tmp, cpu_env, data);
}
}
+ break;
}
if (rb == 31) {
tcg_temp_free(tmp);
}
+
+ return NO_EXIT;
}
#endif /* !USER_ONLY*/
@@ -2721,8 +2760,16 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
break;
case 0xC000:
/* RPCC */
- if (ra != 31)
- gen_helper_load_pcc(cpu_ir[ra]);
+ if (ra != 31) {
+ if (use_icount) {
+ gen_io_start();
+ gen_helper_load_pcc(cpu_ir[ra]);
+ gen_io_end();
+ ret = EXIT_PC_STALE;
+ } else {
+ gen_helper_load_pcc(cpu_ir[ra]);
+ }
+ }
break;
case 0xE000:
/* RC */
@@ -2747,8 +2794,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
/* HW_MFPR (PALcode) */
#ifndef CONFIG_USER_ONLY
if (ctx->tb->flags & TB_FLAGS_PAL_MODE) {
- gen_mfpr(ra, insn & 0xffff);
- break;
+ return gen_mfpr(ra, insn & 0xffff);
}
#endif
goto invalid_opc;
@@ -3053,8 +3099,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
/* HW_MTPR (PALcode) */
#ifndef CONFIG_USER_ONLY
if (ctx->tb->flags & TB_FLAGS_PAL_MODE) {
- gen_mtpr(rb, insn & 0xffff);
- break;
+ return gen_mtpr(ctx, rb, insn & 0xffff);
}
#endif
goto invalid_opc;