summaryrefslogtreecommitdiff
path: root/target-s390x
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2011-06-24 13:49:54 -0700
committerRobert Mustacchi <rm@joyent.com>2011-06-24 13:49:54 -0700
commit68396ea9c0fe4f75ce30b1eba2c44c43c13344bb (patch)
tree802587d411d9db461e6500c5b635043315f81c27 /target-s390x
downloadillumos-kvm-cmd-68396ea9c0fe4f75ce30b1eba2c44c43c13344bb.tar.gz
Initial commit of d32e8d0b8d9e0ef7cf7ab2e74548982972789dfc from qemu-kvm
Diffstat (limited to 'target-s390x')
-rw-r--r--target-s390x/cpu.h269
-rw-r--r--target-s390x/exec.h53
-rw-r--r--target-s390x/helper.c84
-rw-r--r--target-s390x/kvm.c507
-rw-r--r--target-s390x/machine.c30
-rw-r--r--target-s390x/op_helper.c73
-rw-r--r--target-s390x/translate.c61
7 files changed, 1077 insertions, 0 deletions
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
new file mode 100644
index 0000000..e47c372
--- /dev/null
+++ b/target-s390x/cpu.h
@@ -0,0 +1,269 @@
+/*
+ * S/390 virtual CPU header
+ *
+ * Copyright (c) 2009 Ulrich Hecht
+ *
+ * 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/>.
+ */
+#ifndef CPU_S390X_H
+#define CPU_S390X_H
+
+#define TARGET_LONG_BITS 64
+
+#define ELF_MACHINE EM_S390
+
+#define CPUState struct CPUS390XState
+
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+
+#define NB_MMU_MODES 2
+
+typedef union FPReg {
+ struct {
+#ifdef WORDS_BIGENDIAN
+ float32 e;
+ int32_t __pad;
+#else
+ int32_t __pad;
+ float32 e;
+#endif
+ };
+ float64 d;
+ uint64_t i;
+} FPReg;
+
+typedef struct CPUS390XState {
+ uint64_t regs[16]; /* GP registers */
+
+ uint32_t aregs[16]; /* access registers */
+
+ uint32_t fpc; /* floating-point control register */
+ FPReg fregs[16]; /* FP registers */
+ float_status fpu_status; /* passed to softfloat lib */
+
+ struct {
+ uint64_t mask;
+ uint64_t addr;
+ } psw;
+
+ int cc; /* condition code (0-3) */
+
+ uint64_t __excp_addr;
+
+ CPU_COMMON
+} CPUS390XState;
+
+#if defined(CONFIG_USER_ONLY)
+static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
+{
+ if (newsp)
+ env->regs[15] = newsp;
+ env->regs[0] = 0;
+}
+#endif
+
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _user
+#define MMU_USER_IDX 1
+static inline int cpu_mmu_index (CPUState *env)
+{
+ /* XXX: Currently we don't implement virtual memory */
+ return 0;
+}
+
+CPUS390XState *cpu_s390x_init(const char *cpu_model);
+int cpu_s390x_exec(CPUS390XState *s);
+void cpu_s390x_close(CPUS390XState *s);
+
+/* you can call this signal handler from your SIGBUS and SIGSEGV
+ signal handlers to inform the virtual CPU of exceptions. non zero
+ is returned if the signal was handled by the virtual CPU. */
+int cpu_s390x_signal_handler(int host_signum, void *pinfo,
+ void *puc);
+int cpu_s390x_handle_mmu_fault (CPUS390XState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmuu);
+#define cpu_handle_mmu_fault cpu_s390x_handle_mmu_fault
+
+#define TARGET_PAGE_BITS 12
+
+/* ??? This is certainly wrong for 64-bit s390x, but given that only KVM
+ emulation actually works, this is good enough for a placeholder. */
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+#ifndef CONFIG_USER_ONLY
+int s390_virtio_hypercall(CPUState *env);
+void kvm_s390_virtio_irq(CPUState *env, int config_change, uint64_t token);
+CPUState *s390_cpu_addr2state(uint16_t cpu_addr);
+#endif
+
+
+#define cpu_init cpu_s390x_init
+#define cpu_exec cpu_s390x_exec
+#define cpu_gen_code cpu_s390x_gen_code
+
+#include "cpu-all.h"
+
+#define EXCP_OPEX 1 /* operation exception (sigill) */
+#define EXCP_SVC 2 /* supervisor call (syscall) */
+#define EXCP_ADDR 5 /* addressing exception */
+#define EXCP_EXECUTE_SVC 0xff00000 /* supervisor call via execute insn */
+
+static inline void cpu_get_tb_cpu_state(CPUState* env, target_ulong *pc,
+ target_ulong *cs_base, int *flags)
+{
+ *pc = env->psw.addr;
+ /* XXX this is correct for user-mode emulation, but needs
+ * the asce register information as well when softmmu
+ * is implemented in the future */
+ *cs_base = 0;
+ *flags = env->psw.mask;
+}
+
+/* Program Status Word. */
+#define S390_PSWM_REGNUM 0
+#define S390_PSWA_REGNUM 1
+/* General Purpose Registers. */
+#define S390_R0_REGNUM 2
+#define S390_R1_REGNUM 3
+#define S390_R2_REGNUM 4
+#define S390_R3_REGNUM 5
+#define S390_R4_REGNUM 6
+#define S390_R5_REGNUM 7
+#define S390_R6_REGNUM 8
+#define S390_R7_REGNUM 9
+#define S390_R8_REGNUM 10
+#define S390_R9_REGNUM 11
+#define S390_R10_REGNUM 12
+#define S390_R11_REGNUM 13
+#define S390_R12_REGNUM 14
+#define S390_R13_REGNUM 15
+#define S390_R14_REGNUM 16
+#define S390_R15_REGNUM 17
+/* Access Registers. */
+#define S390_A0_REGNUM 18
+#define S390_A1_REGNUM 19
+#define S390_A2_REGNUM 20
+#define S390_A3_REGNUM 21
+#define S390_A4_REGNUM 22
+#define S390_A5_REGNUM 23
+#define S390_A6_REGNUM 24
+#define S390_A7_REGNUM 25
+#define S390_A8_REGNUM 26
+#define S390_A9_REGNUM 27
+#define S390_A10_REGNUM 28
+#define S390_A11_REGNUM 29
+#define S390_A12_REGNUM 30
+#define S390_A13_REGNUM 31
+#define S390_A14_REGNUM 32
+#define S390_A15_REGNUM 33
+/* Floating Point Control Word. */
+#define S390_FPC_REGNUM 34
+/* Floating Point Registers. */
+#define S390_F0_REGNUM 35
+#define S390_F1_REGNUM 36
+#define S390_F2_REGNUM 37
+#define S390_F3_REGNUM 38
+#define S390_F4_REGNUM 39
+#define S390_F5_REGNUM 40
+#define S390_F6_REGNUM 41
+#define S390_F7_REGNUM 42
+#define S390_F8_REGNUM 43
+#define S390_F9_REGNUM 44
+#define S390_F10_REGNUM 45
+#define S390_F11_REGNUM 46
+#define S390_F12_REGNUM 47
+#define S390_F13_REGNUM 48
+#define S390_F14_REGNUM 49
+#define S390_F15_REGNUM 50
+/* Total. */
+#define S390_NUM_REGS 51
+
+/* Pseudo registers -- PC and condition code. */
+#define S390_PC_REGNUM S390_NUM_REGS
+#define S390_CC_REGNUM (S390_NUM_REGS+1)
+#define S390_NUM_PSEUDO_REGS 2
+#define S390_NUM_TOTAL_REGS (S390_NUM_REGS+2)
+
+
+
+/* Program Status Word. */
+#define S390_PSWM_REGNUM 0
+#define S390_PSWA_REGNUM 1
+/* General Purpose Registers. */
+#define S390_R0_REGNUM 2
+#define S390_R1_REGNUM 3
+#define S390_R2_REGNUM 4
+#define S390_R3_REGNUM 5
+#define S390_R4_REGNUM 6
+#define S390_R5_REGNUM 7
+#define S390_R6_REGNUM 8
+#define S390_R7_REGNUM 9
+#define S390_R8_REGNUM 10
+#define S390_R9_REGNUM 11
+#define S390_R10_REGNUM 12
+#define S390_R11_REGNUM 13
+#define S390_R12_REGNUM 14
+#define S390_R13_REGNUM 15
+#define S390_R14_REGNUM 16
+#define S390_R15_REGNUM 17
+/* Access Registers. */
+#define S390_A0_REGNUM 18
+#define S390_A1_REGNUM 19
+#define S390_A2_REGNUM 20
+#define S390_A3_REGNUM 21
+#define S390_A4_REGNUM 22
+#define S390_A5_REGNUM 23
+#define S390_A6_REGNUM 24
+#define S390_A7_REGNUM 25
+#define S390_A8_REGNUM 26
+#define S390_A9_REGNUM 27
+#define S390_A10_REGNUM 28
+#define S390_A11_REGNUM 29
+#define S390_A12_REGNUM 30
+#define S390_A13_REGNUM 31
+#define S390_A14_REGNUM 32
+#define S390_A15_REGNUM 33
+/* Floating Point Control Word. */
+#define S390_FPC_REGNUM 34
+/* Floating Point Registers. */
+#define S390_F0_REGNUM 35
+#define S390_F1_REGNUM 36
+#define S390_F2_REGNUM 37
+#define S390_F3_REGNUM 38
+#define S390_F4_REGNUM 39
+#define S390_F5_REGNUM 40
+#define S390_F6_REGNUM 41
+#define S390_F7_REGNUM 42
+#define S390_F8_REGNUM 43
+#define S390_F9_REGNUM 44
+#define S390_F10_REGNUM 45
+#define S390_F11_REGNUM 46
+#define S390_F12_REGNUM 47
+#define S390_F13_REGNUM 48
+#define S390_F14_REGNUM 49
+#define S390_F15_REGNUM 50
+/* Total. */
+#define S390_NUM_REGS 51
+
+/* Pseudo registers -- PC and condition code. */
+#define S390_PC_REGNUM S390_NUM_REGS
+#define S390_CC_REGNUM (S390_NUM_REGS+1)
+#define S390_NUM_PSEUDO_REGS 2
+#define S390_NUM_TOTAL_REGS (S390_NUM_REGS+2)
+
+
+#endif
diff --git a/target-s390x/exec.h b/target-s390x/exec.h
new file mode 100644
index 0000000..bf3f264
--- /dev/null
+++ b/target-s390x/exec.h
@@ -0,0 +1,53 @@
+/*
+ * S/390 execution defines
+ *
+ * Copyright (c) 2009 Ulrich Hecht
+ *
+ * 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/>.
+ */
+
+#include "dyngen-exec.h"
+
+register struct CPUS390XState *env asm(AREG0);
+
+#include "config.h"
+#include "cpu.h"
+#include "exec-all.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+static inline int cpu_has_work(CPUState *env)
+{
+ return env->interrupt_request & CPU_INTERRUPT_HARD; // guess
+}
+
+static inline int cpu_halted(CPUState *env)
+{
+ if (!env->halted) {
+ return 0;
+ }
+ if (cpu_has_work(env)) {
+ env->halted = 0;
+ return 0;
+ }
+ return EXCP_HALTED;
+}
+
+static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock* tb)
+{
+ env->psw.addr = tb->pc;
+}
+
diff --git a/target-s390x/helper.c b/target-s390x/helper.c
new file mode 100644
index 0000000..4a5297b
--- /dev/null
+++ b/target-s390x/helper.c
@@ -0,0 +1,84 @@
+/*
+ * S/390 helpers
+ *
+ * Copyright (c) 2009 Ulrich Hecht
+ *
+ * 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/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "gdbstub.h"
+#include "qemu-common.h"
+
+#include <linux/kvm.h>
+#include "kvm.h"
+
+CPUS390XState *cpu_s390x_init(const char *cpu_model)
+{
+ CPUS390XState *env;
+ static int inited = 0;
+
+ env = qemu_mallocz(sizeof(CPUS390XState));
+ cpu_exec_init(env);
+ if (!inited) {
+ inited = 1;
+ }
+
+ env->cpu_model_str = cpu_model;
+ cpu_reset(env);
+ qemu_init_vcpu(env);
+ return env;
+}
+
+void cpu_reset(CPUS390XState *env)
+{
+ if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+ qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+ log_cpu_state(env, 0);
+ }
+
+ memset(env, 0, offsetof(CPUS390XState, breakpoints));
+ /* FIXME: reset vector? */
+ tlb_flush(env, 1);
+}
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ return 0;
+}
+
+#ifndef CONFIG_USER_ONLY
+
+int cpu_s390x_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ target_ulong phys;
+ int prot;
+
+ /* XXX: implement mmu */
+
+ phys = address;
+ prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+
+ tlb_set_page(env, address & TARGET_PAGE_MASK,
+ phys & TARGET_PAGE_MASK, prot,
+ mmu_idx, TARGET_PAGE_SIZE);
+ return 0;
+}
+#endif /* CONFIG_USER_ONLY */
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
new file mode 100644
index 0000000..38823f5
--- /dev/null
+++ b/target-s390x/kvm.c
@@ -0,0 +1,507 @@
+/*
+ * QEMU S390x KVM implementation
+ *
+ * Copyright (c) 2009 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/>.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/kvm.h>
+#include <asm/ptrace.h>
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "kvm.h"
+#include "cpu.h"
+#include "device_tree.h"
+
+/* #define DEBUG_KVM */
+
+#ifdef DEBUG_KVM
+#define dprintf(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+ do { } while (0)
+#endif
+
+#define IPA0_DIAG 0x8300
+#define IPA0_SIGP 0xae00
+#define IPA0_PRIV 0xb200
+
+#define PRIV_SCLP_CALL 0x20
+#define DIAG_KVM_HYPERCALL 0x500
+#define DIAG_KVM_BREAKPOINT 0x501
+
+#define SCP_LENGTH 0x00
+#define SCP_FUNCTION_CODE 0x02
+#define SCP_CONTROL_MASK 0x03
+#define SCP_RESPONSE_CODE 0x06
+#define SCP_MEM_CODE 0x08
+#define SCP_INCREMENT 0x0a
+
+#define ICPT_INSTRUCTION 0x04
+#define ICPT_WAITPSW 0x1c
+#define ICPT_SOFT_INTERCEPT 0x24
+#define ICPT_CPU_STOP 0x28
+#define ICPT_IO 0x40
+
+#define SIGP_RESTART 0x06
+#define SIGP_INITIAL_CPU_RESET 0x0b
+#define SIGP_STORE_STATUS_ADDR 0x0e
+#define SIGP_SET_ARCH 0x12
+
+#define SCLP_CMDW_READ_SCP_INFO 0x00020001
+#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
+
+const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
+ KVM_CAP_LAST_INFO
+};
+
+int kvm_arch_init(KVMState *s)
+{
+ return 0;
+}
+
+int kvm_arch_init_vcpu(CPUState *env)
+{
+ int ret = 0;
+
+ if (kvm_vcpu_ioctl(env, KVM_S390_INITIAL_RESET, NULL) < 0) {
+ perror("cannot init reset vcpu");
+ }
+
+ return ret;
+}
+
+void kvm_arch_reset_vcpu(CPUState *env)
+{
+ /* FIXME: add code to reset vcpu. */
+}
+
+int kvm_arch_put_registers(CPUState *env, int level)
+{
+ struct kvm_regs regs;
+ int ret;
+ int i;
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (i = 0; i < 16; i++) {
+ regs.gprs[i] = env->regs[i];
+ }
+
+ ret = kvm_vcpu_ioctl(env, KVM_SET_REGS, &regs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ env->kvm_run->psw_addr = env->psw.addr;
+ env->kvm_run->psw_mask = env->psw.mask;
+
+ return ret;
+}
+
+int kvm_arch_get_registers(CPUState *env)
+{
+ int ret;
+ struct kvm_regs regs;
+ int i;
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (i = 0; i < 16; i++) {
+ env->regs[i] = regs.gprs[i];
+ }
+
+ env->psw.addr = env->kvm_run->psw_addr;
+ env->psw.mask = env->kvm_run->psw_mask;
+
+ return 0;
+}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp)
+{
+ static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01};
+
+ if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) ||
+ cpu_memory_rw_debug(env, bp->pc, (uint8_t *)diag_501, 4, 1)) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp)
+{
+ uint8_t t[4];
+ static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01};
+
+ if (cpu_memory_rw_debug(env, bp->pc, t, 4, 0)) {
+ return -EINVAL;
+ } else if (memcmp(t, diag_501, 4)) {
+ return -EINVAL;
+ } else if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int kvm_arch_pre_run(CPUState *env, struct kvm_run *run)
+{
+ return 0;
+}
+
+int kvm_arch_post_run(CPUState *env, struct kvm_run *run)
+{
+ return 0;
+}
+
+int kvm_arch_process_irqchip_events(CPUState *env)
+{
+ return 0;
+}
+
+static void kvm_s390_interrupt_internal(CPUState *env, int type, uint32_t parm,
+ uint64_t parm64, int vm)
+{
+ struct kvm_s390_interrupt kvmint;
+ int r;
+
+ if (!env->kvm_state) {
+ return;
+ }
+
+ env->halted = 0;
+ env->exception_index = -1;
+
+ kvmint.type = type;
+ kvmint.parm = parm;
+ kvmint.parm64 = parm64;
+
+ if (vm) {
+ r = kvm_vm_ioctl(env->kvm_state, KVM_S390_INTERRUPT, &kvmint);
+ } else {
+ r = kvm_vcpu_ioctl(env, KVM_S390_INTERRUPT, &kvmint);
+ }
+
+ if (r < 0) {
+ fprintf(stderr, "KVM failed to inject interrupt\n");
+ exit(1);
+ }
+}
+
+void kvm_s390_virtio_irq(CPUState *env, int config_change, uint64_t token)
+{
+ kvm_s390_interrupt_internal(env, KVM_S390_INT_VIRTIO, config_change,
+ token, 1);
+}
+
+static void kvm_s390_interrupt(CPUState *env, int type, uint32_t code)
+{
+ kvm_s390_interrupt_internal(env, type, code, 0, 0);
+}
+
+static void enter_pgmcheck(CPUState *env, uint16_t code)
+{
+ kvm_s390_interrupt(env, KVM_S390_PROGRAM_INT, code);
+}
+
+static void setcc(CPUState *env, uint64_t cc)
+{
+ env->kvm_run->psw_mask &= ~(3ul << 44);
+ env->kvm_run->psw_mask |= (cc & 3) << 44;
+
+ env->psw.mask &= ~(3ul << 44);
+ env->psw.mask |= (cc & 3) << 44;
+}
+
+static int sclp_service_call(CPUState *env, struct kvm_run *run, uint16_t ipbh0)
+{
+ uint32_t sccb;
+ uint64_t code;
+ int r = 0;
+
+ cpu_synchronize_state(env);
+ sccb = env->regs[ipbh0 & 0xf];
+ code = env->regs[(ipbh0 & 0xf0) >> 4];
+
+ dprintf("sclp(0x%x, 0x%lx)\n", sccb, code);
+
+ if (sccb & ~0x7ffffff8ul) {
+ fprintf(stderr, "KVM: invalid sccb address 0x%x\n", sccb);
+ r = -1;
+ goto out;
+ }
+
+ switch(code) {
+ case SCLP_CMDW_READ_SCP_INFO:
+ case SCLP_CMDW_READ_SCP_INFO_FORCED:
+ stw_phys(sccb + SCP_MEM_CODE, ram_size >> 20);
+ stb_phys(sccb + SCP_INCREMENT, 1);
+ stw_phys(sccb + SCP_RESPONSE_CODE, 0x10);
+ setcc(env, 0);
+
+ kvm_s390_interrupt_internal(env, KVM_S390_INT_SERVICE,
+ sccb & ~3, 0, 1);
+ break;
+ default:
+ dprintf("KVM: invalid sclp call 0x%x / 0x%lx\n", sccb, code);
+ r = -1;
+ break;
+ }
+
+out:
+ if (r < 0) {
+ setcc(env, 3);
+ }
+ return 0;
+}
+
+static int handle_priv(CPUState *env, struct kvm_run *run, uint8_t ipa1)
+{
+ int r = 0;
+ uint16_t ipbh0 = (run->s390_sieic.ipb & 0xffff0000) >> 16;
+
+ dprintf("KVM: PRIV: %d\n", ipa1);
+ switch (ipa1) {
+ case PRIV_SCLP_CALL:
+ r = sclp_service_call(env, run, ipbh0);
+ break;
+ default:
+ dprintf("KVM: unknown PRIV: 0x%x\n", ipa1);
+ r = -1;
+ break;
+ }
+
+ return r;
+}
+
+static int handle_hypercall(CPUState *env, struct kvm_run *run)
+{
+ int r;
+
+ cpu_synchronize_state(env);
+ r = s390_virtio_hypercall(env);
+
+ return r;
+}
+
+static int handle_diag(CPUState *env, struct kvm_run *run, int ipb_code)
+{
+ int r = 0;
+
+ switch (ipb_code) {
+ case DIAG_KVM_HYPERCALL:
+ r = handle_hypercall(env, run);
+ break;
+ case DIAG_KVM_BREAKPOINT:
+ sleep(10);
+ break;
+ default:
+ dprintf("KVM: unknown DIAG: 0x%x\n", ipb_code);
+ r = -1;
+ break;
+ }
+
+ return r;
+}
+
+static int s390_cpu_restart(CPUState *env)
+{
+ kvm_s390_interrupt(env, KVM_S390_RESTART, 0);
+ env->halted = 0;
+ env->exception_index = -1;
+ qemu_cpu_kick(env);
+ dprintf("DONE: SIGP cpu restart: %p\n", env);
+ return 0;
+}
+
+static int s390_store_status(CPUState *env, uint32_t parameter)
+{
+ /* XXX */
+ fprintf(stderr, "XXX SIGP store status\n");
+ return -1;
+}
+
+static int s390_cpu_initial_reset(CPUState *env)
+{
+ int i;
+
+ if (kvm_vcpu_ioctl(env, KVM_S390_INITIAL_RESET, NULL) < 0) {
+ perror("cannot init reset vcpu");
+ }
+
+ /* Manually zero out all registers */
+ cpu_synchronize_state(env);
+ for (i = 0; i < 16; i++) {
+ env->regs[i] = 0;
+ }
+
+ dprintf("DONE: SIGP initial reset: %p\n", env);
+ return 0;
+}
+
+static int handle_sigp(CPUState *env, struct kvm_run *run, uint8_t ipa1)
+{
+ uint8_t order_code;
+ uint32_t parameter;
+ uint16_t cpu_addr;
+ uint8_t t;
+ int r = -1;
+ CPUState *target_env;
+
+ cpu_synchronize_state(env);
+
+ /* get order code */
+ order_code = run->s390_sieic.ipb >> 28;
+ if (order_code > 0) {
+ order_code = env->regs[order_code];
+ }
+ order_code += (run->s390_sieic.ipb & 0x0fff0000) >> 16;
+
+ /* get parameters */
+ t = (ipa1 & 0xf0) >> 4;
+ if (!(t % 2)) {
+ t++;
+ }
+
+ parameter = env->regs[t] & 0x7ffffe00;
+ cpu_addr = env->regs[ipa1 & 0x0f];
+
+ target_env = s390_cpu_addr2state(cpu_addr);
+ if (!target_env) {
+ goto out;
+ }
+
+ switch (order_code) {
+ case SIGP_RESTART:
+ r = s390_cpu_restart(target_env);
+ break;
+ case SIGP_STORE_STATUS_ADDR:
+ r = s390_store_status(target_env, parameter);
+ break;
+ case SIGP_SET_ARCH:
+ /* make the caller panic */
+ return -1;
+ case SIGP_INITIAL_CPU_RESET:
+ r = s390_cpu_initial_reset(target_env);
+ break;
+ default:
+ fprintf(stderr, "KVM: unknown SIGP: 0x%x\n", ipa1);
+ break;
+ }
+
+out:
+ setcc(env, r ? 3 : 0);
+ return 0;
+}
+
+static int handle_instruction(CPUState *env, struct kvm_run *run)
+{
+ unsigned int ipa0 = (run->s390_sieic.ipa & 0xff00);
+ uint8_t ipa1 = run->s390_sieic.ipa & 0x00ff;
+ int ipb_code = (run->s390_sieic.ipb & 0x0fff0000) >> 16;
+ int r = -1;
+
+ dprintf("handle_instruction 0x%x 0x%x\n", run->s390_sieic.ipa, run->s390_sieic.ipb);
+ switch (ipa0) {
+ case IPA0_PRIV:
+ r = handle_priv(env, run, ipa1);
+ break;
+ case IPA0_DIAG:
+ r = handle_diag(env, run, ipb_code);
+ break;
+ case IPA0_SIGP:
+ r = handle_sigp(env, run, ipa1);
+ break;
+ }
+
+ if (r < 0) {
+ enter_pgmcheck(env, 0x0001);
+ }
+ return r;
+}
+
+static int handle_intercept(CPUState *env)
+{
+ struct kvm_run *run = env->kvm_run;
+ int icpt_code = run->s390_sieic.icptcode;
+ int r = 0;
+
+ dprintf("intercept: 0x%x (at 0x%lx)\n", icpt_code, env->kvm_run->psw_addr);
+ switch (icpt_code) {
+ case ICPT_INSTRUCTION:
+ r = handle_instruction(env, run);
+ break;
+ case ICPT_WAITPSW:
+ /* XXX What to do on system shutdown? */
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ break;
+ case ICPT_SOFT_INTERCEPT:
+ fprintf(stderr, "KVM unimplemented icpt SOFT\n");
+ exit(1);
+ break;
+ case ICPT_CPU_STOP:
+ qemu_system_shutdown_request();
+ break;
+ case ICPT_IO:
+ fprintf(stderr, "KVM unimplemented icpt IO\n");
+ exit(1);
+ break;
+ default:
+ fprintf(stderr, "Unknown intercept code: %d\n", icpt_code);
+ exit(1);
+ break;
+ }
+
+ return r;
+}
+
+int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run)
+{
+ int ret = 0;
+
+ switch (run->exit_reason) {
+ case KVM_EXIT_S390_SIEIC:
+ ret = handle_intercept(env);
+ break;
+ case KVM_EXIT_S390_RESET:
+ fprintf(stderr, "RESET not implemented\n");
+ exit(1);
+ break;
+ default:
+ fprintf(stderr, "Unknown KVM exit: %d\n", run->exit_reason);
+ break;
+ }
+
+ return ret;
+}
+
+bool kvm_arch_stop_on_emulation_error(CPUState *env)
+{
+ return true;
+}
diff --git a/target-s390x/machine.c b/target-s390x/machine.c
new file mode 100644
index 0000000..3e79be6
--- /dev/null
+++ b/target-s390x/machine.c
@@ -0,0 +1,30 @@
+/*
+ * QEMU S390x machine definitions
+ *
+ * Copyright (c) 2009 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/>.
+ */
+
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ return 0;
+}
diff --git a/target-s390x/op_helper.c b/target-s390x/op_helper.c
new file mode 100644
index 0000000..402df2d
--- /dev/null
+++ b/target-s390x/op_helper.c
@@ -0,0 +1,73 @@
+/*
+ * S/390 helper routines
+ *
+ * Copyright (c) 2009 Alexander Graf
+ *
+ * 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/>.
+ */
+
+#include "exec.h"
+
+/*****************************************************************************/
+/* Softmmu support */
+#if !defined (CONFIG_USER_ONLY)
+
+#define MMUSUFFIX _mmu
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+/* try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
+{
+ TranslationBlock *tb;
+ CPUState *saved_env;
+ unsigned long pc;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+ ret = cpu_s390x_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+ if (unlikely(ret != 0)) {
+ if (likely(retaddr)) {
+ /* now we have a real cpu fault */
+ pc = (unsigned long)retaddr;
+ tb = tb_find_pc(pc);
+ if (likely(tb)) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, NULL);
+ }
+ }
+ /* XXX */
+ /* helper_raise_exception_err(env->exception_index, env->error_code); */
+ }
+ env = saved_env;
+}
+
+#endif
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
new file mode 100644
index 0000000..d33bfb1
--- /dev/null
+++ b/target-s390x/translate.c
@@ -0,0 +1,61 @@
+/*
+ * S/390 translation
+ *
+ * Copyright (c) 2009 Ulrich Hecht
+ *
+ * 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/>.
+ */
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "tcg-op.h"
+#include "qemu-log.h"
+
+void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
+ int flags)
+{
+ int i;
+ for (i = 0; i < 16; i++) {
+ cpu_fprintf(f, "R%02d=%016lx", i, env->regs[i]);
+ if ((i % 4) == 3) {
+ cpu_fprintf(f, "\n");
+ } else {
+ cpu_fprintf(f, " ");
+ }
+ }
+ for (i = 0; i < 16; i++) {
+ cpu_fprintf(f, "F%02d=%016lx", i, (long)env->fregs[i].i);
+ if ((i % 4) == 3) {
+ cpu_fprintf(f, "\n");
+ } else {
+ cpu_fprintf(f, " ");
+ }
+ }
+ cpu_fprintf(f, "PSW=mask %016lx addr %016lx cc %02x\n", env->psw.mask, env->psw.addr, env->cc);
+}
+
+void gen_intermediate_code (CPUState *env, struct TranslationBlock *tb)
+{
+}
+
+void gen_intermediate_code_pc (CPUState *env, struct TranslationBlock *tb)
+{
+}
+
+void gen_pc_load(CPUState *env, TranslationBlock *tb,
+ unsigned long searched_pc, int pc_pos, void *puc)
+{
+ env->psw.addr = gen_opc_pc[pc_pos];
+}