summaryrefslogtreecommitdiffstats
path: root/hw
diff options
context:
space:
mode:
authorAvi Kivity <avi@redhat.com>2011-10-09 13:11:50 +0200
committerAvi Kivity <avi@redhat.com>2011-10-09 13:11:50 +0200
commitdf2921d326d0903ff684759ac8508f87396b7018 (patch)
treeb9f9d2a2a116413591362278b2dc9d148c7ee943 /hw
parent306f66b42f961e38442d1721523dbb4906b33afb (diff)
parent02d6516c8ba00bdd6d96b622f000cb28c3449f43 (diff)
downloadhqemu-df2921d326d0903ff684759ac8508f87396b7018.zip
hqemu-df2921d326d0903ff684759ac8508f87396b7018.tar.gz
Merge remote-tracking branch 'upstream' into memory/batch
* upstream: (87 commits) target-alpha: Fix compilation errors for 32 bit hosts 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. tcg/s390: Remove unused tcg_out_addi() tcg/ia64: Remove unused tcg_out_addi() ARM: fix segfault ppc64: Fix linker script pseries: Implement set-time-of-day RTAS function pseries: Refactor spapr irq allocation PPC: Clean up BookE timer code PPC: booke timers KVM: PPC: Use HIOR setting for -M pseries with PR KVM KVM: Update kernel headers KVM: Update kernel headers PPC: Fix heathrow PIC to use little endian MMIO PPC: Fix via-cuda memory registration ... Conflicts: hw/milkymist-uart.c hw/ppce500_mpc8544ds.c Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'hw')
-rw-r--r--hw/adb.c2
-rw-r--r--hw/adb.h67
-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.c820
-rw-r--r--hw/cuda.c29
-rw-r--r--hw/etraxfs_dma.c10
-rw-r--r--hw/heathrow_pic.c2
-rw-r--r--hw/milkymist-hw.h5
-rw-r--r--hw/milkymist-uart.c73
-rw-r--r--hw/milkymist.c14
-rw-r--r--hw/openpic.c289
-rw-r--r--hw/ppc.c138
-rw-r--r--hw/ppc.h37
-rw-r--r--hw/ppc405_boards.c5
-rw-r--r--hw/ppc440_bamboo.c16
-rw-r--r--hw/ppc4xx_devs.c2
-rw-r--r--hw/ppc_booke.c254
-rw-r--r--hw/ppc_mac.h42
-rw-r--r--hw/ppc_newworld.c1
-rw-r--r--hw/ppc_oldworld.c1
-rw-r--r--hw/ppce500_mpc8544ds.c192
-rw-r--r--hw/ppce500_spin.c215
-rw-r--r--hw/spapr.c77
-rw-r--r--hw/spapr.h7
-rw-r--r--hw/spapr_hcall.c220
-rw-r--r--hw/spapr_llan.c11
-rw-r--r--hw/spapr_rtas.c24
-rw-r--r--hw/spapr_vio.c10
-rw-r--r--hw/spapr_vio.h17
-rw-r--r--hw/spapr_vscsi.c13
-rw-r--r--hw/spapr_vty.c10
-rw-r--r--hw/virtex_ml507.c11
-rw-r--r--hw/xics.c17
35 files changed, 2492 insertions, 474 deletions
diff --git a/hw/adb.c b/hw/adb.c
index 8dedbf8..aa15f55 100644
--- a/hw/adb.c
+++ b/hw/adb.c
@@ -22,7 +22,7 @@
* THE SOFTWARE.
*/
#include "hw.h"
-#include "ppc_mac.h"
+#include "adb.h"
#include "console.h"
/* debug ADB */
diff --git a/hw/adb.h b/hw/adb.h
new file mode 100644
index 0000000..b2a591c
--- /dev/null
+++ b/hw/adb.h
@@ -0,0 +1,67 @@
+/*
+ * QEMU ADB emulation shared definitions and prototypes
+ *
+ * Copyright (c) 2004-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * 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.
+ */
+
+#if !defined(__ADB_H__)
+#define __ADB_H__
+
+#define MAX_ADB_DEVICES 16
+
+#define ADB_MAX_OUT_LEN 16
+
+typedef struct ADBDevice ADBDevice;
+
+/* buf = NULL means polling */
+typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out,
+ const uint8_t *buf, int len);
+typedef int ADBDeviceReset(ADBDevice *d);
+
+struct ADBDevice {
+ struct ADBBusState *bus;
+ int devaddr;
+ int handler;
+ ADBDeviceRequest *devreq;
+ ADBDeviceReset *devreset;
+ void *opaque;
+};
+
+typedef struct ADBBusState {
+ ADBDevice devices[MAX_ADB_DEVICES];
+ int nb_devices;
+ int poll_index;
+} ADBBusState;
+
+int adb_request(ADBBusState *s, uint8_t *buf_out,
+ const uint8_t *buf, int len);
+int adb_poll(ADBBusState *s, uint8_t *buf_out);
+
+ADBDevice *adb_register_device(ADBBusState *s, int devaddr,
+ ADBDeviceRequest *devreq,
+ ADBDeviceReset *devreset,
+ void *opaque);
+void adb_kbd_init(ADBBusState *bus);
+void adb_mouse_init(ADBBusState *bus);
+
+extern ADBBusState adb_bus;
+#endif /* !defined(__ADB_H__) */
diff --git a/hw/alpha_dp264.c b/hw/alpha_dp264.c
new file mode 100644
index 0000000..fcc20e9
--- /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 + 0xfffffc0000000000ULL);
+ 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 0000000..e975702
--- /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 0000000..13f0177
--- /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 0000000..c7608bb
--- /dev/null
+++ b/hw/alpha_typhoon.c
@@ -0,0 +1,820 @@
+/*
+ * 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, 0x80180000000ULL,
+ &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, 0x801a0000000ULL,
+ &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, 0x801b0000000ULL,
+ &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, 0x80000000000ULL,
+ &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, 0x801fc000000ULL,
+ &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, 0x801f8000000ULL,
+ &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, 0x801fe000000ULL,
+ &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/hw/cuda.c b/hw/cuda.c
index 5c92d81..4077436 100644
--- a/hw/cuda.c
+++ b/hw/cuda.c
@@ -24,6 +24,7 @@
*/
#include "hw.h"
#include "ppc_mac.h"
+#include "adb.h"
#include "qemu-timer.h"
#include "sysemu.h"
@@ -633,16 +634,20 @@ static uint32_t cuda_readl (void *opaque, target_phys_addr_t addr)
return 0;
}
-static CPUWriteMemoryFunc * const cuda_write[] = {
- &cuda_writeb,
- &cuda_writew,
- &cuda_writel,
-};
-
-static CPUReadMemoryFunc * const cuda_read[] = {
- &cuda_readb,
- &cuda_readw,
- &cuda_readl,
+static MemoryRegionOps cuda_ops = {
+ .old_mmio = {
+ .write = {
+ cuda_writeb,
+ cuda_writew,
+ cuda_writel,
+ },
+ .read = {
+ cuda_readb,
+ cuda_readw,
+ cuda_readl,
+ },
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
};
static bool cuda_timer_exist(void *opaque, int version_id)
@@ -739,8 +744,8 @@ void cuda_init (MemoryRegion **cuda_mem, qemu_irq irq)
s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET;
s->adb_poll_timer = qemu_new_timer_ns(vm_clock, cuda_adb_poll, s);
- cpu_register_io_memory(cuda_read, cuda_write, s,
- DEVICE_NATIVE_ENDIAN);
+ memory_region_init_io(&s->mem, &cuda_ops, s, "cuda", 0x2000);
+
*cuda_mem = &s->mem;
vmstate_register(NULL, -1, &vmstate_cuda, s);
qemu_register_reset(cuda_reset, s);
diff --git a/hw/etraxfs_dma.c b/hw/etraxfs_dma.c
index 94bfb70..02d0183 100644
--- a/hw/etraxfs_dma.c
+++ b/hw/etraxfs_dma.c
@@ -604,12 +604,10 @@ dma_winvalid (void *opaque, target_phys_addr_t addr, uint32_t value)
static void
dma_update_state(struct fs_dma_ctrl *ctrl, int c)
{
- if ((ctrl->channels[c].regs[RW_CFG] & 1) != 3) {
- if (ctrl->channels[c].regs[RW_CFG] & 2)
- ctrl->channels[c].state = STOPPED;
- if (!(ctrl->channels[c].regs[RW_CFG] & 1))
- ctrl->channels[c].state = RST;
- }
+ if (ctrl->channels[c].regs[RW_CFG] & 2)
+ ctrl->channels[c].state = STOPPED;
+ if (!(ctrl->channels[c].regs[RW_CFG] & 1))
+ ctrl->channels[c].state = RST;
}
static void
diff --git a/hw/heathrow_pic.c b/hw/heathrow_pic.c
index 51996ab..16f48d1 100644
--- a/hw/heathrow_pic.c
+++ b/hw/heathrow_pic.c
@@ -126,7 +126,7 @@ static uint64_t pic_read(void *opaque, target_phys_addr_t addr,
static const MemoryRegionOps heathrow_pic_ops = {
.read = pic_read,
.write = pic_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
+ .endianness = DEVICE_LITTLE_ENDIAN,
};
static void heathrow_pic_set_irq(void *opaque, int num, int level)
diff --git a/hw/milkymist-hw.h b/hw/milkymist-hw.h
index 20de68e..9f358a7 100644
--- a/hw/milkymist-hw.h
+++ b/hw/milkymist-hw.h
@@ -5,15 +5,14 @@
#include "qdev-addr.h"
static inline DeviceState *milkymist_uart_create(target_phys_addr_t base,
- qemu_irq rx_irq, qemu_irq tx_irq)
+ qemu_irq irq)
{
DeviceState *dev;
dev = qdev_create(NULL, "milkymist-uart");
qdev_init_nofail(dev);
sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
- sysbus_connect_irq(sysbus_from_qdev(dev), 0, rx_irq);
- sysbus_connect_irq(sysbus_from_qdev(dev), 1, tx_irq);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
return dev;
}
diff --git a/hw/milkymist-uart.c b/hw/milkymist-uart.c
index 128cd8c..5404ca9 100644
--- a/hw/milkymist-uart.c
+++ b/hw/milkymist-uart.c
@@ -30,21 +30,54 @@
enum {
R_RXTX = 0,
R_DIV,
+ R_STAT,
+ R_CTRL,
+ R_DBG,
R_MAX
};
+enum {
+ STAT_THRE = (1<<0),
+ STAT_RX_EVT = (1<<1),
+ STAT_TX_EVT = (1<<2),
+};
+
+enum {
+ CTRL_RX_IRQ_EN = (1<<0),
+ CTRL_TX_IRQ_EN = (1<<1),
+ CTRL_THRU_EN = (1<<2),
+};
+
+enum {
+ DBG_BREAK_EN = (1<<0),
+};
+
struct MilkymistUartState {
SysBusDevice busdev;
MemoryRegion regs_region;
CharDriverState *chr;
-
- qemu_irq rx_irq;
- qemu_irq tx_irq;
+ qemu_irq irq;
uint32_t regs[R_MAX];
};
typedef struct MilkymistUartState MilkymistUartState;
+static void uart_update_irq(MilkymistUartState *s)
+{
+ int rx_event = s->regs[R_STAT] & STAT_RX_EVT;
+ int tx_event = s->regs[R_STAT] & STAT_TX_EVT;
+ int rx_irq_en = s->regs[R_CTRL] & CTRL_RX_IRQ_EN;
+ int tx_irq_en = s->regs[R_CTRL] & CTRL_TX_IRQ_EN;
+
+ if ((rx_irq_en && rx_event) || (tx_irq_en && tx_event)) {
+ trace_milkymist_uart_raise_irq();
+ qemu_irq_raise(s->irq);
+ } else {
+ trace_milkymist_uart_lower_irq();
+ qemu_irq_lower(s->irq);
+ }
+}
+
static uint64_t uart_read(void *opaque, target_phys_addr_t addr,
unsigned size)
{
@@ -54,7 +87,12 @@ static uint64_t uart_read(void *opaque, target_phys_addr_t addr,
addr >>= 2;
switch (addr) {
case R_RXTX:
+ r = s->regs[addr];
+ break;
case R_DIV:
+ case R_STAT:
+ case R_CTRL:
+ case R_DBG:
r = s->regs[addr];
break;
@@ -83,18 +121,26 @@ static void uart_write(void *opaque, target_phys_addr_t addr, uint64_t value,
if (s->chr) {
qemu_chr_fe_write(s->chr, &ch, 1);
}
- trace_milkymist_uart_pulse_irq_tx();
- qemu_irq_pulse(s->tx_irq);
+ s->regs[R_STAT] |= STAT_TX_EVT;
break;
case R_DIV:
+ case R_CTRL:
+ case R_DBG:
s->regs[addr] = value;
break;
+ case R_STAT:
+ /* write one to clear bits */
+ s->regs[addr] &= ~(value & (STAT_RX_EVT | STAT_TX_EVT));
+ break;
+
default:
error_report("milkymist_uart: write access to unknown register 0x"
TARGET_FMT_plx, addr << 2);
break;
}
+
+ uart_update_irq(s);
}
static const MemoryRegionOps uart_mmio_ops = {
@@ -111,14 +157,19 @@ static void uart_rx(void *opaque, const uint8_t *buf, int size)
{
MilkymistUartState *s = opaque;
+ assert(!(s->regs[R_STAT] & STAT_RX_EVT));
+
+ s->regs[R_STAT] |= STAT_RX_EVT;
s->regs[R_RXTX] = *buf;
- trace_milkymist_uart_pulse_irq_rx();
- qemu_irq_pulse(s->rx_irq);
+
+ uart_update_irq(s);
}
static int uart_can_rx(void *opaque)
{
- return 1;
+ MilkymistUartState *s = opaque;
+
+ return !(s->regs[R_STAT] & STAT_RX_EVT);
}
static void uart_event(void *opaque, int event)
@@ -133,14 +184,16 @@ static void milkymist_uart_reset(DeviceState *d)
for (i = 0; i < R_MAX; i++) {
s->regs[i] = 0;
}
+
+ /* THRE is always set */
+ s->regs[R_STAT] = STAT_THRE;
}
static int milkymist_uart_init(SysBusDevice *dev)
{
MilkymistUartState *s = FROM_SYSBUS(typeof(*s), dev);
- sysbus_init_irq(dev, &s->rx_irq);
- sysbus_init_irq(dev, &s->tx_irq);
+ sysbus_init_irq(dev, &s->irq);
memory_region_init_io(&s->regs_region, &uart_mmio_ops, s,
"milkymist-uart", R_MAX * 4);
diff --git a/hw/milkymist.c b/hw/milkymist.c
index bca0a58..b7a8c1c 100644
--- a/hw/milkymist.c
+++ b/hw/milkymist.c
@@ -146,17 +146,17 @@ milkymist_init(ram_addr_t ram_size_not_used,
exit(1);
}
- milkymist_uart_create(0x60000000, irq[0], irq[1]);
- milkymist_sysctl_create(0x60001000, irq[2], irq[3], irq[4],
+ milkymist_uart_create(0x60000000, irq[0]);
+ milkymist_sysctl_create(0x60001000, irq[1], irq[2], irq[3],
80000000, 0x10014d31, 0x0000041f, 0x00000001);
milkymist_hpdmc_create(0x60002000);
milkymist_vgafb_create(0x60003000, 0x40000000, 0x0fffffff);
milkymist_memcard_create(0x60004000);
- milkymist_ac97_create(0x60005000, irq[5], irq[6], irq[7], irq[8]);
- milkymist_pfpu_create(0x60006000, irq[9]);
- milkymist_tmu2_create(0x60007000, irq[10]);
- milkymist_minimac2_create(0x60008000, 0x30000000, irq[11], irq[12]);
- milkymist_softusb_create(0x6000f000, irq[17],
+ milkymist_ac97_create(0x60005000, irq[4], irq[5], irq[6], irq[7]);
+ milkymist_pfpu_create(0x60006000, irq[8]);
+ milkymist_tmu2_create(0x60007000, irq[9]);
+ milkymist_minimac2_create(0x60008000, 0x30000000, irq[10], irq[11]);
+ milkymist_softusb_create(0x6000f000, irq[15],
0x20000000, 0x1000, 0x20020000, 0x2000);
/* make sure juart isn't the first chardev */
diff --git a/hw/openpic.c b/hw/openpic.c
index 88e997e..22fc275 100644
--- a/hw/openpic.c
+++ b/hw/openpic.c
@@ -2,6 +2,7 @@
* OpenPIC emulation
*
* Copyright (c) 2004 Jocelyn Mayer
+ * 2011 Alexander Graf
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -56,13 +57,13 @@
#define MAX_MBX 4
#define MAX_TMR 4
#define VECTOR_BITS 8
-#define MAX_IPI 0
+#define MAX_IPI 4
#define VID (0x00000000)
#elif defined(USE_MPCxxx)
-#define MAX_CPU 2
+#define MAX_CPU 15
#define MAX_IRQ 128
#define MAX_DBL 0
#define MAX_MBX 0
@@ -127,14 +128,14 @@ enum {
#define MPIC_MSI_REG_START 0x11C00
#define MPIC_MSI_REG_SIZE 0x100
#define MPIC_CPU_REG_START 0x20000
-#define MPIC_CPU_REG_SIZE 0x100
+#define MPIC_CPU_REG_SIZE 0x100 + ((MAX_CPU - 1) * 0x1000)
enum mpic_ide_bits {
- IDR_EP = 0,
- IDR_CI0 = 1,
- IDR_CI1 = 2,
- IDR_P1 = 30,
- IDR_P0 = 31,
+ IDR_EP = 31,
+ IDR_CI0 = 30,
+ IDR_CI1 = 29,
+ IDR_P1 = 1,
+ IDR_P0 = 0,
};
#else
@@ -161,6 +162,16 @@ static inline int test_bit (uint32_t *field, int bit)
return (field[bit >> 5] & 1 << (bit & 0x1F)) != 0;
}
+static int get_current_cpu(void)
+{
+ return cpu_single_env->cpu_index;
+}
+
+static uint32_t openpic_cpu_read_internal(void *opaque, target_phys_addr_t addr,
+ int idx);
+static void openpic_cpu_write_internal(void *opaque, target_phys_addr_t addr,
+ uint32_t val, int idx);
+
enum {
IRQ_EXTERNAL = 0x01,
IRQ_INTERNAL = 0x02,
@@ -465,46 +476,35 @@ static void openpic_reset (void *opaque)
opp->glbc = 0x00000000;
}
-static inline uint32_t read_IRQreg (openpic_t *opp, int n_IRQ, uint32_t reg)
+static inline uint32_t read_IRQreg_ide(openpic_t *opp, int n_IRQ)
{
- uint32_t retval;
-
- switch (reg) {
- case IRQ_IPVP:
- retval = opp->src[n_IRQ].ipvp;
- break;
- case IRQ_IDE:
- retval = opp->src[n_IRQ].ide;
- break;
- }
+ return opp->src[n_IRQ].ide;
+}
- return retval;
+static inline uint32_t read_IRQreg_ipvp(openpic_t *opp, int n_IRQ)
+{
+ return opp->src[n_IRQ].ipvp;
}
-static inline void write_IRQreg (openpic_t *opp, int n_IRQ,
- uint32_t reg, uint32_t val)
+static inline void write_IRQreg_ide(openpic_t *opp, int n_IRQ, uint32_t val)
{
uint32_t tmp;
- switch (reg) {
- case IRQ_IPVP:
- /* NOTE: not fully accurate for special IRQs, but simple and
- sufficient */
- /* ACTIVITY bit is read-only */
- opp->src[n_IRQ].ipvp =
- (opp->src[n_IRQ].ipvp & 0x40000000) |
- (val & 0x800F00FF);
- openpic_update_irq(opp, n_IRQ);
- DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n",
- n_IRQ, val, opp->src[n_IRQ].ipvp);
- break;
- case IRQ_IDE:
- tmp = val & 0xC0000000;
- tmp |= val & ((1 << MAX_CPU) - 1);
- opp->src[n_IRQ].ide = tmp;
- DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide);
- break;
- }
+ tmp = val & 0xC0000000;
+ tmp |= val & ((1ULL << MAX_CPU) - 1);
+ opp->src[n_IRQ].ide = tmp;
+ DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide);
+}
+
+static inline void write_IRQreg_ipvp(openpic_t *opp, int n_IRQ, uint32_t val)
+{
+ /* NOTE: not fully accurate for special IRQs, but simple and sufficient */
+ /* ACTIVITY bit is read-only */
+ opp->src[n_IRQ].ipvp = (opp->src[n_IRQ].ipvp & 0x40000000)
+ | (val & 0x800F00FF);
+ openpic_update_irq(opp, n_IRQ);
+ DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n", n_IRQ, val,
+ opp->src[n_IRQ].ipvp);
}
#if 0 // Code provision for Intel model
@@ -516,10 +516,10 @@ static uint32_t read_doorbell_register (openpic_t *opp,
switch (offset) {
case DBL_IPVP_OFFSET:
- retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP);
+ retval = read_IRQreg_ipvp(opp, IRQ_DBL0 + n_dbl);
break;
case DBL_IDE_OFFSET:
- retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE);
+ retval = read_IRQreg_ide(opp, IRQ_DBL0 + n_dbl);
break;
case DBL_DMR_OFFSET:
retval = opp->doorbells[n_dbl].dmr;
@@ -534,10 +534,10 @@ static void write_doorbell_register (penpic_t *opp, int n_dbl,
{
switch (offset) {
case DBL_IVPR_OFFSET:
- write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP, value);
+ write_IRQreg_ipvp(opp, IRQ_DBL0 + n_dbl, value);
break;
case DBL_IDE_OFFSET:
- write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE, value);
+ write_IRQreg_ide(opp, IRQ_DBL0 + n_dbl, value);
break;
case DBL_DMR_OFFSET:
opp->doorbells[n_dbl].dmr = value;
@@ -557,10 +557,10 @@ static uint32_t read_mailbox_register (openpic_t *opp,
retval = opp->mailboxes[n_mbx].mbr;
break;
case MBX_IVPR_OFFSET:
- retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP);
+ retval = read_IRQreg_ipvp(opp, IRQ_MBX0 + n_mbx);
break;
case MBX_DMR_OFFSET:
- retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE);
+ retval = read_IRQreg_ide(opp, IRQ_MBX0 + n_mbx);
break;
}
@@ -575,10 +575,10 @@ static void write_mailbox_register (openpic_t *opp, int n_mbx,
opp->mailboxes[n_mbx].mbr = value;
break;
case MBX_IVPR_OFFSET:
- write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP, value);
+ write_IRQreg_ipvp(opp, IRQ_MBX0 + n_mbx, value);
break;
case MBX_DMR_OFFSET:
- write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE, value);
+ write_IRQreg_ide(opp, IRQ_MBX0 + n_mbx, value);
break;
}
}
@@ -594,18 +594,27 @@ static void openpic_gbl_write (void *opaque, target_phys_addr_t addr, uint32_t v
DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val);
if (addr & 0xF)
return;
- addr &= 0xFF;
switch (addr) {
- case 0x00: /* FREP */
+ case 0x40:
+ case 0x50:
+ case 0x60:
+ case 0x70:
+ case 0x80:
+ case 0x90:
+ case 0xA0:
+ case 0xB0:
+ openpic_cpu_write_internal(opp, addr, val, get_current_cpu());
break;
- case 0x20: /* GLBC */
+ case 0x1000: /* FREP */
+ break;
+ case 0x1020: /* GLBC */
if (val & 0x80000000 && opp->reset)
opp->reset(opp);
opp->glbc = val & ~0x80000000;
break;
- case 0x80: /* VENI */
+ case 0x1080: /* VENI */
break;
- case 0x90: /* PINT */
+ case 0x1090: /* PINT */
for (idx = 0; idx < opp->nb_cpus; idx++) {
if ((val & (1 << idx)) && !(opp->pint & (1 << idx))) {
DPRINTF("Raise OpenPIC RESET output for CPU %d\n", idx);
@@ -619,22 +628,20 @@ static void openpic_gbl_write (void *opaque, target_phys_addr_t addr, uint32_t v
}
opp->pint = val;
break;
-#if MAX_IPI > 0
- case 0xA0: /* IPI_IPVP */
- case 0xB0:
- case 0xC0:
- case 0xD0:
+ case 0x10A0: /* IPI_IPVP */
+ case 0x10B0:
+ case 0x10C0:
+ case 0x10D0:
{
int idx;
- idx = (addr - 0xA0) >> 4;
- write_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IPVP, val);
+ idx = (addr - 0x10A0) >> 4;
+ write_IRQreg_ipvp(opp, opp->irq_ipi0 + idx, val);
}
break;
-#endif
- case 0xE0: /* SPVE */
+ case 0x10E0: /* SPVE */
opp->spve = val & 0x000000FF;
break;
- case 0xF0: /* TIFR */
+ case 0x10F0: /* TIFR */
opp->tifr = val;
break;
default:
@@ -651,36 +658,43 @@ static uint32_t openpic_gbl_read (void *opaque, target_phys_addr_t addr)
retval = 0xFFFFFFFF;
if (addr & 0xF)
return retval;
- addr &= 0xFF;
switch (addr) {
- case 0x00: /* FREP */
+ case 0x1000: /* FREP */
retval = opp->frep;
break;
- case 0x20: /* GLBC */
+ case 0x1020: /* GLBC */
retval = opp->glbc;
break;
- case 0x80: /* VENI */
+ case 0x1080: /* VENI */
retval = opp->veni;
break;
- case 0x90: /* PINT */
+ case 0x1090: /* PINT */
retval = 0x00000000;
break;
-#if MAX_IPI > 0
- case 0xA0: /* IPI_IPVP */
+ case 0x40:
+ case 0x50:
+ case 0x60:
+ case 0x70:
+ case 0x80:
+ case 0x90:
+ case 0xA0:
case 0xB0:
- case 0xC0:
- case 0xD0:
+ retval = openpic_cpu_read_internal(opp, addr, get_current_cpu());
+ break;
+ case 0x10A0: /* IPI_IPVP */
+ case 0x10B0:
+ case 0x10C0:
+ case 0x10D0:
{
int idx;
- idx = (addr - 0xA0) >> 4;
- retval = read_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IPVP);
+ idx = (addr - 0x10A0) >> 4;
+ retval = read_IRQreg_ipvp(opp, opp->irq_ipi0 + idx);
}
break;
-#endif
- case 0xE0: /* SPVE */
+ case 0x10E0: /* SPVE */
retval = opp->spve;
break;
- case 0xF0: /* TIFR */
+ case 0x10F0: /* TIFR */
retval = opp->tifr;
break;
default:
@@ -714,10 +728,10 @@ static void openpic_timer_write (void *opaque, uint32_t addr, uint32_t val)
opp->timers[idx].tibc = val;
break;
case 0x20: /* TIVP */
- write_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IPVP, val);
+ write_IRQreg_ipvp(opp, opp->irq_tim0 + idx, val);
break;
case 0x30: /* TIDE */
- write_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IDE, val);
+ write_IRQreg_ide(opp, opp->irq_tim0 + idx, val);
break;
}
}
@@ -744,10 +758,10 @@ static uint32_t openpic_timer_read (void *opaque, uint32_t addr)
retval = opp->timers[idx].tibc;
break;
case 0x20: /* TIPV */
- retval = read_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IPVP);
+ retval = read_IRQreg_ipvp(opp, opp->irq_tim0 + idx);
break;
case 0x30: /* TIDE */
- retval = read_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IDE);
+ retval = read_IRQreg_ide(opp, opp->irq_tim0 + idx);
break;
}
DPRINTF("%s: => %08x\n", __func__, retval);
@@ -767,10 +781,10 @@ static void openpic_src_write (void *opaque, uint32_t addr, uint32_t val)
idx = addr >> 5;
if (addr & 0x10) {
/* EXDE / IFEDE / IEEDE */
- write_IRQreg(opp, idx, IRQ_IDE, val);
+ write_IRQreg_ide(opp, idx, val);
} else {
/* EXVP / IFEVP / IEEVP */
- write_IRQreg(opp, idx, IRQ_IPVP, val);
+ write_IRQreg_ipvp(opp, idx, val);
}
}
@@ -788,38 +802,40 @@ static uint32_t openpic_src_read (void *opaque, uint32_t addr)
idx = addr >> 5;
if (addr & 0x10) {
/* EXDE / IFEDE / IEEDE */
- retval = read_IRQreg(opp, idx, IRQ_IDE);
+ retval = read_IRQreg_ide(opp, idx);
} else {
/* EXVP / IFEVP / IEEVP */
- retval = read_IRQreg(opp, idx, IRQ_IPVP);
+ retval = read_IRQreg_ipvp(opp, idx);
}
DPRINTF("%s: => %08x\n", __func__, retval);
return retval;
}
-static void openpic_cpu_write (void *opaque, target_phys_addr_t addr, uint32_t val)
+static void openpic_cpu_write_internal(void *opaque, target_phys_addr_t addr,
+ uint32_t val, int idx)
{
openpic_t *opp = opaque;
IRQ_src_t *src;
IRQ_dst_t *dst;
- int idx, s_IRQ, n_IRQ;
+ int s_IRQ, n_IRQ;
- DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val);
+ DPRINTF("%s: cpu %d addr " TARGET_FMT_plx " <= %08x\n", __func__, idx,
+ addr, val);
if (addr & 0xF)
return;
- addr &= 0x1FFF0;
- idx = addr / 0x1000;
dst = &opp->dst[idx];
addr &= 0xFF0;
switch (addr) {
#if MAX_IPI > 0
- case 0x40: /* PIPD */
+ case 0x40: /* IPIDR */
case 0x50:
case 0x60:
case 0x70:
idx = (addr - 0x40) >> 4;
- write_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IDE, val);
+ /* we use IDE as mask which CPUs to deliver the IPI to still. */
+ write_IRQreg_ide(opp, opp->irq_ipi0 + idx,
+ opp->src[opp->irq_ipi0 + idx].ide | val);
openpic_set_irq(opp, opp->irq_ipi0 + idx, 1);
openpic_set_irq(opp, opp->irq_ipi0 + idx, 0);
break;
@@ -856,20 +872,24 @@ static void openpic_cpu_write (void *opaque, target_phys_addr_t addr, uint32_t v
}
}
-static uint32_t openpic_cpu_read (void *opaque, target_phys_addr_t addr)
+static void openpic_cpu_write(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ openpic_cpu_write_internal(opaque, addr, val, (addr & 0x1f000) >> 12);
+}
+
+static uint32_t openpic_cpu_read_internal(void *opaque, target_phys_addr_t addr,
+ int idx)
{
openpic_t *opp = opaque;
IRQ_src_t *src;
IRQ_dst_t *dst;
uint32_t retval;
- int idx, n_IRQ;
+ int n_IRQ;
- DPRINTF("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+ DPRINTF("%s: cpu %d addr " TARGET_FMT_plx "\n", __func__, idx, addr);
retval = 0xFFFFFFFF;
if (addr & 0xF)
return retval;
- addr &= 0x1FFF0;
- idx = addr / 0x1000;
dst = &opp->dst[idx];
addr &= 0xFF0;
switch (addr) {
@@ -909,18 +929,22 @@ static uint32_t openpic_cpu_read (void *opaque, target_phys_addr_t addr)
reset_bit(&src->ipvp, IPVP_ACTIVITY);
src->pending = 0;
}
+
+ if ((n_IRQ >= opp->irq_ipi0) && (n_IRQ < (opp->irq_ipi0 + MAX_IPI))) {
+ src->ide &= ~(1 << idx);
+ if (src->ide && !test_bit(&src->ipvp, IPVP_SENSE)) {
+ /* trigger on CPUs that didn't know about it yet */
+ openpic_set_irq(opp, n_IRQ, 1);
+ openpic_set_irq(opp, n_IRQ, 0);
+ /* if all CPUs knew about it, set active bit again */
+ set_bit(&src->ipvp, IPVP_ACTIVITY);
+ }
+ }
}
break;
case 0xB0: /* PEOI */
retval = 0;
break;
-#if MAX_IPI > 0
- case 0x40: /* IDE */
- case 0x50:
- idx = (addr - 0x40) >> 4;
- retval = read_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IDE);
- break;
-#endif
default:
break;
}
@@ -929,6 +953,11 @@ static uint32_t openpic_cpu_read (void *opaque, target_phys_addr_t addr)
return retval;
}
+static uint32_t openpic_cpu_read(void *opaque, target_phys_addr_t addr)
+{
+ return openpic_cpu_read_internal(opaque, addr, (addr & 0x1f000) >> 12);
+}
+
static void openpic_buggy_write (void *opaque,
target_phys_addr_t addr, uint32_t val)
{
@@ -1247,7 +1276,7 @@ static void mpic_reset (void *opaque)
mpp->glbc = 0x80000000;
/* Initialise controller registers */
- mpp->frep = 0x004f0002;
+ mpp->frep = 0x004f0002 | ((mpp->nb_cpus - 1) << 8);
mpp->veni = VENI;
mpp->pint = 0x00000000;
mpp->spve = 0x0000FFFF;
@@ -1256,6 +1285,10 @@ static void mpic_reset (void *opaque)
mpp->src[i].ipvp = 0x80800000;
mpp->src[i].ide = 0x00000001;
}
+ /* Set IDE for IPIs to 0 so we don't get spurious interrupts */
+ for (i = mpp->irq_ipi0; i < (mpp->irq_ipi0 + MAX_IPI); i++) {
+ mpp->src[i].ide = 0;
+ }
/* Initialise IRQ destinations */
for (i = 0; i < MAX_CPU; i++) {
mpp->dst[i].pctp = 0x0000000F;
@@ -1296,13 +1329,13 @@ static void mpic_timer_write (void *opaque, target_phys_addr_t addr, uint32_t va
mpp->timers[idx].tibc = val;
break;
case 0x20: /* GTIVPR */
- write_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IPVP, val);
+ write_IRQreg_ipvp(mpp, MPIC_TMR_IRQ + idx, val);
break;
case 0x30: /* GTIDR & TFRR */
if ((addr & 0xF0) == 0xF0)
mpp->dst[cpu].tfrr = val;
else
- write_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IDE, val);
+ write_IRQreg_ide(mpp, MPIC_TMR_IRQ + idx, val);
break;
}
}
@@ -1328,13 +1361,13 @@ static uint32_t mpic_timer_read (void *opaque, target_phys_addr_t addr)
retval = mpp->timers[idx].tibc;
break;
case 0x20: /* TIPV */
- retval = read_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IPVP);
+ retval = read_IRQreg_ipvp(mpp, MPIC_TMR_IRQ + idx);
break;
case 0x30: /* TIDR */
if ((addr &0xF0) == 0XF0)
retval = mpp->dst[cpu].tfrr;
else
- retval = read_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IDE);
+ retval = read_IRQreg_ide(mpp, MPIC_TMR_IRQ + idx);
break;
}
DPRINTF("%s: => %08x\n", __func__, retval);
@@ -1357,10 +1390,10 @@ static void mpic_src_ext_write (void *opaque, target_phys_addr_t addr,
idx += (addr & 0xFFF0) >> 5;
if (addr & 0x10) {
/* EXDE / IFEDE / IEEDE */
- write_IRQreg(mpp, idx, IRQ_IDE, val);
+ write_IRQreg_ide(mpp, idx, val);
} else {
/* EXVP / IFEVP / IEEVP */
- write_IRQreg(mpp, idx, IRQ_IPVP, val);
+ write_IRQreg_ipvp(mpp, idx, val);
}
}
}
@@ -1381,10 +1414,10 @@ static uint32_t mpic_src_ext_read (void *opaque, target_phys_addr_t addr)
idx += (addr & 0xFFF0) >> 5;
if (addr & 0x10) {
/* EXDE / IFEDE / IEEDE */
- retval = read_IRQreg(mpp, idx, IRQ_IDE);
+ retval = read_IRQreg_ide(mpp, idx);
} else {
/* EXVP / IFEVP / IEEVP */
- retval = read_IRQreg(mpp, idx, IRQ_IPVP);
+ retval = read_IRQreg_ipvp(mpp, idx);
}
DPRINTF("%s: => %08x\n", __func__, retval);
}
@@ -1407,10 +1440,10 @@ static void mpic_src_int_write (void *opaque, target_phys_addr_t addr,
idx += (addr & 0xFFF0) >> 5;
if (addr & 0x10) {
/* EXDE / IFEDE / IEEDE */
- write_IRQreg(mpp, idx, IRQ_IDE, val);
+ write_IRQreg_ide(mpp, idx, val);
} else {
/* EXVP / IFEVP / IEEVP */
- write_IRQreg(mpp, idx, IRQ_IPVP, val);
+ write_IRQreg_ipvp(mpp, idx, val);
}
}
}
@@ -1431,10 +1464,10 @@ static uint32_t mpic_src_int_read (void *opaque, target_phys_addr_t addr)
idx += (addr & 0xFFF0) >> 5;
if (addr & 0x10) {
/* EXDE / IFEDE / IEEDE */
- retval = read_IRQreg(mpp, idx, IRQ_IDE);
+ retval = read_IRQreg_ide(mpp, idx);
} else {
/* EXVP / IFEVP / IEEVP */
- retval = read_IRQreg(mpp, idx, IRQ_IPVP);
+ retval = read_IRQreg_ipvp(mpp, idx);
}
DPRINTF("%s: => %08x\n", __func__, retval);
}
@@ -1457,10 +1490,10 @@ static void mpic_src_msg_write (void *opaque, target_phys_addr_t addr,
idx += (addr & 0xFFF0) >> 5;
if (addr & 0x10) {
/* EXDE / IFEDE / IEEDE */
- write_IRQreg(mpp, idx, IRQ_IDE, val);
+ write_IRQreg_ide(mpp, idx, val);
} else {
/* EXVP / IFEVP / IEEVP */
- write_IRQreg(mpp, idx, IRQ_IPVP, val);
+ write_IRQreg_ipvp(mpp, idx, val);
}
}
}
@@ -1481,10 +1514,10 @@ static uint32_t mpic_src_msg_read (void *opaque, target_phys_addr_t addr)
idx += (addr & 0xFFF0) >> 5;
if (addr & 0x10) {
/* EXDE / IFEDE / IEEDE */
- retval = read_IRQreg(mpp, idx, IRQ_IDE);
+ retval = read_IRQreg_ide(mpp, idx);
} else {
/* EXVP / IFEVP / IEEVP */
- retval = read_IRQreg(mpp, idx, IRQ_IPVP);
+ retval = read_IRQreg_ipvp(mpp, idx);
}
DPRINTF("%s: => %08x\n", __func__, retval);
}
@@ -1507,10 +1540,10 @@ static void mpic_src_msi_write (void *opaque, target_phys_addr_t addr,
idx += (addr & 0xFFF0) >> 5;
if (addr & 0x10) {
/* EXDE / IFEDE / IEEDE */
- write_IRQreg(mpp, idx, IRQ_IDE, val);
+ write_IRQreg_ide(mpp, idx, val);
} else {
/* EXVP / IFEVP / IEEVP */
- write_IRQreg(mpp, idx, IRQ_IPVP, val);
+ write_IRQreg_ipvp(mpp, idx, val);
}
}
}
@@ -1530,10 +1563,10 @@ static uint32_t mpic_src_msi_read (void *opaque, target_phys_addr_t addr)
idx += (addr & 0xFFF0) >> 5;
if (addr & 0x10) {
/* EXDE / IFEDE / IEEDE */
- retval = read_IRQreg(mpp, idx, IRQ_IDE);
+ retval = read_IRQreg_ide(mpp, idx);
} else {
/* EXVP / IFEVP / IEEVP */
- retval = read_IRQreg(mpp, idx, IRQ_IPVP);
+ retval = read_IRQreg_ipvp(mpp, idx);
}
DPRINTF("%s: => %08x\n", __func__, retval);
}
@@ -1659,10 +1692,6 @@ qemu_irq *mpic_init (MemoryRegion *address_space, target_phys_addr_t base,
{"cpu", &mpic_cpu_ops, MPIC_CPU_REG_START, MPIC_CPU_REG_SIZE},
};
- /* XXX: for now, only one CPU is supported */
- if (nb_cpus != 1)
- return NULL;
-
mpp = g_malloc0(sizeof(openpic_t));
memory_region_init(&mpp->mem, "mpic", 0x40000);
diff --git a/hw/ppc.c b/hw/ppc.c
index 8870748..25b59dd 100644
--- a/hw/ppc.c
+++ b/hw/ppc.c
@@ -50,7 +50,7 @@
static void cpu_ppc_tb_stop (CPUState *env);
static void cpu_ppc_tb_start (CPUState *env);
-static void ppc_set_irq (CPUState *env, int n_IRQ, int level)
+void ppc_set_irq(CPUState *env, int n_IRQ, int level)
{
unsigned int old_pending = env->pending_interrupts;
@@ -423,25 +423,8 @@ void ppce500_irq_init (CPUState *env)
}
/*****************************************************************************/
/* PowerPC time base and decrementer emulation */
-struct ppc_tb_t {
- /* Time base management */
- int64_t tb_offset; /* Compensation */
- int64_t atb_offset; /* Compensation */
- uint32_t tb_freq; /* TB frequency */
- /* Decrementer management */
- uint64_t decr_next; /* Tick for next decr interrupt */
- uint32_t decr_freq; /* decrementer frequency */
- struct QEMUTimer *decr_timer;
- /* Hypervisor decrementer management */
- uint64_t hdecr_next; /* Tick for next hdecr interrupt */
- struct QEMUTimer *hdecr_timer;
- uint64_t purr_load;
- uint64_t purr_start;
- void *opaque;
-};
-static inline uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk,
- int64_t tb_offset)
+uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset)
{
/* TB time in tb periods */
return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset;
@@ -611,10 +594,13 @@ static inline uint32_t _cpu_ppc_load_decr(CPUState *env, uint64_t next)
int64_t diff;
diff = next - qemu_get_clock_ns(vm_clock);
- if (diff >= 0)
+ if (diff >= 0) {
decr = muldiv64(diff, tb_env->decr_freq, get_ticks_per_sec());
- else
+ } else if (tb_env->flags & PPC_TIMER_BOOKE) {
+ decr = 0;
+ } else {
decr = -muldiv64(-diff, tb_env->decr_freq, get_ticks_per_sec());
+ }
LOG_TB("%s: %08" PRIx32 "\n", __func__, decr);
return decr;
@@ -678,18 +664,24 @@ static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp,
decr, value);
now = qemu_get_clock_ns(vm_clock);
next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq);
- if (is_excp)
+ if (is_excp) {
next += *nextp - now;
- if (next == now)
+ }
+ if (next == now) {
next++;
+ }
*nextp = next;
/* Adjust timer */
qemu_mod_timer(timer, next);
- /* If we set a negative value and the decrementer was positive,
- * raise an exception.
+
+ /* If we set a negative value and the decrementer was positive, raise an
+ * exception.
*/
- if ((value & 0x80000000) && !(decr & 0x80000000))
+ if ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED)
+ && (value & 0x80000000)
+ && !(decr & 0x80000000)) {
(*raise_excp)(env);
+ }
}
static inline void _cpu_ppc_store_decr(CPUState *env, uint32_t decr,
@@ -763,6 +755,7 @@ clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq)
tb_env = g_malloc0(sizeof(ppc_tb_t));
env->tb_env = tb_env;
+ tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED;
/* Create new timer */
tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_decr_cb, env);
if (0) {
@@ -806,11 +799,11 @@ uint32_t cpu_ppc601_load_rtcl (CPUState *env)
}
/*****************************************************************************/
-/* Embedded PowerPC timers */
+/* PowerPC 40x timers */
/* PIT, FIT & WDT */
-typedef struct ppcemb_timer_t ppcemb_timer_t;
-struct ppcemb_timer_t {
+typedef struct ppc40x_timer_t ppc40x_timer_t;
+struct ppc40x_timer_t {
uint64_t pit_reload; /* PIT auto-reload value */
uint64_t fit_next; /* Tick for next FIT interrupt */
struct QEMUTimer *fit_timer;
@@ -826,12 +819,12 @@ static void cpu_4xx_fit_cb (void *opaque)
{
CPUState *env;
ppc_tb_t *tb_env;
- ppcemb_timer_t *ppcemb_timer;
+ ppc40x_timer_t *ppc40x_timer;
uint64_t now, next;
env = opaque;
tb_env = env->tb_env;
- ppcemb_timer = tb_env->opaque;
+ ppc40x_timer = tb_env->opaque;
now = qemu_get_clock_ns(vm_clock);
switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) {
case 0:
@@ -853,7 +846,7 @@ static void cpu_4xx_fit_cb (void *opaque)
next = now + muldiv64(next, get_ticks_per_sec(), tb_env->tb_freq);
if (next == now)
next++;
- qemu_mod_timer(ppcemb_timer->fit_timer, next);
+ qemu_mod_timer(ppc40x_timer->fit_timer, next);
env->spr[SPR_40x_TSR] |= 1 << 26;
if ((env->spr[SPR_40x_TCR] >> 23) & 0x1)
ppc_set_irq(env, PPC_INTERRUPT_FIT, 1);
@@ -865,11 +858,11 @@ static void cpu_4xx_fit_cb (void *opaque)
/* Programmable interval timer */
static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp)
{
- ppcemb_timer_t *ppcemb_timer;
+ ppc40x_timer_t *ppc40x_timer;
uint64_t now, next;
- ppcemb_timer = tb_env->opaque;
- if (ppcemb_timer->pit_reload <= 1 ||
+ ppc40x_timer = tb_env->opaque;
+ if (ppc40x_timer->pit_reload <= 1 ||
!((env->spr[SPR_40x_TCR] >> 26) & 0x1) ||
(is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) {
/* Stop PIT */
@@ -877,9 +870,9 @@ static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp)
qemu_del_timer(tb_env->decr_timer);
} else {
LOG_TB("%s: start PIT %016" PRIx64 "\n",
- __func__, ppcemb_timer->pit_reload);
+ __func__, ppc40x_timer->pit_reload);
now = qemu_get_clock_ns(vm_clock);
- next = now + muldiv64(ppcemb_timer->pit_reload,
+ next = now + muldiv64(ppc40x_timer->pit_reload,
get_ticks_per_sec(), tb_env->decr_freq);
if (is_excp)
next += tb_env->decr_next - now;
@@ -894,21 +887,21 @@ static void cpu_4xx_pit_cb (void *opaque)
{
CPUState *env;
ppc_tb_t *tb_env;
- ppcemb_timer_t *ppcemb_timer;
+ ppc40x_timer_t *ppc40x_timer;
env = opaque;
tb_env = env->tb_env;
- ppcemb_timer = tb_env->opaque;
+ ppc40x_timer = tb_env->opaque;
env->spr[SPR_40x_TSR] |= 1 << 27;
if ((env->spr[SPR_40x_TCR] >> 26) & 0x1)
- ppc_set_irq(env, ppcemb_timer->decr_excp, 1);
+ ppc_set_irq(env, ppc40x_timer->decr_excp, 1);
start_stop_pit(env, tb_env, 1);
LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " "
"%016" PRIx64 "\n", __func__,
(int)((env->spr[SPR_40x_TCR] >> 22) & 0x1),
(int)((env->spr[SPR_40x_TCR] >> 26) & 0x1),
env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR],
- ppcemb_timer->pit_reload);
+ ppc40x_timer->pit_reload);
}
/* Watchdog timer */
@@ -916,12 +909,12 @@ static void cpu_4xx_wdt_cb (void *opaque)
{
CPUState *env;
ppc_tb_t *tb_env;
- ppcemb_timer_t *ppcemb_timer;
+ ppc40x_timer_t *ppc40x_timer;
uint64_t now, next;
env = opaque;
tb_env = env->tb_env;
- ppcemb_timer = tb_env->opaque;
+ ppc40x_timer = tb_env->opaque;
now = qemu_get_clock_ns(vm_clock);
switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) {
case 0:
@@ -948,13 +941,13 @@ static void cpu_4xx_wdt_cb (void *opaque)
switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) {
case 0x0:
case 0x1:
- qemu_mod_timer(ppcemb_timer->wdt_timer, next);
- ppcemb_timer->wdt_next = next;
+ qemu_mod_timer(ppc40x_timer->wdt_timer, next);
+ ppc40x_timer->wdt_next = next;
env->spr[SPR_40x_TSR] |= 1 << 31;
break;
case 0x2:
- qemu_mod_timer(ppcemb_timer->wdt_timer, next);
- ppcemb_timer->wdt_next = next;
+ qemu_mod_timer(ppc40x_timer->wdt_timer, next);
+ ppc40x_timer->wdt_next = next;
env->spr[SPR_40x_TSR] |= 1 << 30;
if ((env->spr[SPR_40x_TCR] >> 27) & 0x1)
ppc_set_irq(env, PPC_INTERRUPT_WDT, 1);
@@ -982,12 +975,12 @@ static void cpu_4xx_wdt_cb (void *opaque)
void store_40x_pit (CPUState *env, target_ulong val)
{
ppc_tb_t *tb_env;
- ppcemb_timer_t *ppcemb_timer;
+ ppc40x_timer_t *ppc40x_timer;
tb_env = env->tb_env;
- ppcemb_timer = tb_env->opaque;
+ ppc40x_timer = tb_env->opaque;
LOG_TB("%s val" TARGET_FMT_lx "\n", __func__, val);
- ppcemb_timer->pit_reload = val;
+ ppc40x_timer->pit_reload = val;
start_stop_pit(env, tb_env, 0);
}
@@ -996,31 +989,7 @@ target_ulong load_40x_pit (CPUState *env)
return cpu_ppc_load_decr(env);
}
-void store_booke_tsr (CPUState *env, target_ulong val)
-{
- ppc_tb_t *tb_env = env->tb_env;
- ppcemb_timer_t *ppcemb_timer;
-
- ppcemb_timer = tb_env->opaque;
-
- LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val);
- env->spr[SPR_40x_TSR] &= ~(val & 0xFC000000);
- if (val & 0x80000000)
- ppc_set_irq(env, ppcemb_timer->decr_excp, 0);
-}
-
-void store_booke_tcr (CPUState *env, target_ulong val)
-{
- ppc_tb_t *tb_env;
-
- tb_env = env->tb_env;
- LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val);
- env->spr[SPR_40x_TCR] = val & 0xFFC00000;
- start_stop_pit(env, tb_env, 1);
- cpu_4xx_wdt_cb(env);
-}
-
-static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq)
+static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq)
{
CPUState *env = opaque;
ppc_tb_t *tb_env = env->tb_env;
@@ -1032,30 +1001,31 @@ static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq)
/* XXX: we should also update all timers */
}
-clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq,
+clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq,
unsigned int decr_excp)
{
ppc_tb_t *tb_env;
- ppcemb_timer_t *ppcemb_timer;
+ ppc40x_timer_t *ppc40x_timer;
tb_env = g_malloc0(sizeof(ppc_tb_t));
env->tb_env = tb_env;
- ppcemb_timer = g_malloc0(sizeof(ppcemb_timer_t));
+ tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED;
+ ppc40x_timer = g_malloc0(sizeof(ppc40x_timer_t));
tb_env->tb_freq = freq;
tb_env->decr_freq = freq;
- tb_env->opaque = ppcemb_timer;
+ tb_env->opaque = ppc40x_timer;
LOG_TB("%s freq %" PRIu32 "\n", __func__, freq);
- if (ppcemb_timer != NULL) {
+ if (ppc40x_timer != NULL) {
/* We use decr timer for PIT */
tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_4xx_pit_cb, env);
- ppcemb_timer->fit_timer =
+ ppc40x_timer->fit_timer =
qemu_new_timer_ns(vm_clock, &cpu_4xx_fit_cb, env);
- ppcemb_timer->wdt_timer =
+ ppc40x_timer->wdt_timer =
qemu_new_timer_ns(vm_clock, &cpu_4xx_wdt_cb, env);
- ppcemb_timer->decr_excp = decr_excp;
+ ppc40x_timer->decr_excp = decr_excp;
}
- return &ppc_emb_set_tb_clk;
+ return &ppc_40x_set_tb_clk;
}
/*****************************************************************************/
diff --git a/hw/ppc.h b/hw/ppc.h
index 3ccf134..9f91170 100644
--- a/hw/ppc.h
+++ b/hw/ppc.h
@@ -1,3 +1,5 @@
+void ppc_set_irq (CPUState *env, int n_IRQ, int level);
+
/* PowerPC hardware exceptions management helpers */
typedef void (*clk_setup_cb)(void *opaque, uint32_t freq);
typedef struct clk_setup_t clk_setup_t;
@@ -11,6 +13,36 @@ static inline void clk_setup (clk_setup_t *clk, uint32_t freq)
(*clk->cb)(clk->opaque, freq);
}
+struct ppc_tb_t {
+ /* Time base management */
+ int64_t tb_offset; /* Compensation */
+ int64_t atb_offset; /* Compensation */
+ uint32_t tb_freq; /* TB frequency */
+ /* Decrementer management */
+ uint64_t decr_next; /* Tick for next decr interrupt */
+ uint32_t decr_freq; /* decrementer frequency */
+ struct QEMUTimer *decr_timer;
+ /* Hypervisor decrementer management */
+ uint64_t hdecr_next; /* Tick for next hdecr interrupt */
+ struct QEMUTimer *hdecr_timer;
+ uint64_t purr_load;
+ uint64_t purr_start;
+ void *opaque;
+ uint32_t flags;
+};
+
+/* PPC Timers flags */
+#define PPC_TIMER_BOOKE (1 << 0) /* Enable Booke support */
+#define PPC_TIMER_E500 (1 << 1) /* Enable e500 support */
+#define PPC_DECR_UNDERFLOW_TRIGGERED (1 << 2) /* Decr interrupt triggered when
+ * the most significant bit
+ * changes from 0 to 1.
+ */
+#define PPC_DECR_ZERO_TRIGGERED (1 << 3) /* Decr interrupt triggered when
+ * the decrementer reaches zero.
+ */
+
+uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset);
clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq);
/* Embedded PowerPC DCR management */
typedef uint32_t (*dcr_read_cb)(void *opaque, int dcrn);
@@ -19,7 +51,7 @@ int ppc_dcr_init (CPUState *env, int (*dcr_read_error)(int dcrn),
int (*dcr_write_error)(int dcrn));
int ppc_dcr_register (CPUState *env, int dcrn, void *opaque,
dcr_read_cb drc_read, dcr_write_cb dcr_write);
-clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq,
+clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq,
unsigned int decr_excp);
/* Embedded PowerPC reset */
@@ -55,3 +87,6 @@ enum {
#define FW_CFG_PPC_KVM_PID (FW_CFG_ARCH_LOCAL + 0x07)
#define PPC_SERIAL_MM_BAUDBASE 399193
+
+/* ppc_booke.c */
+void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags);
diff --git a/hw/ppc405_boards.c b/hw/ppc405_boards.c
index ca65ac3..9136288 100644
--- a/hw/ppc405_boards.c
+++ b/hw/ppc405_boards.c
@@ -213,7 +213,8 @@ static void ref405ep_init (ram_addr_t ram_size,
sram_size = 512 * 1024;
sram_offset = qemu_ram_alloc(NULL, "ef405ep.sram", sram_size);
#ifdef DEBUG_BOARD_INIT
- printf("%s: register SRAM at offset %08lx\n", __func__, sram_offset);
+ printf("%s: register SRAM at offset " RAM_ADDR_FMT "\n",
+ __func__, sram_offset);
#endif
cpu_register_physical_memory(0xFFF00000, sram_size,
sram_offset | IO_MEM_RAM);
@@ -357,7 +358,7 @@ static void ref405ep_init (ram_addr_t ram_size,
#ifdef DEBUG_BOARD_INIT
printf("%s: Done\n", __func__);
#endif
- printf("bdloc %016lx\n", (unsigned long)bdloc);
+ printf("bdloc " RAM_ADDR_FMT "\n", bdloc);
}
static QEMUMachine ref405ep_machine = {
diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c
index 9228939..b734e3a 100644
--- a/hw/ppc440_bamboo.c
+++ b/hw/ppc440_bamboo.c
@@ -44,6 +44,8 @@ static int bamboo_load_device_tree(target_phys_addr_t addr,
char *filename;
int fdt_size;
void *fdt;
+ uint32_t tb_freq = 400000000;
+ uint32_t clock_freq = 400000000;
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
if (!filename) {
@@ -77,8 +79,18 @@ static int bamboo_load_device_tree(target_phys_addr_t addr,
if (ret < 0)
fprintf(stderr, "couldn't set /chosen/bootargs\n");
- if (kvm_enabled())
- kvmppc_fdt_update(fdt);
+ /* Copy data from the host device tree into the guest. Since the guest can
+ * directly access the timebase without host involvement, we must expose
+ * the correct frequencies. */
+ if (kvm_enabled()) {
+ tb_freq = kvmppc_get_tbfreq();
+ clock_freq = kvmppc_get_clockfreq();
+ }
+
+ qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency",
+ clock_freq);
+ qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency",
+ tb_freq);
ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr);
g_free(fdt);
diff --git a/hw/ppc4xx_devs.c b/hw/ppc4xx_devs.c
index 349f046..d18caa4 100644
--- a/hw/ppc4xx_devs.c
+++ b/hw/ppc4xx_devs.c
@@ -56,7 +56,7 @@ CPUState *ppc4xx_init (const char *cpu_model,
cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */
cpu_clk->opaque = env;
/* Set time-base frequency to sysclk */
- tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_PIT);
+ tb_clk->cb = ppc_40x_timers_init(env, sysclk, PPC_INTERRUPT_PIT);
tb_clk->opaque = env;
ppc_dcr_init(env, NULL, NULL);
/* Register qemu callbacks */
diff --git a/hw/ppc_booke.c b/hw/ppc_booke.c
new file mode 100644
index 0000000..8871945
--- /dev/null
+++ b/hw/ppc_booke.c
@@ -0,0 +1,254 @@
+/*
+ * QEMU PowerPC Booke hardware System Emulator
+ *
+ * Copyright (c) 2011 AdaCore
+ *
+ * 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 "hw.h"
+#include "ppc.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "nvram.h"
+#include "qemu-log.h"
+#include "loader.h"
+
+
+/* Timer Control Register */
+
+#define TCR_WP_SHIFT 30 /* Watchdog Timer Period */
+#define TCR_WP_MASK (0x3 << TCR_WP_SHIFT)
+#define TCR_WRC_SHIFT 28 /* Watchdog Timer Reset Control */
+#define TCR_WRC_MASK (0x3 << TCR_WRC_SHIFT)
+#define TCR_WIE (1 << 27) /* Watchdog Timer Interrupt Enable */
+#define TCR_DIE (1 << 26) /* Decrementer Interrupt Enable */
+#define TCR_FP_SHIFT 24 /* Fixed-Interval Timer Period */
+#define TCR_FP_MASK (0x3 << TCR_FP_SHIFT)
+#define TCR_FIE (1 << 23) /* Fixed-Interval Timer Interrupt Enable */
+#define TCR_ARE (1 << 22) /* Auto-Reload Enable */
+
+/* Timer Control Register (e500 specific fields) */
+
+#define TCR_E500_FPEXT_SHIFT 13 /* Fixed-Interval Timer Period Extension */
+#define TCR_E500_FPEXT_MASK (0xf << TCR_E500_FPEXT_SHIFT)
+#define TCR_E500_WPEXT_SHIFT 17 /* Watchdog Timer Period Extension */
+#define TCR_E500_WPEXT_MASK (0xf << TCR_E500_WPEXT_SHIFT)
+
+/* Timer Status Register */
+
+#define TSR_FIS (1 << 26) /* Fixed-Interval Timer Interrupt Status */
+#define TSR_DIS (1 << 27) /* Decrementer Interrupt Status */
+#define TSR_WRS_SHIFT 28 /* Watchdog Timer Reset Status */
+#define TSR_WRS_MASK (0x3 << TSR_WRS_SHIFT)
+#define TSR_WIS (1 << 30) /* Watchdog Timer Interrupt Status */
+#define TSR_ENW (1 << 31) /* Enable Next Watchdog Timer */
+
+typedef struct booke_timer_t booke_timer_t;
+struct booke_timer_t {
+
+ uint64_t fit_next;
+ struct QEMUTimer *fit_timer;
+
+ uint64_t wdt_next;
+ struct QEMUTimer *wdt_timer;
+
+ uint32_t flags;
+};
+
+static void booke_update_irq(CPUState *env)
+{
+ ppc_set_irq(env, PPC_INTERRUPT_DECR,
+ (env->spr[SPR_BOOKE_TSR] & TSR_DIS
+ && env->spr[SPR_BOOKE_TCR] & TCR_DIE));
+
+ ppc_set_irq(env, PPC_INTERRUPT_WDT,
+ (env->spr[SPR_BOOKE_TSR] & TSR_WIS
+ && env->spr[SPR_BOOKE_TCR] & TCR_WIE));
+
+ ppc_set_irq(env, PPC_INTERRUPT_FIT,
+ (env->spr[SPR_BOOKE_TSR] & TSR_FIS
+ && env->spr[SPR_BOOKE_TCR] & TCR_FIE));
+}
+
+/* Return the location of the bit of time base at which the FIT will raise an
+ interrupt */
+static uint8_t booke_get_fit_target(CPUState *env, ppc_tb_t *tb_env)
+{
+ uint8_t fp = (env->spr[SPR_BOOKE_TCR] & TCR_FP_MASK) >> TCR_FP_SHIFT;
+
+ if (tb_env->flags & PPC_TIMER_E500) {
+ /* e500 Fixed-interval timer period extension */
+ uint32_t fpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_FPEXT_MASK)
+ >> TCR_E500_FPEXT_SHIFT;
+ fp = 63 - (fp | fpext << 2);
+ } else {
+ fp = env->fit_period[fp];
+ }
+
+ return fp;
+}
+
+/* Return the location of the bit of time base at which the WDT will raise an
+ interrupt */
+static uint8_t booke_get_wdt_target(CPUState *env, ppc_tb_t *tb_env)
+{
+ uint8_t wp = (env->spr[SPR_BOOKE_TCR] & TCR_WP_MASK) >> TCR_WP_SHIFT;
+
+ if (tb_env->flags & PPC_TIMER_E500) {
+ /* e500 Watchdog timer period extension */
+ uint32_t wpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_WPEXT_MASK)
+ >> TCR_E500_WPEXT_SHIFT;
+ wp = 63 - (wp | wpext << 2);
+ } else {
+ wp = env->wdt_period[wp];
+ }
+
+ return wp;
+}
+
+static void booke_update_fixed_timer(CPUState *env,
+ uint8_t target_bit,
+ uint64_t *next,
+ struct QEMUTimer *timer)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t lapse;
+ uint64_t tb;
+ uint64_t period = 1 << (target_bit + 1);
+ uint64_t now;
+
+ now = qemu_get_clock_ns(vm_clock);
+ tb = cpu_ppc_get_tb(tb_env, now, tb_env->tb_offset);
+
+ lapse = period - ((tb - (1 << target_bit)) & (period - 1));
+
+ *next = now + muldiv64(lapse, get_ticks_per_sec(), tb_env->tb_freq);
+
+ /* XXX: If expire time is now. We can't run the callback because we don't
+ * have access to it. So we just set the timer one nanosecond later.
+ */
+
+ if (*next == now) {
+ (*next)++;
+ }
+
+ qemu_mod_timer(timer, *next);
+}
+
+static void booke_decr_cb(void *opaque)
+{
+ CPUState *env = opaque;
+
+ env->spr[SPR_BOOKE_TSR] |= TSR_DIS;
+ booke_update_irq(env);
+
+ if (env->spr[SPR_BOOKE_TCR] & TCR_ARE) {
+ /* Auto Reload */
+ cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]);
+ }
+}
+
+static void booke_fit_cb(void *opaque)
+{
+ CPUState *env;
+ ppc_tb_t *tb_env;
+ booke_timer_t *booke_timer;
+
+ env = opaque;
+ tb_env = env->tb_env;
+ booke_timer = tb_env->opaque;
+ env->spr[SPR_BOOKE_TSR] |= TSR_FIS;
+
+ booke_update_irq(env);
+
+ booke_update_fixed_timer(env,
+ booke_get_fit_target(env, tb_env),
+ &booke_timer->fit_next,
+ booke_timer->fit_timer);
+}
+
+static void booke_wdt_cb(void *opaque)
+{
+ CPUState *env;
+ ppc_tb_t *tb_env;
+ booke_timer_t *booke_timer;
+
+ env = opaque;
+ tb_env = env->tb_env;
+ booke_timer = tb_env->opaque;
+
+ /* TODO: There's lots of complicated stuff to do here */
+
+ booke_update_irq(env);
+
+ booke_update_fixed_timer(env,
+ booke_get_wdt_target(env, tb_env),
+ &booke_timer->wdt_next,
+ booke_timer->wdt_timer);
+}
+
+void store_booke_tsr(CPUState *env, target_ulong val)
+{
+ env->spr[SPR_BOOKE_TSR] &= ~val;
+ booke_update_irq(env);
+}
+
+void store_booke_tcr(CPUState *env, target_ulong val)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ booke_timer_t *booke_timer = tb_env->opaque;
+
+ tb_env = env->tb_env;
+ env->spr[SPR_BOOKE_TCR] = val;
+
+ booke_update_irq(env);
+
+ booke_update_fixed_timer(env,
+ booke_get_fit_target(env, tb_env),
+ &booke_timer->fit_next,
+ booke_timer->fit_timer);
+
+ booke_update_fixed_timer(env,
+ booke_get_wdt_target(env, tb_env),
+ &booke_timer->wdt_next,
+ booke_timer->wdt_timer);
+
+}
+
+void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags)
+{
+ ppc_tb_t *tb_env;
+ booke_timer_t *booke_timer;
+
+ tb_env = g_malloc0(sizeof(ppc_tb_t));
+ booke_timer = g_malloc0(sizeof(booke_timer_t));
+
+ env->tb_env = tb_env;
+ tb_env->flags = flags | PPC_TIMER_BOOKE | PPC_DECR_ZERO_TRIGGERED;
+
+ tb_env->tb_freq = freq;
+ tb_env->decr_freq = freq;
+ tb_env->opaque = booke_timer;
+ tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &booke_decr_cb, env);
+
+ booke_timer->fit_timer =
+ qemu_new_timer_ns(vm_clock, &booke_fit_cb, env);
+ booke_timer->wdt_timer =
+ qemu_new_timer_ns(vm_clock, &booke_wdt_cb, env);
+}
diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h
index 7351bb6..af75e45 100644
--- a/hw/ppc_mac.h
+++ b/hw/ppc_mac.h
@@ -77,46 +77,4 @@ void macio_nvram_setup_bar(MacIONVRAMState *s, MemoryRegion *bar,
void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len);
uint32_t macio_nvram_read (void *opaque, uint32_t addr);
void macio_nvram_write (void *opaque, uint32_t addr, uint32_t val);
-
-/* adb.c */
-
-#define MAX_ADB_DEVICES 16
-
-#define ADB_MAX_OUT_LEN 16
-
-typedef struct ADBDevice ADBDevice;
-
-/* buf = NULL means polling */
-typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out,
- const uint8_t *buf, int len);
-typedef int ADBDeviceReset(ADBDevice *d);
-
-struct ADBDevice {
- struct ADBBusState *bus;
- int devaddr;
- int handler;
- ADBDeviceRequest *devreq;
- ADBDeviceReset *devreset;
- void *opaque;
-};
-
-typedef struct ADBBusState {
- ADBDevice devices[MAX_ADB_DEVICES];
- int nb_devices;
- int poll_index;
-} ADBBusState;
-
-int adb_request(ADBBusState *s, uint8_t *buf_out,
- const uint8_t *buf, int len);
-int adb_poll(ADBBusState *s, uint8_t *buf_out);
-
-ADBDevice *adb_register_device(ADBBusState *s, int devaddr,
- ADBDeviceRequest *devreq,
- ADBDeviceReset *devreset,
- void *opaque);
-void adb_kbd_init(ADBBusState *bus);
-void adb_mouse_init(ADBBusState *bus);
-
-extern ADBBusState adb_bus;
-
#endif /* !defined(__PPC_MAC_H__) */
diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c
index b1cc3d7..b9a50db 100644
--- a/hw/ppc_newworld.c
+++ b/hw/ppc_newworld.c
@@ -49,6 +49,7 @@
#include "hw.h"
#include "ppc.h"
#include "ppc_mac.h"
+#include "adb.h"
#include "mac_dbdma.h"
#include "nvram.h"
#include "pc.h"
diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c
index fa2c1e7..ebcaafa 100644
--- a/hw/ppc_oldworld.c
+++ b/hw/ppc_oldworld.c
@@ -26,6 +26,7 @@
#include "hw.h"
#include "ppc.h"
#include "ppc_mac.h"
+#include "adb.h"
#include "mac_dbdma.h"
#include "nvram.h"
#include "pc.h"
diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c
index 28acf0b..5bf8eab 100644
--- a/hw/ppce500_mpc8544ds.c
+++ b/hw/ppce500_mpc8544ds.c
@@ -14,8 +14,6 @@
* (at your option) any later version.
*/
-#include <dirent.h>
-
#include "config.h"
#include "qemu-common.h"
#include "net.h"
@@ -52,6 +50,7 @@
#define MPC8544_PCI_IO 0xE1000000
#define MPC8544_PCI_IOLEN 0x10000
#define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000)
+#define MPC8544_SPIN_BASE 0xEF000000
struct boot_info
{
@@ -59,30 +58,6 @@ struct boot_info
uint32_t entry;
};
-#ifdef CONFIG_FDT
-static int mpc8544_copy_soc_cell(void *fdt, const char *node, const char *prop)
-{
- uint32_t cell;
- int ret;
-
- ret = kvmppc_read_host_property(node, prop, &cell, sizeof(cell));
- if (ret < 0) {
- fprintf(stderr, "couldn't read host %s/%s\n", node, prop);
- goto out;
- }
-
- ret = qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0",
- prop, cell);
- if (ret < 0) {
- fprintf(stderr, "couldn't set guest /cpus/PowerPC,8544@0/%s\n", prop);
- goto out;
- }
-
-out:
- return ret;
-}
-#endif
-
static int mpc8544_load_device_tree(CPUState *env,
target_phys_addr_t addr,
uint32_t ramsize,
@@ -97,6 +72,9 @@ static int mpc8544_load_device_tree(CPUState *env,
int fdt_size;
void *fdt;
uint8_t hypercall[16];
+ uint32_t clock_freq = 400000000;
+ uint32_t tb_freq = 400000000;
+ int i;
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
if (!filename) {
@@ -134,32 +112,9 @@ static int mpc8544_load_device_tree(CPUState *env,
fprintf(stderr, "couldn't set /chosen/bootargs\n");
if (kvm_enabled()) {
- struct dirent *dirp;
- DIR *dp;
- char buf[128];
-
- if ((dp = opendir("/proc/device-tree/cpus/")) == NULL) {
- printf("Can't open directory /proc/device-tree/cpus/\n");
- ret = -1;
- goto out;
- }
-
- buf[0] = '\0';
- while ((dirp = readdir(dp)) != NULL) {
- if (strncmp(dirp->d_name, "PowerPC", 7) == 0) {
- snprintf(buf, 128, "/cpus/%s", dirp->d_name);
- break;
- }
- }
- closedir(dp);
- if (buf[0] == '\0') {
- printf("Unknow host!\n");
- ret = -1;
- goto out;
- }
-
- mpc8544_copy_soc_cell(fdt, buf, "clock-frequency");
- mpc8544_copy_soc_cell(fdt, buf, "timebase-frequency");
+ /* Read out host's frequencies */
+ clock_freq = kvmppc_get_clockfreq();
+ tb_freq = kvmppc_get_tbfreq();
/* indicate KVM hypercall interface */
qemu_devtree_setprop_string(fdt, "/hypervisor", "compatible",
@@ -167,13 +122,45 @@ static int mpc8544_load_device_tree(CPUState *env,
kvmppc_get_hypercall(env, hypercall, sizeof(hypercall));
qemu_devtree_setprop(fdt, "/hypervisor", "hcall-instructions",
hypercall, sizeof(hypercall));
- } else {
- const uint32_t freq = 400000000;
+ }
- qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0",
- "clock-frequency", freq);
- qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0",
- "timebase-frequency", freq);
+ /* We need to generate the cpu nodes in reverse order, so Linux can pick
+ the first node as boot node and be happy */
+ for (i = smp_cpus - 1; i >= 0; i--) {
+ char cpu_name[128];
+ uint64_t cpu_release_addr = cpu_to_be64(MPC8544_SPIN_BASE + (i * 0x20));
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ if (env->cpu_index == i) {
+ break;
+ }
+ }
+
+ if (!env) {
+ continue;
+ }
+
+ snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", env->cpu_index);
+ qemu_devtree_add_subnode(fdt, cpu_name);
+ qemu_devtree_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq);
+ qemu_devtree_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq);
+ qemu_devtree_setprop_string(fdt, cpu_name, "device_type", "cpu");
+ qemu_devtree_setprop_cell(fdt, cpu_name, "reg", env->cpu_index);
+ qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-line-size",
+ env->dcache_line_size);
+ qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-line-size",
+ env->icache_line_size);
+ qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-size", 0x8000);
+ qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-size", 0x8000);
+ qemu_devtree_setprop_cell(fdt, cpu_name, "bus-frequency", 0);
+ if (env->cpu_index) {
+ qemu_devtree_setprop_string(fdt, cpu_name, "status", "disabled");
+ qemu_devtree_setprop_string(fdt, cpu_name, "enable-method", "spin-table");
+ qemu_devtree_setprop(fdt, cpu_name, "cpu-release-addr",
+ &cpu_release_addr, sizeof(cpu_release_addr));
+ } else {
+ qemu_devtree_setprop_string(fdt, cpu_name, "status", "okay");
+ }
}
ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr);
@@ -188,7 +175,7 @@ out:
/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */
static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size)
{
- return (ffs(size >> 10) - 1) >> 1;
+ return ffs(size >> 10) - 1;
}
static void mmubooke_create_initial_mapping(CPUState *env,
@@ -203,6 +190,20 @@ static void mmubooke_create_initial_mapping(CPUState *env,
tlb->mas2 = va & TARGET_PAGE_MASK;
tlb->mas7_3 = pa & TARGET_PAGE_MASK;
tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX;
+
+ env->tlb_dirty = true;
+}
+
+static void mpc8544ds_cpu_reset_sec(void *opaque)
+{
+ CPUState *env = opaque;
+
+ cpu_reset(env);
+
+ /* Secondary CPU starts in halted state for now. Needs to change when
+ implementing non-kernel boot. */
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
}
static void mpc8544ds_cpu_reset(void *opaque)
@@ -213,6 +214,7 @@ static void mpc8544ds_cpu_reset(void *opaque)
cpu_reset(env);
/* Set initial guest state. */
+ env->halted = 0;
env->gpr[1] = (16<<20) - 8;
env->gpr[3] = bi->dt_base;
env->nip = bi->entry;
@@ -228,7 +230,7 @@ static void mpc8544ds_init(ram_addr_t ram_size,
{
MemoryRegion *address_space_mem = get_system_memory();
PCIBus *pci_bus;
- CPUState *env;
+ CPUState *env = NULL;
uint64_t elf_entry;
uint64_t elf_lowaddr;
target_phys_addr_t entry=0;
@@ -239,27 +241,51 @@ static void mpc8544ds_init(ram_addr_t ram_size,
target_long initrd_size=0;
int i=0;
unsigned int pci_irq_nrs[4] = {1, 2, 3, 4};
- qemu_irq *irqs, *mpic;
+ qemu_irq **irqs, *mpic;
DeviceState *dev;
- struct boot_info *boot_info;
+ CPUState *firstenv = NULL;
- /* Setup CPU */
+ /* Setup CPUs */
if (cpu_model == NULL) {
cpu_model = "e500v2_v30";
}
- env = cpu_ppc_init(cpu_model);
- if (!env) {
- fprintf(stderr, "Unable to initialize CPU!\n");
- exit(1);
- }
+ irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *));
+ irqs[0] = g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB);
+ for (i = 0; i < smp_cpus; i++) {
+ qemu_irq *input;
+ env = cpu_ppc_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to initialize CPU!\n");
+ exit(1);
+ }
- /* XXX register timer? */
- ppc_emb_timers_init(env, 400000000, PPC_INTERRUPT_DECR);
- ppc_dcr_init(env, NULL, NULL);
+ if (!firstenv) {
+ firstenv = env;
+ }
+
+ irqs[i] = irqs[0] + (i * OPENPIC_OUTPUT_NB);
+ input = (qemu_irq *)env->irq_inputs;
+ irqs[i][OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT];
+ irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT];
+ env->spr[SPR_BOOKE_PIR] = env->cpu_index = i;
+
+ ppc_booke_timers_init(env, 400000000, PPC_TIMER_E500);
+
+ /* Register reset handler */
+ if (!i) {
+ /* Primary CPU */
+ struct boot_info *boot_info;
+ boot_info = g_malloc0(sizeof(struct boot_info));
+ qemu_register_reset(mpc8544ds_cpu_reset, env);
+ env->load_info = boot_info;
+ } else {
+ /* Secondary CPUs */
+ qemu_register_reset(mpc8544ds_cpu_reset_sec, env);
+ }
+ }
- /* Register reset handler */
- qemu_register_reset(mpc8544ds_cpu_reset, env);
+ env = firstenv;
/* Fixup Memory size on a alignment boundary */
ram_size &= ~(RAM_SIZES_ALIGN - 1);
@@ -269,10 +295,12 @@ static void mpc8544ds_init(ram_addr_t ram_size,
"mpc8544ds.ram", ram_size));
/* MPIC */
- irqs = g_malloc0(sizeof(qemu_irq) * OPENPIC_OUTPUT_NB);
- irqs[OPENPIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPCE500_INPUT_INT];
- irqs[OPENPIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPCE500_INPUT_CINT];
- mpic = mpic_init(address_space_mem, MPC8544_MPIC_REGS_BASE, 1, &irqs, NULL);
+ mpic = mpic_init(address_space_mem, MPC8544_MPIC_REGS_BASE,
+ smp_cpus, irqs, NULL);
+
+ if (!mpic) {
+ cpu_abort(env, "MPIC failed to initialize\n");
+ }
/* Serial */
if (serial_hds[0]) {
@@ -308,6 +336,9 @@ static void mpc8544ds_init(ram_addr_t ram_size,
}
}
+ /* Register spinning region */
+ sysbus_create_simple("e500-spin", MPC8544_SPIN_BASE, NULL);
+
/* Load kernel. */
if (kernel_filename) {
kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL);
@@ -338,10 +369,10 @@ static void mpc8544ds_init(ram_addr_t ram_size,
}
}
- boot_info = g_malloc0(sizeof(struct boot_info));
-
/* If we're loading a kernel directly, we must load the device tree too. */
if (kernel_filename) {
+ struct boot_info *boot_info;
+
#ifndef CONFIG_FDT
cpu_abort(env, "Compiled without FDT support - can't load kernel\n");
#endif
@@ -352,10 +383,10 @@ static void mpc8544ds_init(ram_addr_t ram_size,
exit(1);
}
+ boot_info = env->load_info;
boot_info->entry = entry;
boot_info->dt_base = dt_base;
}
- env->load_info = boot_info;
if (kvm_enabled()) {
kvmppc_init();
@@ -366,6 +397,7 @@ static QEMUMachine mpc8544ds_machine = {
.name = "mpc8544ds",
.desc = "mpc8544ds",
.init = mpc8544ds_init,
+ .max_cpus = 15,
};
static void mpc8544ds_machine_init(void)
diff --git a/hw/ppce500_spin.c b/hw/ppce500_spin.c
new file mode 100644
index 0000000..cccd940
--- /dev/null
+++ b/hw/ppce500_spin.c
@@ -0,0 +1,215 @@
+/*
+ * QEMU PowerPC e500v2 ePAPR spinning code
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Alexander Graf, <agraf@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is not really a device, but models an interface that usually
+ * firmware takes care of. It's used when QEMU plays the role of firmware.
+ *
+ * Specification:
+ *
+ * https://www.power.org/resources/downloads/Power_ePAPR_APPROVED_v1.1.pdf
+ *
+ */
+
+#include "hw.h"
+#include "sysemu.h"
+#include "sysbus.h"
+#include "kvm.h"
+
+#define MAX_CPUS 32
+
+typedef struct spin_info {
+ uint64_t addr;
+ uint64_t r3;
+ uint32_t resv;
+ uint32_t pir;
+ uint64_t reserved;
+} __attribute__ ((packed)) SpinInfo;
+
+typedef struct spin_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ SpinInfo spin[MAX_CPUS];
+} SpinState;
+
+typedef struct spin_kick {
+ CPUState *env;
+ SpinInfo *spin;
+} SpinKick;
+
+static void spin_reset(void *opaque)
+{
+ SpinState *s = opaque;
+ int i;
+
+ for (i = 0; i < MAX_CPUS; i++) {
+ SpinInfo *info = &s->spin[i];
+
+ info->pir = i;
+ info->r3 = i;
+ info->addr = 1;
+ }
+}
+
+/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */
+static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size)
+{
+ return (ffs(size >> 10) - 1) >> 1;
+}
+
+static void mmubooke_create_initial_mapping(CPUState *env,
+ target_ulong va,
+ target_phys_addr_t pa,
+ target_phys_addr_t len)
+{
+ ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 1);
+ target_phys_addr_t size;
+
+ size = (booke206_page_size_to_tlb(len) << MAS1_TSIZE_SHIFT);
+ tlb->mas1 = MAS1_VALID | size;
+ tlb->mas2 = (va & TARGET_PAGE_MASK) | MAS2_M;
+ tlb->mas7_3 = pa & TARGET_PAGE_MASK;
+ tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX;
+}
+
+static void spin_kick(void *data)
+{
+ SpinKick *kick = data;
+ CPUState *env = kick->env;
+ SpinInfo *curspin = kick->spin;
+ target_phys_addr_t map_size = 64 * 1024 * 1024;
+ target_phys_addr_t map_start;
+
+ cpu_synchronize_state(env);
+ stl_p(&curspin->pir, env->spr[SPR_PIR]);
+ env->nip = ldq_p(&curspin->addr) & (map_size - 1);
+ env->gpr[3] = ldq_p(&curspin->r3);
+ env->gpr[4] = 0;
+ env->gpr[5] = 0;
+ env->gpr[6] = 0;
+ env->gpr[7] = map_size;
+ env->gpr[8] = 0;
+ env->gpr[9] = 0;
+
+ map_start = ldq_p(&curspin->addr) & ~(map_size - 1);
+ mmubooke_create_initial_mapping(env, 0, map_start, map_size);
+
+ env->halted = 0;
+ env->exception_index = -1;
+ qemu_cpu_kick(env);
+}
+
+static void spin_write(void *opaque, target_phys_addr_t addr, uint64_t value,
+ unsigned len)
+{
+ SpinState *s = opaque;
+ int env_idx = addr / sizeof(SpinInfo);
+ CPUState *env;
+ SpinInfo *curspin = &s->spin[env_idx];
+ uint8_t *curspin_p = (uint8_t*)curspin;
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ if (env->cpu_index == env_idx) {
+ break;
+ }
+ }
+
+ if (!env) {
+ /* Unknown CPU */
+ return;
+ }
+
+ if (!env->cpu_index) {
+ /* primary CPU doesn't spin */
+ return;
+ }
+
+ curspin_p = &curspin_p[addr % sizeof(SpinInfo)];
+ switch (len) {
+ case 1:
+ stb_p(curspin_p, value);
+ break;
+ case 2:
+ stw_p(curspin_p, value);
+ break;
+ case 4:
+ stl_p(curspin_p, value);
+ break;
+ }
+
+ if (!(ldq_p(&curspin->addr) & 1)) {
+ /* run CPU */
+ SpinKick kick = {
+ .env = env,
+ .spin = curspin,
+ };
+
+ run_on_cpu(env, spin_kick, &kick);
+ }
+}
+
+static uint64_t spin_read(void *opaque, target_phys_addr_t addr, unsigned len)
+{
+ SpinState *s = opaque;
+ uint8_t *spin_p = &((uint8_t*)s->spin)[addr];
+
+ switch (len) {
+ case 1:
+ return ldub_p(spin_p);
+ case 2:
+ return lduw_p(spin_p);
+ case 4:
+ return ldl_p(spin_p);
+ default:
+ assert(0);
+ }
+}
+
+const MemoryRegionOps spin_rw_ops = {
+ .read = spin_read,
+ .write = spin_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static int ppce500_spin_initfn(SysBusDevice *dev)
+{
+ SpinState *s;
+
+ s = FROM_SYSBUS(SpinState, sysbus_from_qdev(dev));
+
+ memory_region_init_io(&s->iomem, &spin_rw_ops, s, "e500 spin pv device",
+ sizeof(SpinInfo) * MAX_CPUS);
+ sysbus_init_mmio_region(dev, &s->iomem);
+
+ qemu_register_reset(spin_reset, s);
+
+ return 0;
+}
+
+static SysBusDeviceInfo ppce500_spin_info = {
+ .init = ppce500_spin_initfn,
+ .qdev.name = "e500-spin",
+ .qdev.size = sizeof(SpinState),
+};
+
+static void ppce500_spin_register(void)
+{
+ sysbus_register_withprop(&ppce500_spin_info);
+}
+device_init(ppce500_spin_register);
diff --git a/hw/spapr.c b/hw/spapr.c
index 1265cee..b118975 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -38,6 +38,9 @@
#include "hw/spapr_vio.h"
#include "hw/xics.h"
+#include "kvm.h"
+#include "kvm_ppc.h"
+
#include <libfdt.h>
#define KERNEL_LOAD_ADDR 0x00000000
@@ -54,8 +57,34 @@
#define MAX_CPUS 256
#define XICS_IRQS 1024
+#define PHANDLE_XICP 0x00001111
+
sPAPREnvironment *spapr;
+qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num)
+{
+ uint32_t irq;
+ qemu_irq qirq;
+
+ if (hint) {
+ irq = hint;
+ /* FIXME: we should probably check for collisions somehow */
+ } else {
+ irq = spapr->next_irq++;
+ }
+
+ qirq = xics_find_qirq(spapr->icp, irq);
+ if (!qirq) {
+ return NULL;
+ }
+
+ if (irq_num) {
+ *irq_num = irq;
+ }
+
+ return qirq;
+}
+
static void *spapr_create_fdt_skel(const char *cpu_model,
target_phys_addr_t initrd_base,
target_phys_addr_t initrd_size,
@@ -70,7 +99,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)};
char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt"
- "\0hcall-tce\0hcall-vio\0hcall-splpar";
+ "\0hcall-tce\0hcall-vio\0hcall-splpar\0hcall-bulk";
uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
int i;
char *modelname;
@@ -137,6 +166,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
char *nodename;
uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
0xffffffff, 0xffffffff};
+ uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TIMEBASE_FREQ;
+ uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000;
if (asprintf(&nodename, "%s@%x", modelname, index) < 0) {
fprintf(stderr, "Allocation failure\n");
@@ -155,10 +186,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
env->dcache_line_size)));
_FDT((fdt_property_cell(fdt, "icache-block-size",
env->icache_line_size)));
- _FDT((fdt_property_cell(fdt, "timebase-frequency", TIMEBASE_FREQ)));
- /* Hardcode CPU frequency for now. It's kind of arbitrary on
- * full emu, for kvm we should copy it from the host */
- _FDT((fdt_property_cell(fdt, "clock-frequency", 1000000000)));
+ _FDT((fdt_property_cell(fdt, "timebase-frequency", tbfreq)));
+ _FDT((fdt_property_cell(fdt, "clock-frequency", cpufreq)));
_FDT((fdt_property_cell(fdt, "ibm,slb-size", env->slb_nr)));
_FDT((fdt_property(fdt, "ibm,pft-size",
pft_size_prop, sizeof(pft_size_prop))));
@@ -189,16 +218,18 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
_FDT((fdt_end_node(fdt)));
/* interrupt controller */
- _FDT((fdt_begin_node(fdt, "interrupt-controller@0")));
+ _FDT((fdt_begin_node(fdt, "interrupt-controller")));
_FDT((fdt_property_string(fdt, "device_type",
"PowerPC-External-Interrupt-Presentation")));
_FDT((fdt_property_string(fdt, "compatible", "IBM,ppc-xicp")));
- _FDT((fdt_property_cell(fdt, "reg", 0)));
_FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
_FDT((fdt_property(fdt, "ibm,interrupt-server-ranges",
interrupt_server_ranges_prop,
sizeof(interrupt_server_ranges_prop))));
+ _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2)));
+ _FDT((fdt_property_cell(fdt, "linux,phandle", PHANDLE_XICP)));
+ _FDT((fdt_property_cell(fdt, "phandle", PHANDLE_XICP)));
_FDT((fdt_end_node(fdt)));
@@ -298,7 +329,6 @@ static void ppc_spapr_init(ram_addr_t ram_size,
long kernel_size, initrd_size, fw_size;
long pteg_shift = 17;
char *filename;
- int irq = 16;
spapr = g_malloc(sizeof(*spapr));
cpu_ppc_hypercall = emulate_spapr_hypercall;
@@ -330,19 +360,29 @@ static void ppc_spapr_init(ram_addr_t ram_size,
}
/* allocate RAM */
- ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", ram_size);
+ spapr->ram_limit = ram_size;
+ ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", spapr->ram_limit);
cpu_register_physical_memory(0, ram_size, ram_offset);
/* allocate hash page table. For now we always make this 16mb,
* later we should probably make it scale to the size of guest
* RAM */
spapr->htab_size = 1ULL << (pteg_shift + 7);
- spapr->htab = g_malloc(spapr->htab_size);
+ spapr->htab = qemu_memalign(spapr->htab_size, spapr->htab_size);
for (env = first_cpu; env != NULL; env = env->next_cpu) {
env->external_htab = spapr->htab;
env->htab_base = -1;
env->htab_mask = spapr->htab_size - 1;
+
+ /* Tell KVM that we're in PAPR mode */
+ env->spr[SPR_SDR1] = (unsigned long)spapr->htab |
+ ((pteg_shift + 7) - 18);
+ env->spr[SPR_HIOR] = 0;
+
+ if (kvm_enabled()) {
+ kvmppc_set_papr(env);
+ }
}
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin");
@@ -356,19 +396,19 @@ static void ppc_spapr_init(ram_addr_t ram_size,
/* Set up Interrupt Controller */
spapr->icp = xics_system_init(XICS_IRQS);
+ spapr->next_irq = 16;
/* Set up VIO bus */
spapr->vio_bus = spapr_vio_bus_init();
- for (i = 0; i < MAX_SERIAL_PORTS; i++, irq++) {
+ for (i = 0; i < MAX_SERIAL_PORTS; i++) {
if (serial_hds[i]) {
spapr_vty_create(spapr->vio_bus, SPAPR_VTY_BASE_ADDRESS + i,
- serial_hds[i], xics_find_qirq(spapr->icp, irq),
- irq);
+ serial_hds[i]);
}
}
- for (i = 0; i < nb_nics; i++, irq++) {
+ for (i = 0; i < nb_nics; i++) {
NICInfo *nd = &nd_table[i];
if (!nd->model) {
@@ -376,8 +416,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
}
if (strcmp(nd->model, "ibmveth") == 0) {
- spapr_vlan_create(spapr->vio_bus, 0x1000 + i, nd,
- xics_find_qirq(spapr->icp, irq), irq);
+ spapr_vlan_create(spapr->vio_bus, 0x1000 + i, nd);
} else {
fprintf(stderr, "pSeries (sPAPR) platform does not support "
"NIC model '%s' (only ibmveth is supported)\n",
@@ -387,9 +426,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
}
for (i = 0; i <= drive_get_max_bus(IF_SCSI); i++) {
- spapr_vscsi_create(spapr->vio_bus, 0x2000 + i,
- xics_find_qirq(spapr->icp, irq), irq);
- irq++;
+ spapr_vscsi_create(spapr->vio_bus, 0x2000 + i);
}
if (kernel_filename) {
@@ -430,7 +467,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
"%ldM guest RAM\n", MIN_RAM_SLOF);
exit(1);
}
- filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "slof.bin");
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, FW_FILE_NAME);
fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
if (fw_size < 0) {
hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
diff --git a/hw/spapr.h b/hw/spapr.h
index 263691b..6657c33 100644
--- a/hw/spapr.h
+++ b/hw/spapr.h
@@ -1,6 +1,8 @@
#if !defined(__HW_SPAPR_H__)
#define __HW_SPAPR_H__
+#include "hw/xics.h"
+
struct VIOsPAPRBus;
struct icp_state;
@@ -8,12 +10,15 @@ typedef struct sPAPREnvironment {
struct VIOsPAPRBus *vio_bus;
struct icp_state *icp;
+ target_phys_addr_t ram_limit;
void *htab;
long htab_size;
target_phys_addr_t fdt_addr, rtas_addr;
long rtas_size;
void *fdt_skel;
target_ulong entry_point;
+ int next_irq;
+ int rtc_offset;
} sPAPREnvironment;
#define H_SUCCESS 0
@@ -278,6 +283,8 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn);
target_ulong spapr_hypercall(CPUState *env, target_ulong opcode,
target_ulong *args);
+qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num);
+
static inline uint32_t rtas_ld(target_ulong phys, int n)
{
return ldl_be_phys(phys + 4*n);
diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c
index f7ead04..84281be 100644
--- a/hw/spapr_hcall.c
+++ b/hw/spapr_hcall.c
@@ -99,6 +99,8 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr,
target_ulong pte_index = args[1];
target_ulong pteh = args[2];
target_ulong ptel = args[3];
+ target_ulong page_shift = 12;
+ target_ulong raddr;
target_ulong i;
uint8_t *hpte;
@@ -111,6 +113,7 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr,
#endif
if ((ptel & 0xff000) == 0) {
/* 16M page */
+ page_shift = 24;
/* lowest AVA bit must be 0 for 16M pages */
if (pteh & 0x80) {
return H_PARAMETER;
@@ -120,12 +123,23 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr,
}
}
- /* FIXME: bounds check the pa? */
+ raddr = (ptel & HPTE_R_RPN) & ~((1ULL << page_shift) - 1);
- /* Check WIMG */
- if ((ptel & HPTE_R_WIMG) != HPTE_R_M) {
- return H_PARAMETER;
+ if (raddr < spapr->ram_limit) {
+ /* Regular RAM - should have WIMG=0010 */
+ if ((ptel & HPTE_R_WIMG) != HPTE_R_M) {
+ return H_PARAMETER;
+ }
+ } else {
+ /* Looks like an IO address */
+ /* FIXME: What WIMG combinations could be sensible for IO?
+ * For now we allow WIMG=010x, but are there others? */
+ /* FIXME: Should we check against registered IO addresses? */
+ if ((ptel & (HPTE_R_W | HPTE_R_I | HPTE_R_M)) != HPTE_R_I) {
+ return H_PARAMETER;
+ }
}
+
pteh &= ~0x60ULL;
if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
@@ -160,20 +174,26 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr,
return H_SUCCESS;
}
-static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr,
- target_ulong opcode, target_ulong *args)
+enum {
+ REMOVE_SUCCESS = 0,
+ REMOVE_NOT_FOUND = 1,
+ REMOVE_PARM = 2,
+ REMOVE_HW = 3,
+};
+
+static target_ulong remove_hpte(CPUState *env, target_ulong ptex,
+ target_ulong avpn,
+ target_ulong flags,
+ target_ulong *vp, target_ulong *rp)
{
- target_ulong flags = args[0];
- target_ulong pte_index = args[1];
- target_ulong avpn = args[2];
uint8_t *hpte;
target_ulong v, r, rb;
- if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
- return H_PARAMETER;
+ if ((ptex * HASH_PTE_SIZE_64) & ~env->htab_mask) {
+ return REMOVE_PARM;
}
- hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
+ hpte = env->external_htab + (ptex * HASH_PTE_SIZE_64);
while (!lock_hpte(hpte, HPTE_V_HVLOCK)) {
/* We have no real concurrency in qemu soft-emulation, so we
* will never actually have a contested lock */
@@ -188,14 +208,106 @@ static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr,
((flags & H_ANDCOND) && (v & avpn) != 0)) {
stq_p(hpte, v & ~HPTE_V_HVLOCK);
assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
- return H_NOT_FOUND;
+ return REMOVE_NOT_FOUND;
}
- args[0] = v & ~HPTE_V_HVLOCK;
- args[1] = r;
+ *vp = v & ~HPTE_V_HVLOCK;
+ *rp = r;
stq_p(hpte, 0);
- rb = compute_tlbie_rb(v, r, pte_index);
+ rb = compute_tlbie_rb(v, r, ptex);
ppc_tlb_invalidate_one(env, rb);
assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
+ return REMOVE_SUCCESS;
+}
+
+static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong flags = args[0];
+ target_ulong pte_index = args[1];
+ target_ulong avpn = args[2];
+ int ret;
+
+ ret = remove_hpte(env, pte_index, avpn, flags,
+ &args[0], &args[1]);
+
+ switch (ret) {
+ case REMOVE_SUCCESS:
+ return H_SUCCESS;
+
+ case REMOVE_NOT_FOUND:
+ return H_NOT_FOUND;
+
+ case REMOVE_PARM:
+ return H_PARAMETER;
+
+ case REMOVE_HW:
+ return H_HARDWARE;
+ }
+
+ assert(0);
+}
+
+#define H_BULK_REMOVE_TYPE 0xc000000000000000ULL
+#define H_BULK_REMOVE_REQUEST 0x4000000000000000ULL
+#define H_BULK_REMOVE_RESPONSE 0x8000000000000000ULL
+#define H_BULK_REMOVE_END 0xc000000000000000ULL
+#define H_BULK_REMOVE_CODE 0x3000000000000000ULL
+#define H_BULK_REMOVE_SUCCESS 0x0000000000000000ULL
+#define H_BULK_REMOVE_NOT_FOUND 0x1000000000000000ULL
+#define H_BULK_REMOVE_PARM 0x2000000000000000ULL
+#define H_BULK_REMOVE_HW 0x3000000000000000ULL
+#define H_BULK_REMOVE_RC 0x0c00000000000000ULL
+#define H_BULK_REMOVE_FLAGS 0x0300000000000000ULL
+#define H_BULK_REMOVE_ABSOLUTE 0x0000000000000000ULL
+#define H_BULK_REMOVE_ANDCOND 0x0100000000000000ULL
+#define H_BULK_REMOVE_AVPN 0x0200000000000000ULL
+#define H_BULK_REMOVE_PTEX 0x00ffffffffffffffULL
+
+#define H_BULK_REMOVE_MAX_BATCH 4
+
+static target_ulong h_bulk_remove(CPUState *env, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ int i;
+
+ for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) {
+ target_ulong *tsh = &args[i*2];
+ target_ulong tsl = args[i*2 + 1];
+ target_ulong v, r, ret;
+
+ if ((*tsh & H_BULK_REMOVE_TYPE) == H_BULK_REMOVE_END) {
+ break;
+ } else if ((*tsh & H_BULK_REMOVE_TYPE) != H_BULK_REMOVE_REQUEST) {
+ return H_PARAMETER;
+ }
+
+ *tsh &= H_BULK_REMOVE_PTEX | H_BULK_REMOVE_FLAGS;
+ *tsh |= H_BULK_REMOVE_RESPONSE;
+
+ if ((*tsh & H_BULK_REMOVE_ANDCOND) && (*tsh & H_BULK_REMOVE_AVPN)) {
+ *tsh |= H_BULK_REMOVE_PARM;
+ return H_PARAMETER;
+ }
+
+ ret = remove_hpte(env, *tsh & H_BULK_REMOVE_PTEX, tsl,
+ (*tsh & H_BULK_REMOVE_FLAGS) >> 26,
+ &v, &r);
+
+ *tsh |= ret << 60;
+
+ switch (ret) {
+ case REMOVE_SUCCESS:
+ *tsh |= (r & (HPTE_R_C | HPTE_R_R)) << 43;
+ break;
+
+ case REMOVE_PARM:
+ return H_PARAMETER;
+
+ case REMOVE_HW:
+ return H_HARDWARE;
+ }
+ }
+
return H_SUCCESS;
}
@@ -449,6 +561,67 @@ static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr,
nret, rtas_r3 + 12 + 4*nargs);
}
+static target_ulong h_logical_load(CPUState *env, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong size = args[0];
+ target_ulong addr = args[1];
+
+ switch (size) {
+ case 1:
+ args[0] = ldub_phys(addr);
+ return H_SUCCESS;
+ case 2:
+ args[0] = lduw_phys(addr);
+ return H_SUCCESS;
+ case 4:
+ args[0] = ldl_phys(addr);
+ return H_SUCCESS;
+ case 8:
+ args[0] = ldq_phys(addr);
+ return H_SUCCESS;
+ }
+ return H_PARAMETER;
+}
+
+static target_ulong h_logical_store(CPUState *env, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ target_ulong size = args[0];
+ target_ulong addr = args[1];
+ target_ulong val = args[2];
+
+ switch (size) {
+ case 1:
+ stb_phys(addr, val);
+ return H_SUCCESS;
+ case 2:
+ stw_phys(addr, val);
+ return H_SUCCESS;
+ case 4:
+ stl_phys(addr, val);
+ return H_SUCCESS;
+ case 8:
+ stq_phys(addr, val);
+ return H_SUCCESS;
+ }
+ return H_PARAMETER;
+}
+
+static target_ulong h_logical_icbi(CPUState *env, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ /* Nothing to do on emulation, KVM will trap this in the kernel */
+ return H_SUCCESS;
+}
+
+static target_ulong h_logical_dcbf(CPUState *env, sPAPREnvironment *spapr,
+ target_ulong opcode, target_ulong *args)
+{
+ /* Nothing to do on emulation, KVM will trap this in the kernel */
+ return H_SUCCESS;
+}
+
static spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1];
static spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE + 1];
@@ -506,6 +679,9 @@ static void hypercall_init(void)
spapr_register_hypercall(H_REMOVE, h_remove);
spapr_register_hypercall(H_PROTECT, h_protect);
+ /* hcall-bulk */
+ spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove);
+
/* hcall-dabr */
spapr_register_hypercall(H_SET_DABR, h_set_dabr);
@@ -513,6 +689,18 @@ static void hypercall_init(void)
spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa);
spapr_register_hypercall(H_CEDE, h_cede);
+ /* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate
+ * here between the "CI" and the "CACHE" variants, they will use whatever
+ * mapping attributes qemu is using. When using KVM, the kernel will
+ * enforce the attributes more strongly
+ */
+ spapr_register_hypercall(H_LOGICAL_CI_LOAD, h_logical_load);
+ spapr_register_hypercall(H_LOGICAL_CI_STORE, h_logical_store);
+ spapr_register_hypercall(H_LOGICAL_CACHE_LOAD, h_logical_load);
+ spapr_register_hypercall(H_LOGICAL_CACHE_STORE, h_logical_store);
+ spapr_register_hypercall(H_LOGICAL_ICBI, h_logical_icbi);
+ spapr_register_hypercall(H_LOGICAL_DCBF, h_logical_dcbf);
+
/* qemu/KVM-PPC specific hcalls */
spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas);
}
diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c
index c18efc7..abe1297 100644
--- a/hw/spapr_llan.c
+++ b/hw/spapr_llan.c
@@ -195,11 +195,9 @@ static int spapr_vlan_init(VIOsPAPRDevice *sdev)
return 0;
}
-void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd,
- qemu_irq qirq, uint32_t vio_irq_num)
+void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd)
{
DeviceState *dev;
- VIOsPAPRDevice *sdev;
dev = qdev_create(&bus->bus, "spapr-vlan");
qdev_prop_set_uint32(dev, "reg", reg);
@@ -207,9 +205,6 @@ void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd,
qdev_set_nic_properties(dev, nd);
qdev_init_nofail(dev);
- sdev = (VIOsPAPRDevice *)dev;
- sdev->qirq = qirq;
- sdev->vio_irq_num = vio_irq_num;
}
static int spapr_vlan_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off)
@@ -500,9 +495,7 @@ static VIOsPAPRDeviceInfo spapr_vlan = {
.qdev.name = "spapr-vlan",
.qdev.size = sizeof(VIOsPAPRVLANDevice),
.qdev.props = (Property[]) {
- DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x1000),
- DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice, rtce_window_size,
- 0x10000000),
+ DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev, 0x1000, 0x10000000),
DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf),
DEFINE_PROP_END_OF_LIST(),
},
diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c
index 00c8ce5..d1ac74c 100644
--- a/hw/spapr_rtas.c
+++ b/hw/spapr_rtas.c
@@ -67,7 +67,7 @@ static void rtas_get_time_of_day(sPAPREnvironment *spapr,
return;
}
- qemu_get_timedate(&tm, 0);
+ qemu_get_timedate(&tm, spapr->rtc_offset);
rtas_st(rets, 0, 0); /* Success */
rtas_st(rets, 1, tm.tm_year + 1900);
@@ -79,6 +79,27 @@ static void rtas_get_time_of_day(sPAPREnvironment *spapr,
rtas_st(rets, 7, 0); /* we don't do nanoseconds */
}
+static void rtas_set_time_of_day(sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ struct tm tm;
+
+ tm.tm_year = rtas_ld(args, 0) - 1900;
+ tm.tm_mon = rtas_ld(args, 1) - 1;
+ tm.tm_mday = rtas_ld(args, 2);
+ tm.tm_hour = rtas_ld(args, 3);
+ tm.tm_min = rtas_ld(args, 4);
+ tm.tm_sec = rtas_ld(args, 5);
+
+ /* Just generate a monitor event for the change */
+ rtc_change_mon_event(&tm);
+ spapr->rtc_offset = qemu_timedate_diff(&tm);
+
+ rtas_st(rets, 0, 0); /* Success */
+}
+
static void rtas_power_off(sPAPREnvironment *spapr,
uint32_t token, uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets)
@@ -271,6 +292,7 @@ static void register_core_rtas(void)
{
spapr_rtas_register("display-character", rtas_display_character);
spapr_rtas_register("get-time-of-day", rtas_get_time_of_day);
+ spapr_rtas_register("set-time-of-day", rtas_set_time_of_day);
spapr_rtas_register("power-off", rtas_power_off);
spapr_rtas_register("query-cpu-stopped-state",
rtas_query_cpu_stopped_state);
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index ce6558b..35818e1 100644
--- a/hw/spapr_vio.c
+++ b/hw/spapr_vio.c
@@ -32,6 +32,7 @@
#include "hw/spapr.h"
#include "hw/spapr_vio.h"
+#include "hw/xics.h"
#ifdef CONFIG_FDT
#include <libfdt.h>
@@ -51,6 +52,10 @@
static struct BusInfo spapr_vio_bus_info = {
.name = "spapr-vio",
.size = sizeof(VIOsPAPRBus),
+ .props = (Property[]) {
+ DEFINE_PROP_UINT32("irq", VIOsPAPRDevice, vio_irq_num, 0), \
+ DEFINE_PROP_END_OF_LIST(),
+ },
};
VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg)
@@ -603,6 +608,11 @@ static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo)
dev->qdev.id = id;
+ dev->qirq = spapr_allocate_irq(dev->vio_irq_num, &dev->vio_irq_num);
+ if (!dev->qirq) {
+ return -1;
+ }
+
rtce_init(dev);
return info->init(dev);
diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h
index 603a8c4..4fe5f74 100644
--- a/hw/spapr_vio.h
+++ b/hw/spapr_vio.h
@@ -60,6 +60,11 @@ typedef struct VIOsPAPRDevice {
VIOsPAPR_CRQ crq;
} VIOsPAPRDevice;
+#define DEFINE_SPAPR_PROPERTIES(type, field, default_reg, default_dma_window) \
+ DEFINE_PROP_UINT32("reg", type, field.reg, default_reg), \
+ DEFINE_PROP_UINT32("dma-window", type, field.rtce_window_size, \
+ default_dma_window)
+
typedef struct VIOsPAPRBus {
BusState bus;
} VIOsPAPRBus;
@@ -98,15 +103,9 @@ uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr);
int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq);
void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len);
-void spapr_vty_create(VIOsPAPRBus *bus,
- uint32_t reg, CharDriverState *chardev,
- qemu_irq qirq, uint32_t vio_irq_num);
-
-void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd,
- qemu_irq qirq, uint32_t vio_irq_num);
-
-void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg,
- qemu_irq qirq, uint32_t vio_irq_num);
+void spapr_vty_create(VIOsPAPRBus *bus, uint32_t reg, CharDriverState *chardev);
+void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd);
+void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg);
int spapr_tce_set_bypass(uint32_t unit, uint32_t enable);
void spapr_vio_quiesce(void);
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index fc9ac6a..e8426d7 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -483,7 +483,6 @@ static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status)
if (status == CHECK_CONDITION) {
req->senselen = scsi_req_get_sense(req->sreq, req->sense,
sizeof(req->sense));
- status = 0;
dprintf("VSCSI: Sense data, %d bytes:\n", len);
dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
req->sense[0], req->sense[1], req->sense[2], req->sense[3],
@@ -893,20 +892,14 @@ static int spapr_vscsi_init(VIOsPAPRDevice *dev)
return 0;
}
-void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg,
- qemu_irq qirq, uint32_t vio_irq_num)
+void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg)
{
DeviceState *dev;
- VIOsPAPRDevice *sdev;
dev = qdev_create(&bus->bus, "spapr-vscsi");
qdev_prop_set_uint32(dev, "reg", reg);
qdev_init_nofail(dev);
-
- sdev = (VIOsPAPRDevice *)dev;
- sdev->qirq = qirq;
- sdev->vio_irq_num = vio_irq_num;
}
static int spapr_vscsi_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off)
@@ -936,9 +929,7 @@ static VIOsPAPRDeviceInfo spapr_vscsi = {
.qdev.name = "spapr-vscsi",
.qdev.size = sizeof(VSCSIState),
.qdev.props = (Property[]) {
- DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x2000),
- DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice,
- rtce_window_size, 0x10000000),
+ DEFINE_SPAPR_PROPERTIES(VSCSIState, vdev, 0x2000, 0x10000000),
DEFINE_PROP_END_OF_LIST(),
},
};
diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c
index f5046d9..a9d4b03 100644
--- a/hw/spapr_vty.c
+++ b/hw/spapr_vty.c
@@ -115,20 +115,14 @@ static target_ulong h_get_term_char(CPUState *env, sPAPREnvironment *spapr,
return H_SUCCESS;
}
-void spapr_vty_create(VIOsPAPRBus *bus,
- uint32_t reg, CharDriverState *chardev,
- qemu_irq qirq, uint32_t vio_irq_num)
+void spapr_vty_create(VIOsPAPRBus *bus, uint32_t reg, CharDriverState *chardev)
{
DeviceState *dev;
- VIOsPAPRDevice *sdev;
dev = qdev_create(&bus->bus, "spapr-vty");
qdev_prop_set_uint32(dev, "reg", reg);
qdev_prop_set_chr(dev, "chardev", chardev);
qdev_init_nofail(dev);
- sdev = (VIOsPAPRDevice *)dev;
- sdev->qirq = qirq;
- sdev->vio_irq_num = vio_irq_num;
}
static void vty_hcalls(VIOsPAPRBus *bus)
@@ -146,7 +140,7 @@ static VIOsPAPRDeviceInfo spapr_vty = {
.qdev.name = "spapr-vty",
.qdev.size = sizeof(VIOsPAPRVTYDevice),
.qdev.props = (Property[]) {
- DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0),
+ DEFINE_SPAPR_PROPERTIES(VIOsPAPRVTYDevice, sdev, 0, 0),
DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev),
DEFINE_PROP_END_OF_LIST(),
},
diff --git a/hw/virtex_ml507.c b/hw/virtex_ml507.c
index 68bf53a..d31a204 100644
--- a/hw/virtex_ml507.c
+++ b/hw/virtex_ml507.c
@@ -82,7 +82,6 @@ static void mmubooke_create_initial_mapping(CPUState *env,
static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size,
int do_init,
const char *cpu_model,
- clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
uint32_t sysclk)
{
CPUState *env;
@@ -94,11 +93,7 @@ static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size,
exit(1);
}
- cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */
- cpu_clk->opaque = env;
- /* Set time-base frequency to sysclk */
- tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_DECR);
- tb_clk->opaque = env;
+ ppc_booke_timers_init(env, sysclk, 0/* no flags */);
ppc_dcr_init(env, NULL, NULL);
@@ -199,7 +194,6 @@ static void virtex_init(ram_addr_t ram_size,
DriveInfo *dinfo;
ram_addr_t phys_ram;
qemu_irq irq[32], *cpu_irq;
- clk_setup_t clk_setup[7];
int kernel_size;
int i;
@@ -209,8 +203,7 @@ static void virtex_init(ram_addr_t ram_size,
}
memset(clk_setup, 0, sizeof(clk_setup));
- env = ppc440_init_xilinx(&ram_size, 1, cpu_model, &clk_setup[0],
- &clk_setup[1], 400000000);
+ env = ppc440_init_xilinx(&ram_size, 1, cpu_model, 400000000);
qemu_register_reset(main_cpu_reset, env);
phys_ram = qemu_ram_alloc(NULL, "ram", ram_size);
diff --git a/hw/xics.c b/hw/xics.c
index 80e064e..1c5eaa4 100644
--- a/hw/xics.c
+++ b/hw/xics.c
@@ -185,17 +185,17 @@ static int ics_valid_irq(struct ics_state *ics, uint32_t nr)
&& (nr < (ics->offset + ics->nr_irqs));
}
-static void ics_set_irq_msi(void *opaque, int nr, int val)
+static void ics_set_irq_msi(void *opaque, int srcno, int val)
{
struct ics_state *ics = (struct ics_state *)opaque;
- struct ics_irq_state *irq = ics->irqs + nr;
+ struct ics_irq_state *irq = ics->irqs + srcno;
if (val) {
if (irq->priority == 0xff) {
irq->masked_pending = 1;
/* masked pending */ ;
} else {
- icp_irq(ics->icp, irq->server, nr + ics->offset, irq->priority);
+ icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
}
}
}
@@ -227,7 +227,7 @@ static void ics_resend_msi(struct ics_state *ics)
static void ics_write_xive_msi(struct ics_state *ics, int nr, int server,
uint8_t priority)
{
- struct ics_irq_state *irq = ics->irqs + nr;
+ struct ics_irq_state *irq = ics->irqs + nr - ics->offset;
irq->server = server;
irq->priority = priority;
@@ -237,7 +237,7 @@ static void ics_write_xive_msi(struct ics_state *ics, int nr, int server,
}
irq->masked_pending = 0;
- icp_irq(ics->icp, server, nr + ics->offset, priority);
+ icp_irq(ics->icp, server, nr, priority);
}
static void ics_reject(struct ics_state *ics, int nr)
@@ -332,7 +332,7 @@ static void rtas_set_xive(sPAPREnvironment *spapr, uint32_t token,
return;
}
- ics_write_xive_msi(ics, nr - ics->offset, server, priority);
+ ics_write_xive_msi(ics, nr, server, priority);
rtas_st(rets, 0, 0); /* Success */
}
@@ -386,7 +386,7 @@ static void rtas_int_off(sPAPREnvironment *spapr, uint32_t token,
struct ics_irq_state *irq = xics->irqs + (nr - xics->offset);
irq->saved_priority = irq->priority;
- ics_write_xive_msi(xics, nr - xics->offset, irq->server, 0xff);
+ ics_write_xive_msi(xics, nr, irq->server, 0xff);
#endif
rtas_st(rets, 0, 0); /* Success */
@@ -416,8 +416,7 @@ static void rtas_int_on(sPAPREnvironment *spapr, uint32_t token,
#if 0
struct ics_irq_state *irq = xics->irqs + (nr - xics->offset);
- ics_write_xive_msi(xics, nr - xics->offset,
- irq->server, irq->saved_priority);
+ ics_write_xive_msi(xics, nr, irq->server, irq->saved_priority);
#endif
rtas_st(rets, 0, 0); /* Success */
OpenPOWER on IntegriCloud