diff options
author | Robert Mustacchi <rm@joyent.com> | 2011-06-24 13:49:54 -0700 |
---|---|---|
committer | Robert Mustacchi <rm@joyent.com> | 2011-06-24 13:49:54 -0700 |
commit | 68396ea9c0fe4f75ce30b1eba2c44c43c13344bb (patch) | |
tree | 802587d411d9db461e6500c5b635043315f81c27 /target-s390x | |
download | illumos-kvm-cmd-68396ea9c0fe4f75ce30b1eba2c44c43c13344bb.tar.gz |
Initial commit of d32e8d0b8d9e0ef7cf7ab2e74548982972789dfc from qemu-kvm
Diffstat (limited to 'target-s390x')
-rw-r--r-- | target-s390x/cpu.h | 269 | ||||
-rw-r--r-- | target-s390x/exec.h | 53 | ||||
-rw-r--r-- | target-s390x/helper.c | 84 | ||||
-rw-r--r-- | target-s390x/kvm.c | 507 | ||||
-rw-r--r-- | target-s390x/machine.c | 30 | ||||
-rw-r--r-- | target-s390x/op_helper.c | 73 | ||||
-rw-r--r-- | target-s390x/translate.c | 61 |
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, ®s); + 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, ®s); + 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, ®s); + 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]; +} |