[Spice-commits] 156 commits - Makefile Makefile.objs Makefile.target audio/mixeng_template.h block.c block.h block/qed.c block/qed.h block_int.h blockdev.c blockdev.h configure console.c cpu-all.h cpu-common.h cpu-exec.c default-configs/pci.mak default-configs/s390x-linux-user.mak docs/usb2.txt exec-all.h exec.c fpu/softfloat-native.c fpu/softfloat-native.h fpu/softfloat-specialize.h fpu/softfloat.c fpu/softfloat.h gdbstub.c hw/9pfs hw/acpi_piix4.c hw/bitbang_i2c.c hw/bt-hid.c hw/esp.c hw/ide hw/lsi53c895a.c hw/multiboot.c hw/pci_ids.h hw/pflash_cfi02.c hw/piix_pci.c hw/ppce500_mpc8544ds.c hw/realview.c hw/s390-virtio-bus.c hw/s390-virtio-bus.h hw/s390-virtio.c hw/scsi-bus.c hw/scsi-disk.c hw/scsi-generic.c hw/scsi.h hw/sd.c hw/spapr_hcall.c hw/spapr_llan.c hw/spapr_vscsi.c hw/usb-bt.c hw/usb-ccid.c hw/usb-desc.c hw/usb-desc.h hw/usb-ehci.c hw/usb-hid.c hw/usb-hub.c hw/usb-msd.c hw/usb-musb.c hw/usb-net.c hw/usb-ohci.c hw/usb-serial.c hw/usb-uhci.c hw/usb-wacom.c hw/usb.c hw/usb.h h w/virtio-console.c hw/virtio-serial-bus.c hw/virtio-serial.h hw/xen_devconfig.c hw/xen_disk.c linux-user/elfload.c linux-user/main.c linux-user/s390x linux-user/signal.c linux-user/syscall.c linux-user/syscall_defs.h pc-bios/s390-zipl.rom posix-aio-compat.c qemu-common.h qemu-img.c qemu-progress.c qemu-tool.c qmp-commands.hx scripts/qemu-binfmt-conf.sh target-arm/exec.h target-arm/helper.c target-arm/helper.h target-arm/neon_helper.c target-arm/translate.c target-i386/cpu.h target-i386/exec.h target-i386/helper.c target-i386/machine.c target-i386/op_helper.c target-ppc/STATUS target-ppc/cpu.h target-ppc/helper.c target-ppc/helper.h target-ppc/kvm.c target-ppc/op_helper.c target-ppc/translate.c target-ppc/translate_init.c target-s390x/cpu.h target-s390x/helper.c target-s390x/helpers.h target-s390x/kvm.c target-s390x/op_helper.c target-s390x/translate.c target-sparc/helper.h target-sparc/op_helper.c target-sparc/translate.c tcg/mips tcg/tcg-op.h tcg/tcg.c tcg/tcg.h trace-event s usb-bsd.c usb-linux.c user-exec.c vl.c
Gerd Hoffmann
kraxel at kemper.freedesktop.org
Mon Jun 6 05:44:55 PDT 2011
Makefile | 1
Makefile.objs | 1
Makefile.target | 13
audio/mixeng_template.h | 4
block.c | 32
block.h | 5
block/qed.c | 126
block/qed.h | 7
block_int.h | 1
blockdev.c | 5
blockdev.h | 1
configure | 9
console.c | 2
cpu-all.h | 7
cpu-common.h | 1
cpu-exec.c | 631 ----
default-configs/pci.mak | 1
default-configs/s390x-linux-user.mak | 1
docs/usb2.txt | 38
exec-all.h | 5
exec.c | 29
fpu/softfloat-native.c | 540 ---
fpu/softfloat-native.h | 531 ---
fpu/softfloat-specialize.h | 7
fpu/softfloat.c | 103
fpu/softfloat.h | 79
gdbstub.c | 12
hw/9pfs/virtio-9p.c | 1
hw/acpi_piix4.c | 4
hw/bitbang_i2c.c | 5
hw/bt-hid.c | 6
hw/esp.c | 119
hw/ide/ahci.c | 35
hw/ide/core.c | 10
hw/ide/internal.h | 2
hw/ide/pci.c | 8
hw/ide/qdev.c | 81
hw/lsi53c895a.c | 200 -
hw/multiboot.c | 2
hw/pci_ids.h | 1
hw/pflash_cfi02.c | 2
hw/piix_pci.c | 2
hw/ppce500_mpc8544ds.c | 2
hw/realview.c | 1
hw/s390-virtio-bus.c | 3
hw/s390-virtio-bus.h | 2
hw/s390-virtio.c | 20
hw/scsi-bus.c | 224 +
hw/scsi-disk.c | 408 +-
hw/scsi-generic.c | 223 -
hw/scsi.h | 91
hw/sd.c | 11
hw/spapr_hcall.c | 7
hw/spapr_llan.c | 3
hw/spapr_vscsi.c | 187 -
hw/usb-bt.c | 6
hw/usb-ccid.c | 4
hw/usb-desc.c | 56
hw/usb-desc.h | 24
hw/usb-ehci.c | 2037 +++++++++++++
hw/usb-hid.c | 9
hw/usb-hub.c | 9
hw/usb-msd.c | 140
hw/usb-musb.c | 2
hw/usb-net.c | 6
hw/usb-ohci.c | 4
hw/usb-serial.c | 7
hw/usb-uhci.c | 6
hw/usb-wacom.c | 7
hw/usb.c | 101
hw/usb.h | 40
hw/virtio-console.c | 47
hw/virtio-serial-bus.c | 83
hw/virtio-serial.h | 11
hw/xen_devconfig.c | 2
hw/xen_disk.c | 4
linux-user/elfload.c | 19
linux-user/main.c | 105
linux-user/s390x/syscall.h | 23
linux-user/s390x/syscall_nr.h | 349 ++
linux-user/s390x/target_signal.h | 26
linux-user/s390x/termbits.h | 283 +
linux-user/signal.c | 333 ++
linux-user/syscall.c | 16
linux-user/syscall_defs.h | 55
pc-bios/s390-zipl.rom |binary
posix-aio-compat.c | 6
qemu-common.h | 2
qemu-img.c | 35
qemu-progress.c | 24
qemu-tool.c | 25
qmp-commands.hx | 11
scripts/qemu-binfmt-conf.sh | 4
target-arm/exec.h | 2
target-arm/helper.c | 159 -
target-arm/helper.h | 60
target-arm/neon_helper.c | 40
target-arm/translate.c | 148
target-i386/cpu.h | 22
target-i386/exec.h | 132
target-i386/helper.c | 5
target-i386/machine.c | 101
target-i386/op_helper.c | 273 -
target-ppc/STATUS | 2
target-ppc/cpu.h | 4
target-ppc/helper.c | 9
target-ppc/helper.h | 2
target-ppc/kvm.c | 10
target-ppc/op_helper.c | 12
target-ppc/translate.c | 2
target-ppc/translate_init.c | 12
target-s390x/cpu.h | 28
target-s390x/helper.c | 564 +++
target-s390x/helpers.h | 151 +
target-s390x/kvm.c | 48
target-s390x/op_helper.c | 2917 +++++++++++++++++++
target-s390x/translate.c | 5190 ++++++++++++++++++++++++++++++++++-
target-sparc/helper.h | 1
target-sparc/op_helper.c | 6
target-sparc/translate.c | 2
tcg/mips/tcg-target.h | 4
tcg/tcg-op.h | 48
tcg/tcg.c | 109
tcg/tcg.h | 58
trace-events | 12
usb-bsd.c | 1
usb-linux.c | 444 --
user-exec.c | 673 ++++
vl.c | 3
129 files changed, 15115 insertions(+), 3912 deletions(-)
New commits:
commit d800040fb47fe4500d1f8bf604b9fd129bda9419
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Fri Jun 3 14:57:06 2011 +0200
scsi: fix tracing of scsi requests with simple backend
The simple backend only supports a maximum of 6 arguments. Split the
scsi_req_parsed event in two parts to cope with the limit.
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Signed-off-by: Blue Swirl <blauwirbel at gmail.com>
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 837f24e..ad6a730 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -413,7 +413,11 @@ int scsi_req_parse(SCSIRequest *req, uint8_t *buf)
scsi_req_xfer_mode(req);
req->cmd.lba = scsi_req_lba(req);
trace_scsi_req_parsed(req->dev->id, req->lun, req->tag, buf[0],
- req->cmd.mode, req->cmd.xfer, req->cmd.lba);
+ req->cmd.mode, req->cmd.xfer);
+ if (req->cmd.lba != -1) {
+ trace_scsi_req_parsed_lba(req->dev->id, req->lun, req->tag, buf[0],
+ req->cmd.lba);
+ }
return 0;
}
diff --git a/trace-events b/trace-events
index 3137a15..e0e9574 100644
--- a/trace-events
+++ b/trace-events
@@ -210,7 +210,8 @@ disable scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d"
disable scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d"
disable scsi_req_dequeue(int target, int lun, int tag) "target %d lun %d tag %d"
disable scsi_req_continue(int target, int lun, int tag) "target %d lun %d tag %d"
-disable scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer, uint64_t lba) "target %d lun %d tag %d command %d dir %d length %d lba %"PRIu64""
+disable scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer) "target %d lun %d tag %d command %d dir %d length %d"
+disable scsi_req_parsed_lba(int target, int lun, int tag, int cmd, uint64_t lba) "target %d lun %d tag %d command %d lba %"PRIu64""
disable scsi_req_parse_bad(int target, int lun, int tag, int cmd) "target %d lun %d tag %d command %d"
# vl.c
commit f9188227a455446b5c10a8f5114f266001c1c801
Author: Mike Frysinger <vapier at gentoo.org>
Date: Tue May 17 17:08:43 2011 -0400
configure: check for -Wendif-labels support
Older gcc compilers do not support -Wendif-labels, so move it from the
hardcoded list to the dynamically detected list.
Signed-off-by: Mike Frysinger <vapier at gentoo.org>
Reviewed-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/configure b/configure
index 0c26a26..d38b952 100755
--- a/configure
+++ b/configure
@@ -229,7 +229,7 @@ sdl_config="${cross_prefix}${SDL_CONFIG-sdl-config}"
# default flags for all hosts
QEMU_CFLAGS="-fno-strict-aliasing $QEMU_CFLAGS"
CFLAGS="-g $CFLAGS"
-QEMU_CFLAGS="-Wall -Wundef -Wendif-labels -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS"
+QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS"
QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS"
QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS"
QEMU_CFLAGS="-D_FORTIFY_SOURCE=2 $QEMU_CFLAGS"
@@ -1037,7 +1037,7 @@ fi
gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits"
gcc_flags="-Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers $gcc_flags"
gcc_flags="-Wmissing-include-dirs -Wempty-body -Wnested-externs $gcc_flags"
-gcc_flags="-fstack-protector-all $gcc_flags"
+gcc_flags="-fstack-protector-all -Wendif-labels $gcc_flags"
cat > $TMPC << EOF
int main(void) { return 0; }
EOF
commit 9694b5d11aad2f37c91fcb5c66644675597056a2
Author: Stefan Weil <weil at mail.berlios.de>
Date: Sat May 7 22:23:49 2011 +0200
virtio-9p: Remove statement without effect (fix warning from cppcheck)
cppcheck report:
virtio-9p.c:197: warning: Redundant assignment of "flags" to itself
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Reviewed-by: Venkateswararao Jujjuri <jvrao at linux.vnet.ibm.com>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c
index b5fc52b..a8a0a97 100644
--- a/hw/9pfs/virtio-9p.c
+++ b/hw/9pfs/virtio-9p.c
@@ -194,7 +194,6 @@ static int v9fs_do_open2(V9fsState *s, char *fullname, uid_t uid, gid_t gid,
cred.fc_uid = uid;
cred.fc_gid = gid;
cred.fc_mode = mode & 07777;
- flags = flags;
return s->ops->open2(&s->ctx, fullname, flags, &cred);
}
commit 1f2e98b62d62205de9d52e81aca78e78712af973
Author: Alex Williamson <alex.williamson at redhat.com>
Date: Tue May 3 12:48:09 2011 -0600
exec: Implement qemu_ram_free_from_ptr()
Required for regions mapped via qemu_ram_alloc_from_ptr(). VFIO
and ivshmem will make use of this to remove mappings when devices
are hot unplugged.
Signed-off-by: Alex Williamson <alex.williamson at redhat.com>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/cpu-common.h b/cpu-common.h
index 151c32c..9f59172 100644
--- a/cpu-common.h
+++ b/cpu-common.h
@@ -61,6 +61,7 @@ ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name,
ram_addr_t size, void *host);
ram_addr_t qemu_ram_alloc(DeviceState *dev, const char *name, ram_addr_t size);
void qemu_ram_free(ram_addr_t addr);
+void qemu_ram_free_from_ptr(ram_addr_t addr);
void qemu_ram_remap(ram_addr_t addr, ram_addr_t length);
/* This should only be used for ram local to a device. */
void *qemu_get_ram_ptr(ram_addr_t addr);
diff --git a/exec.c b/exec.c
index 8529390..6f339ef 100644
--- a/exec.c
+++ b/exec.c
@@ -2952,6 +2952,19 @@ ram_addr_t qemu_ram_alloc(DeviceState *dev, const char *name, ram_addr_t size)
return qemu_ram_alloc_from_ptr(dev, name, size, NULL);
}
+void qemu_ram_free_from_ptr(ram_addr_t addr)
+{
+ RAMBlock *block;
+
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ if (addr == block->offset) {
+ QLIST_REMOVE(block, next);
+ qemu_free(block);
+ return;
+ }
+ }
+}
+
void qemu_ram_free(ram_addr_t addr)
{
RAMBlock *block;
commit c83066d4c4a13d687d60f1f18c748f934b5a5be6
Author: Arun Thomas <arun.thomas at gmail.com>
Date: Thu Apr 28 16:11:11 2011 +0200
multiboot: set boot_device to first partition
The multiboot info struct's 'boot_device' field has 'part1' set to 0x01, which
maps to the second primary partition. To specify the first primary partition,
'part1' should be set to 0x00, since partition numbers start from zero
according to the multiboot spec.
Signed-off-by: Arun Thomas <arun.thomas at gmail.com>
Reviewed-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/hw/multiboot.c b/hw/multiboot.c
index 394ed01..6e6cfb9 100644
--- a/hw/multiboot.c
+++ b/hw/multiboot.c
@@ -307,7 +307,7 @@ int load_multiboot(void *fw_cfg,
| MULTIBOOT_FLAGS_MMAP);
stl_p(bootinfo + MBI_MEM_LOWER, 640);
stl_p(bootinfo + MBI_MEM_UPPER, (ram_size / 1024) - 1024);
- stl_p(bootinfo + MBI_BOOT_DEVICE, 0x8001ffff); /* XXX: use the -boot switch? */
+ stl_p(bootinfo + MBI_BOOT_DEVICE, 0x8000ffff); /* XXX: use the -boot switch? */
stl_p(bootinfo + MBI_MMAP_ADDR, ADDR_E820_MAP);
mb_debug("multiboot: mh_entry_addr = %#x\n", mh_entry_addr);
commit 81c05daf08d1d5033771f7acaef5f19840bf9efa
Author: Alex Zuepke <azuepke at sysgo.com>
Date: Fri Jun 3 18:42:17 2011 +0200
target-arm: BKPT instructions should raise prefetch aborts with IFSR type 00010
Signed-off-by: Alex Zuepke <azuepke at sysgo.com>
Reviewed-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 1cc492d..1208416 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -848,6 +848,7 @@ void do_interrupt(CPUARMState *env)
return;
}
}
+ env->cp15.c5_insn = 2;
/* Fall through to prefetch abort. */
case EXCP_PREFETCH_ABORT:
new_mode = ARM_CPU_MODE_ABT;
commit 568fffe3537c6e98ba853a0769eb19954abc5f86
Author: Christophe Fergeau <cfergeau at redhat.com>
Date: Tue May 31 09:53:48 2011 +0200
tcg: Fix unused-but-set-variable warning
Based on a patch from Hans de Goede <hdegoede at redhat.com>
This warning is new in gcc 4.6.
Acked-by: Amit Shah <amit.shah at redhat.com>
Signed-off-by: Christophe Fergeau <cfergeau at redhat.com>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/tcg/tcg.c b/tcg/tcg.c
index fad92f9..184c208 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -585,7 +585,7 @@ void tcg_register_helper(void *func, const char *name)
void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags,
int sizemask, TCGArg ret, int nargs, TCGArg *args)
{
-#ifdef TCG_TARGET_I386
+#if defined(TCG_TARGET_I386) && TCG_TARGET_REG_BITS < 64
int call_type;
#endif
int i;
@@ -612,7 +612,7 @@ void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags,
*gen_opc_ptr++ = INDEX_op_call;
nparam = gen_opparam_ptr++;
-#ifdef TCG_TARGET_I386
+#if defined(TCG_TARGET_I386) && TCG_TARGET_REG_BITS < 64
call_type = (flags & TCG_CALL_TYPE_MASK);
#endif
if (ret != TCG_CALL_DUMMY_ARG) {
commit ebecf36381ad70a5490794404f23b2802840fdf6
Author: Peter Maydell <peter.maydell at linaro.org>
Date: Fri May 27 13:12:13 2011 +0100
tcg: If DEBUG_TCGV, distinguish TCGv_ptr from TCGv_i32/TCGv_i64
When compiling with DEBUG_TCGV enabled, make the TCGv_ptr type distinct
from TCGv_i32/TCGv_i64. This means that using an i32 or i64 TCG op to
manipulate a TCGv_ptr will always be detected at compile time, rather
than only if compiling on a host system with the other word size.
NB: the tcg_add_ptr and tcg_sub_ptr macros have been removed as they
were not used anywhere.
Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h
index 6529655..ebf5e13 100644
--- a/tcg/tcg-op.h
+++ b/tcg/tcg-op.h
@@ -2304,8 +2304,8 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index)
#endif
}
-#define tcg_gen_ld_ptr tcg_gen_ld_i32
-#define tcg_gen_discard_ptr tcg_gen_discard_i32
+#define tcg_gen_ld_ptr(R, A, O) tcg_gen_ld_i32(TCGV_PTR_TO_NAT(R), (A), (O))
+#define tcg_gen_discard_ptr(A) tcg_gen_discard_i32(TCGV_PTR_TO_NAT(A))
#else /* TCG_TARGET_REG_BITS == 32 */
@@ -2372,8 +2372,8 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index)
tcg_gen_qemu_ldst_op_i64(INDEX_op_qemu_st64, arg, addr, mem_index);
}
-#define tcg_gen_ld_ptr tcg_gen_ld_i64
-#define tcg_gen_discard_ptr tcg_gen_discard_i64
+#define tcg_gen_ld_ptr(R, A, O) tcg_gen_ld_i64(TCGV_PTR_TO_NAT(R), (A), (O))
+#define tcg_gen_discard_ptr(A) tcg_gen_discard_i64(TCGV_PTR_TO_NAT(A))
#endif /* TCG_TARGET_REG_BITS != 32 */
@@ -2523,11 +2523,17 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index)
#endif
#if TCG_TARGET_REG_BITS == 32
-#define tcg_gen_add_ptr tcg_gen_add_i32
-#define tcg_gen_addi_ptr tcg_gen_addi_i32
-#define tcg_gen_ext_i32_ptr tcg_gen_mov_i32
+#define tcg_gen_add_ptr(R, A, B) tcg_gen_add_i32(TCGV_PTR_TO_NAT(R), \
+ TCGV_PTR_TO_NAT(A), \
+ TCGV_PTR_TO_NAT(B))
+#define tcg_gen_addi_ptr(R, A, B) tcg_gen_addi_i32(TCGV_PTR_TO_NAT(R), \
+ TCGV_PTR_TO_NAT(A), (B))
+#define tcg_gen_ext_i32_ptr(R, A) tcg_gen_mov_i32(TCGV_PTR_TO_NAT(R), (A))
#else /* TCG_TARGET_REG_BITS == 32 */
-#define tcg_gen_add_ptr tcg_gen_add_i64
-#define tcg_gen_addi_ptr tcg_gen_addi_i64
-#define tcg_gen_ext_i32_ptr tcg_gen_ext_i32_i64
+#define tcg_gen_add_ptr(R, A, B) tcg_gen_add_i64(TCGV_PTR_TO_NAT(R), \
+ TCGV_PTR_TO_NAT(A), \
+ TCGV_PTR_TO_NAT(B))
+#define tcg_gen_addi_ptr(R, A, B) tcg_gen_addi_i64(TCGV_PTR_TO_NAT(R), \
+ TCGV_PTR_TO_NAT(A), (B))
+#define tcg_gen_ext_i32_ptr(R, A) tcg_gen_ext_i32_i64(TCGV_PTR_TO_NAT(R), (A))
#endif /* TCG_TARGET_REG_BITS != 32 */
diff --git a/tcg/tcg.h b/tcg/tcg.h
index 746378a..3647390 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -150,12 +150,19 @@ typedef struct
int i64;
} TCGv_i64;
+typedef struct {
+ int iptr;
+} TCGv_ptr;
+
#define MAKE_TCGV_I32(i) __extension__ \
({ TCGv_i32 make_tcgv_tmp = {i}; make_tcgv_tmp;})
#define MAKE_TCGV_I64(i) __extension__ \
({ TCGv_i64 make_tcgv_tmp = {i}; make_tcgv_tmp;})
+#define MAKE_TCGV_PTR(i) __extension__ \
+ ({ TCGv_ptr make_tcgv_tmp = {i}; make_tcgv_tmp; })
#define GET_TCGV_I32(t) ((t).i32)
#define GET_TCGV_I64(t) ((t).i64)
+#define GET_TCGV_PTR(t) ((t).iptr)
#if TCG_TARGET_REG_BITS == 32
#define TCGV_LOW(t) MAKE_TCGV_I32(GET_TCGV_I64(t))
#define TCGV_HIGH(t) MAKE_TCGV_I32(GET_TCGV_I64(t) + 1)
@@ -165,10 +172,17 @@ typedef struct
typedef int TCGv_i32;
typedef int TCGv_i64;
+#if TCG_TARGET_REG_BITS == 32
+#define TCGv_ptr TCGv_i32
+#else
+#define TCGv_ptr TCGv_i64
+#endif
#define MAKE_TCGV_I32(x) (x)
#define MAKE_TCGV_I64(x) (x)
+#define MAKE_TCGV_PTR(x) (x)
#define GET_TCGV_I32(t) (t)
#define GET_TCGV_I64(t) (t)
+#define GET_TCGV_PTR(t) (t)
#if TCG_TARGET_REG_BITS == 32
#define TCGV_LOW(t) (t)
@@ -459,25 +473,27 @@ do {\
void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs);
#if TCG_TARGET_REG_BITS == 32
-#define tcg_const_ptr tcg_const_i32
-#define tcg_add_ptr tcg_add_i32
-#define tcg_sub_ptr tcg_sub_i32
-#define TCGv_ptr TCGv_i32
-#define GET_TCGV_PTR GET_TCGV_I32
-#define tcg_global_reg_new_ptr tcg_global_reg_new_i32
-#define tcg_global_mem_new_ptr tcg_global_mem_new_i32
-#define tcg_temp_new_ptr tcg_temp_new_i32
-#define tcg_temp_free_ptr tcg_temp_free_i32
+#define TCGV_NAT_TO_PTR(n) MAKE_TCGV_PTR(GET_TCGV_I32(n))
+#define TCGV_PTR_TO_NAT(n) MAKE_TCGV_I32(GET_TCGV_PTR(n))
+
+#define tcg_const_ptr(V) TCGV_NAT_TO_PTR(tcg_const_i32(V))
+#define tcg_global_reg_new_ptr(R, N) \
+ TCGV_NAT_TO_PTR(tcg_global_reg_new_i32((R), (N)))
+#define tcg_global_mem_new_ptr(R, O, N) \
+ TCGV_NAT_TO_PTR(tcg_global_mem_new_i32((R), (O), (N)))
+#define tcg_temp_new_ptr() TCGV_NAT_TO_PTR(tcg_temp_new_i32())
+#define tcg_temp_free_ptr(T) tcg_temp_free_i32(TCGV_PTR_TO_NAT(T))
#else
-#define tcg_const_ptr tcg_const_i64
-#define tcg_add_ptr tcg_add_i64
-#define tcg_sub_ptr tcg_sub_i64
-#define TCGv_ptr TCGv_i64
-#define GET_TCGV_PTR GET_TCGV_I64
-#define tcg_global_reg_new_ptr tcg_global_reg_new_i64
-#define tcg_global_mem_new_ptr tcg_global_mem_new_i64
-#define tcg_temp_new_ptr tcg_temp_new_i64
-#define tcg_temp_free_ptr tcg_temp_free_i64
+#define TCGV_NAT_TO_PTR(n) MAKE_TCGV_PTR(GET_TCGV_I64(n))
+#define TCGV_PTR_TO_NAT(n) MAKE_TCGV_I64(GET_TCGV_PTR(n))
+
+#define tcg_const_ptr(V) TCGV_NAT_TO_PTR(tcg_const_i64(V))
+#define tcg_global_reg_new_ptr(R, N) \
+ TCGV_NAT_TO_PTR(tcg_global_reg_new_i64((R), (N)))
+#define tcg_global_mem_new_ptr(R, O, N) \
+ TCGV_NAT_TO_PTR(tcg_global_mem_new_i64((R), (O), (N)))
+#define tcg_temp_new_ptr() TCGV_NAT_TO_PTR(tcg_temp_new_i64())
+#define tcg_temp_free_ptr(T) tcg_temp_free_i64(TCGV_PTR_TO_NAT(T))
#endif
void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags,
commit 6bd4b08ab236d59eb2cec92a52efb9c7df2388ac
Author: Peter Maydell <peter.maydell at linaro.org>
Date: Fri May 27 13:12:12 2011 +0100
tcg/tcg-op.h: Fix prototypes for ld/st functions on 64 bit hosts
The prototypes for the ld/st functions on a 64 bit host declared
the address parameter as a TCGv_i64 rather than a TCGv_ptr. This
worked OK (since the two are aliases), but needs to be fixed to
allow extension of TCG type debugging to i64/i32/ptr mismatches.
Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h
index 207a89f..6529655 100644
--- a/tcg/tcg-op.h
+++ b/tcg/tcg-op.h
@@ -1063,66 +1063,66 @@ static inline void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg)
tcg_gen_op2i_i64(INDEX_op_movi_i64, ret, arg);
}
-static inline void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_i64 arg2,
+static inline void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2,
tcg_target_long offset)
{
tcg_gen_ldst_op_i64(INDEX_op_ld8u_i64, ret, arg2, offset);
}
-static inline void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_i64 arg2,
+static inline void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2,
tcg_target_long offset)
{
tcg_gen_ldst_op_i64(INDEX_op_ld8s_i64, ret, arg2, offset);
}
-static inline void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_i64 arg2,
+static inline void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2,
tcg_target_long offset)
{
tcg_gen_ldst_op_i64(INDEX_op_ld16u_i64, ret, arg2, offset);
}
-static inline void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_i64 arg2,
+static inline void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2,
tcg_target_long offset)
{
tcg_gen_ldst_op_i64(INDEX_op_ld16s_i64, ret, arg2, offset);
}
-static inline void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_i64 arg2,
+static inline void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2,
tcg_target_long offset)
{
tcg_gen_ldst_op_i64(INDEX_op_ld32u_i64, ret, arg2, offset);
}
-static inline void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_i64 arg2,
+static inline void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2,
tcg_target_long offset)
{
tcg_gen_ldst_op_i64(INDEX_op_ld32s_i64, ret, arg2, offset);
}
-static inline void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_i64 arg2, tcg_target_long offset)
+static inline void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset)
{
tcg_gen_ldst_op_i64(INDEX_op_ld_i64, ret, arg2, offset);
}
-static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_i64 arg2,
+static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2,
tcg_target_long offset)
{
tcg_gen_ldst_op_i64(INDEX_op_st8_i64, arg1, arg2, offset);
}
-static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_i64 arg2,
+static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2,
tcg_target_long offset)
{
tcg_gen_ldst_op_i64(INDEX_op_st16_i64, arg1, arg2, offset);
}
-static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_i64 arg2,
+static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2,
tcg_target_long offset)
{
tcg_gen_ldst_op_i64(INDEX_op_st32_i64, arg1, arg2, offset);
}
-static inline void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_i64 arg2, tcg_target_long offset)
+static inline void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset)
{
tcg_gen_ldst_op_i64(INDEX_op_st_i64, arg1, arg2, offset);
}
commit 03938c133e60c10a6903ddf71021d76496d69cb0
Author: Brad <brad at comstyle.com>
Date: Wed May 25 23:06:00 2011 -0400
Use the correct header in the TCG MIPS code to find cacheflush() on OpenBSD.
Use the correct header in the TCG MIPS code to find cacheflush() on OpenBSD
to fix compilation of the MIPS host support for OpenBSD/mips64 based architecures.
Signed-off-by: Brad Smith <brad at comstyle.com>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h
index 0028bfa..8cb7d88 100644
--- a/tcg/mips/tcg-target.h
+++ b/tcg/mips/tcg-target.h
@@ -102,7 +102,11 @@ enum {
/* guest base is supported */
#define TCG_TARGET_HAS_GUEST_BASE
+#ifdef __OpenBSD__
+#include <machine/sysarch.h>
+#else
#include <sys/cachectl.h>
+#endif
static inline void flush_icache_range(unsigned long start, unsigned long stop)
{
commit b7fa9214d8d4f57992c9acd0ccb125c54a095f00
Author: Peter Maydell <peter.maydell at linaro.org>
Date: Thu May 26 12:03:36 2011 +0100
target-arm: Fix compilation failure for 64 bit hosts
Use the correct _ptr aliases for manipulating the pointer to
the fp_status; this fixes a compilation failure on 64 bit hosts.
Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
Acked-by: Stefan Weil <weil at mail.berlios.de>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 1501db1..f5507ec 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -980,20 +980,20 @@ static inline void gen_vfp_F1_ld0(int dp)
#define VFP_GEN_ITOF(name) \
static inline void gen_vfp_##name(int dp, int neon) \
{ \
- TCGv statusptr = tcg_temp_new_i32(); \
+ TCGv_ptr statusptr = tcg_temp_new_ptr(); \
int offset; \
if (neon) { \
offset = offsetof(CPUState, vfp.standard_fp_status); \
} else { \
offset = offsetof(CPUState, vfp.fp_status); \
} \
- tcg_gen_addi_i32(statusptr, cpu_env, offset); \
+ tcg_gen_addi_ptr(statusptr, cpu_env, offset); \
if (dp) { \
gen_helper_vfp_##name##d(cpu_F0d, cpu_F0s, statusptr); \
} else { \
gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, statusptr); \
} \
- tcg_temp_free_i32(statusptr); \
+ tcg_temp_free_ptr(statusptr); \
}
VFP_GEN_ITOF(uito)
@@ -1003,20 +1003,20 @@ VFP_GEN_ITOF(sito)
#define VFP_GEN_FTOI(name) \
static inline void gen_vfp_##name(int dp, int neon) \
{ \
- TCGv statusptr = tcg_temp_new_i32(); \
+ TCGv_ptr statusptr = tcg_temp_new_ptr(); \
int offset; \
if (neon) { \
offset = offsetof(CPUState, vfp.standard_fp_status); \
} else { \
offset = offsetof(CPUState, vfp.fp_status); \
} \
- tcg_gen_addi_i32(statusptr, cpu_env, offset); \
+ tcg_gen_addi_ptr(statusptr, cpu_env, offset); \
if (dp) { \
gen_helper_vfp_##name##d(cpu_F0s, cpu_F0d, statusptr); \
} else { \
gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, statusptr); \
} \
- tcg_temp_free_i32(statusptr); \
+ tcg_temp_free_ptr(statusptr); \
}
VFP_GEN_FTOI(toui)
@@ -1029,21 +1029,21 @@ VFP_GEN_FTOI(tosiz)
static inline void gen_vfp_##name(int dp, int shift, int neon) \
{ \
TCGv tmp_shift = tcg_const_i32(shift); \
- TCGv statusptr = tcg_temp_new_i32(); \
+ TCGv_ptr statusptr = tcg_temp_new_ptr(); \
int offset; \
if (neon) { \
offset = offsetof(CPUState, vfp.standard_fp_status); \
} else { \
offset = offsetof(CPUState, vfp.fp_status); \
} \
- tcg_gen_addi_i32(statusptr, cpu_env, offset); \
+ tcg_gen_addi_ptr(statusptr, cpu_env, offset); \
if (dp) { \
gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, tmp_shift, statusptr); \
} else { \
gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, tmp_shift, statusptr); \
} \
tcg_temp_free_i32(tmp_shift); \
- tcg_temp_free_i32(statusptr); \
+ tcg_temp_free_ptr(statusptr); \
}
VFP_GEN_FIX(tosh)
VFP_GEN_FIX(tosl)
commit 47ba1984548f7c27d24b14e46cc5c2488a544055
Merge: 594caf0... e1b45cc...
Author: Aurelien Jarno <aurelien at aurel32.net>
Date: Fri Jun 3 17:47:04 2011 +0200
Merge branch 's390-next' of git://repo.or.cz/qemu/agraf
* 's390-next' of git://repo.or.cz/qemu/agraf:
s390x: implement lrvgr
s390x: fix cksm instruction
s390x: free tmp explicitly in every opcode for disas_a5()
target-s390x: Add missing tcg_temp_free_i32()
target-s390x: Add missing tcg_temp_free_i64() in disas_s390_insn(), opc == 0x90
target-s390x: Add missing tcg_temp_free_i64() in disas_s390_insn(), opc == 0x8e
target-s390x: Add missing tcg_temp_free_i64() in disas_b2()
target-s390x: Add missing tcg_temp_free_i64() in do_mh()
target-s390x: Add missing tcg_temp_free_i64() in gen_jcc()
target-s390x: Fix duplicate call of tcg_temp_new_i64
target-s390x: Fix wrong argument in call of tcg_gen_shl_i64()
target-s390x: Fix build for non-linux hosts
s390x: update zipl rom
commit 594caf07cefd41738ab92335e8990be30e1cc361
Merge: 587eabf... d461e3b...
Author: Aurelien Jarno <aurelien at aurel32.net>
Date: Fri Jun 3 17:45:19 2011 +0200
Merge branch 'ppc-next' of git://repo.or.cz/qemu/agraf
* 'ppc-next' of git://repo.or.cz/qemu/agraf:
PPC: fix mpc8544ds pci default devices
Fix segfault on screendump with -nographic
PPC: install mpc8544ds.dtb
PPC: fix sregs usage on booke
ppc: Fix compilation for ppc64-softmmu
commit 587eabfafc9dbf80c381bd0d98c000a94f8af60d
Author: Aurelien Jarno <aurelien at aurel32.net>
Date: Sun May 15 14:09:18 2011 +0200
softfloat: add float*_is_zero_or_denormal()
float*_is_zero_or_denormal() is available for float32, but not for
float64, floatx80 and float128. Fix that.
Reviewed-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/fpu/softfloat.h b/fpu/softfloat.h
index 8931446..bde2500 100644
--- a/fpu/softfloat.h
+++ b/fpu/softfloat.h
@@ -449,6 +449,11 @@ INLINE int float64_is_any_nan(float64 a)
return ((float64_val(a) & ~(1ULL << 63)) > 0x7ff0000000000000ULL);
}
+INLINE int float64_is_zero_or_denormal(float64 a)
+{
+ return (float64_val(a) & 0x7ff0000000000000LL) == 0;
+}
+
INLINE float64 float64_set_sign(float64 a, int sign)
{
return make_float64((float64_val(a) & 0x7fffffffffffffffULL)
@@ -538,6 +543,11 @@ INLINE int floatx80_is_zero(floatx80 a)
return (a.high & 0x7fff) == 0 && a.low == 0;
}
+INLINE int floatx80_is_zero_or_denormal(floatx80 a)
+{
+ return (a.high & 0x7fff) == 0;
+}
+
INLINE int floatx80_is_any_nan(floatx80 a)
{
return ((a.high & 0x7fff) == 0x7fff) && (a.low<<1);
@@ -626,6 +636,11 @@ INLINE int float128_is_zero(float128 a)
return (a.high & 0x7fffffffffffffffLL) == 0 && a.low == 0;
}
+INLINE int float128_is_zero_or_denormal(float128 a)
+{
+ return (a.high & 0x7fff000000000000LL) == 0;
+}
+
INLINE int float128_is_any_nan(float128 a)
{
return ((a.high >> 48) & 0x7fff) == 0x7fff &&
commit 66fcf8ffcfb538dda612bfa57d28d502e83ff795
Author: Aurelien Jarno <aurelien at aurel32.net>
Date: Sun May 15 14:09:18 2011 +0200
target-i386: use floatx80 constants in helper_fld*_ST0()
Instead of using a table which doesn't correspond to anything from
physical in the CPU, use directly the constants in helper_fld*_ST0().
Cc: Andreas Färber <andreas.faerber at web.de>
Reviewed-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/target-i386/op_helper.c b/target-i386/op_helper.c
index 4d309ab..cec0c76 100644
--- a/target-i386/op_helper.c
+++ b/target-i386/op_helper.c
@@ -99,17 +99,6 @@ static const uint8_t rclb_table[32] = {
#define floatx80_l2e make_floatx80( 0x3fff, 0xb8aa3b295c17f0bcLL )
#define floatx80_l2t make_floatx80( 0x4000, 0xd49a784bcd1b8afeLL )
-static const floatx80 f15rk[7] =
-{
- floatx80_zero,
- floatx80_one,
- floatx80_pi,
- floatx80_lg2,
- floatx80_ln2,
- floatx80_l2e,
- floatx80_l2t,
-};
-
/* broken thread support */
static spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
@@ -3816,42 +3805,42 @@ void helper_fabs_ST0(void)
void helper_fld1_ST0(void)
{
- ST0 = f15rk[1];
+ ST0 = floatx80_one;
}
void helper_fldl2t_ST0(void)
{
- ST0 = f15rk[6];
+ ST0 = floatx80_l2t;
}
void helper_fldl2e_ST0(void)
{
- ST0 = f15rk[5];
+ ST0 = floatx80_l2e;
}
void helper_fldpi_ST0(void)
{
- ST0 = f15rk[2];
+ ST0 = floatx80_pi;
}
void helper_fldlg2_ST0(void)
{
- ST0 = f15rk[3];
+ ST0 = floatx80_lg2;
}
void helper_fldln2_ST0(void)
{
- ST0 = f15rk[4];
+ ST0 = floatx80_ln2;
}
void helper_fldz_ST0(void)
{
- ST0 = f15rk[0];
+ ST0 = floatx80_zero;
}
void helper_fldz_FT0(void)
{
- FT0 = f15rk[0];
+ FT0 = floatx80_zero;
}
uint32_t helper_fnstsw(void)
commit be22a9abc0dd02aa99726b656482b64e2aa4af80
Author: Aurelien Jarno <aurelien at aurel32.net>
Date: Sun May 15 14:09:18 2011 +0200
softfloat: always enable floatx80 and float128 support
Now that softfloat-native is gone, there is no real point on not always
enabling floatx80 and float128 support.
Reviewed-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/cpu-all.h b/cpu-all.h
index fc252ba..880f570 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -137,7 +137,6 @@ typedef union {
uint64_t ll;
} CPU_DoubleU;
-#if defined(FLOATX80)
typedef union {
floatx80 d;
struct {
@@ -145,7 +144,6 @@ typedef union {
uint16_t upper;
} l;
} CPU_LDoubleU;
-#endif
typedef union {
float128 q;
diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h
index 9d68aae..c7d35a1 100644
--- a/fpu/softfloat-specialize.h
+++ b/fpu/softfloat-specialize.h
@@ -523,8 +523,6 @@ static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM)
}
}
-#ifdef FLOATX80
-
/*----------------------------------------------------------------------------
| Returns 1 if the extended double-precision floating-point value `a' is a
| quiet NaN; otherwise returns 0. This slightly differs from the same
@@ -681,10 +679,6 @@ static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM)
}
}
-#endif
-
-#ifdef FLOAT128
-
/*----------------------------------------------------------------------------
| Returns 1 if the quadruple-precision floating-point value `a' is a quiet
| NaN; otherwise returns 0.
@@ -820,4 +814,3 @@ static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM)
}
}
-#endif
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index e3cd8a7..7951a0e 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -64,12 +64,10 @@ void set_float_exception_flags(int val STATUS_PARAM)
STATUS(float_exception_flags) = val;
}
-#ifdef FLOATX80
void set_floatx80_rounding_precision(int val STATUS_PARAM)
{
STATUS(floatx80_rounding_precision) = val;
}
-#endif
/*----------------------------------------------------------------------------
| Returns the fraction bits of the half-precision floating-point value `a'.
@@ -564,8 +562,6 @@ static float64
}
-#ifdef FLOATX80
-
/*----------------------------------------------------------------------------
| Returns the fraction bits of the extended double-precision floating-point
| value `a'.
@@ -851,10 +847,6 @@ static floatx80
}
-#endif
-
-#ifdef FLOAT128
-
/*----------------------------------------------------------------------------
| Returns the least-significant 64 fraction bits of the quadruple-precision
| floating-point value `a'.
@@ -1118,8 +1110,6 @@ static float128
}
-#endif
-
/*----------------------------------------------------------------------------
| Returns the result of converting the 32-bit two's complement integer `a'
| to the single-precision floating-point format. The conversion is performed
@@ -1159,8 +1149,6 @@ float64 int32_to_float64( int32 a STATUS_PARAM )
}
-#ifdef FLOATX80
-
/*----------------------------------------------------------------------------
| Returns the result of converting the 32-bit two's complement integer `a'
| to the extended double-precision floating-point format. The conversion
@@ -1184,10 +1172,6 @@ floatx80 int32_to_floatx80( int32 a STATUS_PARAM )
}
-#endif
-
-#ifdef FLOAT128
-
/*----------------------------------------------------------------------------
| Returns the result of converting the 32-bit two's complement integer `a' to
| the quadruple-precision floating-point format. The conversion is performed
@@ -1210,8 +1194,6 @@ float128 int32_to_float128( int32 a STATUS_PARAM )
}
-#endif
-
/*----------------------------------------------------------------------------
| Returns the result of converting the 64-bit two's complement integer `a'
| to the single-precision floating-point format. The conversion is performed
@@ -1291,8 +1273,6 @@ float64 uint64_to_float64( uint64 a STATUS_PARAM )
}
-#ifdef FLOATX80
-
/*----------------------------------------------------------------------------
| Returns the result of converting the 64-bit two's complement integer `a'
| to the extended double-precision floating-point format. The conversion
@@ -1314,10 +1294,6 @@ floatx80 int64_to_floatx80( int64 a STATUS_PARAM )
}
-#endif
-
-#ifdef FLOAT128
-
/*----------------------------------------------------------------------------
| Returns the result of converting the 64-bit two's complement integer `a' to
| the quadruple-precision floating-point format. The conversion is performed
@@ -1351,8 +1327,6 @@ float128 int64_to_float128( int64 a STATUS_PARAM )
}
-#endif
-
/*----------------------------------------------------------------------------
| Returns the result of converting the single-precision floating-point value
| `a' to the 32-bit two's complement integer format. The conversion is
@@ -1590,8 +1564,6 @@ float64 float32_to_float64( float32 a STATUS_PARAM )
}
-#ifdef FLOATX80
-
/*----------------------------------------------------------------------------
| Returns the result of converting the single-precision floating-point value
| `a' to the extended double-precision floating-point format. The conversion
@@ -1622,10 +1594,6 @@ floatx80 float32_to_floatx80( float32 a STATUS_PARAM )
}
-#endif
-
-#ifdef FLOAT128
-
/*----------------------------------------------------------------------------
| Returns the result of converting the single-precision floating-point value
| `a' to the double-precision floating-point format. The conversion is
@@ -1656,8 +1624,6 @@ float128 float32_to_float128( float32 a STATUS_PARAM )
}
-#endif
-
/*----------------------------------------------------------------------------
| Rounds the single-precision floating-point value `a' to an integer, and
| returns the result as a single-precision floating-point value. The
@@ -2939,8 +2905,6 @@ float16 float32_to_float16(float32 a, flag ieee STATUS_PARAM)
return packFloat16(aSign, aExp + 14, aSig >> 13);
}
-#ifdef FLOATX80
-
/*----------------------------------------------------------------------------
| Returns the result of converting the double-precision floating-point value
| `a' to the extended double-precision floating-point format. The conversion
@@ -2972,10 +2936,6 @@ floatx80 float64_to_floatx80( float64 a STATUS_PARAM )
}
-#endif
-
-#ifdef FLOAT128
-
/*----------------------------------------------------------------------------
| Returns the result of converting the double-precision floating-point value
| `a' to the quadruple-precision floating-point format. The conversion is
@@ -3007,8 +2967,6 @@ float128 float64_to_float128( float64 a STATUS_PARAM )
}
-#endif
-
/*----------------------------------------------------------------------------
| Rounds the double-precision floating-point value `a' to an integer, and
| returns the result as a double-precision floating-point value. The
@@ -3816,8 +3774,6 @@ int float64_unordered_quiet( float64 a, float64 b STATUS_PARAM )
return 0;
}
-#ifdef FLOATX80
-
/*----------------------------------------------------------------------------
| Returns the result of converting the extended double-precision floating-
| point value `a' to the 32-bit two's complement integer format. The
@@ -4030,8 +3986,6 @@ float64 floatx80_to_float64( floatx80 a STATUS_PARAM )
}
-#ifdef FLOAT128
-
/*----------------------------------------------------------------------------
| Returns the result of converting the extended double-precision floating-
| point value `a' to the quadruple-precision floating-point format. The
@@ -4056,8 +4010,6 @@ float128 floatx80_to_float128( floatx80 a STATUS_PARAM )
}
-#endif
-
/*----------------------------------------------------------------------------
| Rounds the extended double-precision floating-point value `a' to an integer,
| and returns the result as an extended quadruple-precision floating-point
@@ -4849,10 +4801,6 @@ int floatx80_unordered_quiet( floatx80 a, floatx80 b STATUS_PARAM )
return 0;
}
-#endif
-
-#ifdef FLOAT128
-
/*----------------------------------------------------------------------------
| Returns the result of converting the quadruple-precision floating-point
| value `a' to the 32-bit two's complement integer format. The conversion
@@ -5102,8 +5050,6 @@ float64 float128_to_float64( float128 a STATUS_PARAM )
}
-#ifdef FLOATX80
-
/*----------------------------------------------------------------------------
| Returns the result of converting the quadruple-precision floating-point
| value `a' to the extended double-precision floating-point format. The
@@ -5139,8 +5085,6 @@ floatx80 float128_to_floatx80( float128 a STATUS_PARAM )
}
-#endif
-
/*----------------------------------------------------------------------------
| Rounds the quadruple-precision floating-point value `a' to an integer, and
| returns the result as a quadruple-precision floating-point value. The
@@ -6020,8 +5964,6 @@ int float128_unordered_quiet( float128 a, float128 b STATUS_PARAM )
return 0;
}
-#endif
-
/* misc functions */
float32 uint32_to_float32( unsigned int a STATUS_PARAM )
{
@@ -6423,7 +6365,6 @@ float64 float64_scalbn( float64 a, int n STATUS_PARAM )
return normalizeRoundAndPackFloat64( aSign, aExp, aSig STATUS_VAR );
}
-#ifdef FLOATX80
floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM )
{
flag aSign;
@@ -6454,9 +6395,7 @@ floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM )
return normalizeRoundAndPackFloatx80( STATUS(floatx80_rounding_precision),
aSign, aExp, aSig, 0 STATUS_VAR );
}
-#endif
-#ifdef FLOAT128
float128 float128_scalbn( float128 a, int n STATUS_PARAM )
{
flag aSign;
@@ -6489,4 +6428,3 @@ float128 float128_scalbn( float128 a, int n STATUS_PARAM )
STATUS_VAR );
}
-#endif
diff --git a/fpu/softfloat.h b/fpu/softfloat.h
index 999b95c..8931446 100644
--- a/fpu/softfloat.h
+++ b/fpu/softfloat.h
@@ -74,17 +74,6 @@ typedef int64_t int64;
#define SNAN_BIT_IS_ONE 0
#endif
-/*----------------------------------------------------------------------------
-| The macro `FLOATX80' must be defined to enable the extended double-precision
-| floating-point format `floatx80'. If this macro is not defined, the
-| `floatx80' type will not be defined, and none of the functions that either
-| input or output the `floatx80' type will be defined. The same applies to
-| the `FLOAT128' macro and the quadruple-precision format `float128'.
-*----------------------------------------------------------------------------*/
-/* bit exact soft float support */
-#define FLOATX80
-#define FLOAT128
-
#define STATUS_PARAM , float_status *status
#define STATUS(field) status->field
#define STATUS_VAR , status
@@ -141,14 +130,11 @@ typedef uint64_t float64;
#define const_float32(x) (x)
#define const_float64(x) (x)
#endif
-#ifdef FLOATX80
typedef struct {
uint64_t low;
uint16_t high;
} floatx80;
#define make_floatx80(exp, mant) ((floatx80) { mant, exp })
-#endif
-#ifdef FLOAT128
typedef struct {
#ifdef HOST_WORDS_BIGENDIAN
uint64_t high, low;
@@ -156,7 +142,6 @@ typedef struct {
uint64_t low, high;
#endif
} float128;
-#endif
/*----------------------------------------------------------------------------
| Software IEC/IEEE floating-point underflow tininess-detection mode.
@@ -193,9 +178,7 @@ typedef struct float_status {
signed char float_detect_tininess;
signed char float_rounding_mode;
signed char float_exception_flags;
-#ifdef FLOATX80
signed char floatx80_rounding_precision;
-#endif
/* should denormalised results go to zero and set the inexact flag? */
flag flush_to_zero;
/* should denormalised inputs go to zero and set the input_denormal flag? */
@@ -225,9 +208,7 @@ INLINE int get_float_exception_flags(float_status *status)
{
return STATUS(float_exception_flags);
}
-#ifdef FLOATX80
void set_floatx80_rounding_precision(int val STATUS_PARAM);
-#endif
/*----------------------------------------------------------------------------
| Routine to raise any or all of the software IEC/IEEE floating-point
@@ -242,22 +223,14 @@ float32 int32_to_float32( int32 STATUS_PARAM );
float64 int32_to_float64( int32 STATUS_PARAM );
float32 uint32_to_float32( unsigned int STATUS_PARAM );
float64 uint32_to_float64( unsigned int STATUS_PARAM );
-#ifdef FLOATX80
floatx80 int32_to_floatx80( int32 STATUS_PARAM );
-#endif
-#ifdef FLOAT128
float128 int32_to_float128( int32 STATUS_PARAM );
-#endif
float32 int64_to_float32( int64 STATUS_PARAM );
float32 uint64_to_float32( uint64 STATUS_PARAM );
float64 int64_to_float64( int64 STATUS_PARAM );
float64 uint64_to_float64( uint64 STATUS_PARAM );
-#ifdef FLOATX80
floatx80 int64_to_floatx80( int64 STATUS_PARAM );
-#endif
-#ifdef FLOAT128
float128 int64_to_float128( int64 STATUS_PARAM );
-#endif
/*----------------------------------------------------------------------------
| Software half-precision conversion routines.
@@ -295,12 +268,8 @@ uint32 float32_to_uint32_round_to_zero( float32 STATUS_PARAM );
int64 float32_to_int64( float32 STATUS_PARAM );
int64 float32_to_int64_round_to_zero( float32 STATUS_PARAM );
float64 float32_to_float64( float32 STATUS_PARAM );
-#ifdef FLOATX80
floatx80 float32_to_floatx80( float32 STATUS_PARAM );
-#endif
-#ifdef FLOAT128
float128 float32_to_float128( float32 STATUS_PARAM );
-#endif
/*----------------------------------------------------------------------------
| Software IEC/IEEE single-precision operations.
@@ -412,12 +381,8 @@ int64 float64_to_int64_round_to_zero( float64 STATUS_PARAM );
uint64 float64_to_uint64 (float64 a STATUS_PARAM);
uint64 float64_to_uint64_round_to_zero (float64 a STATUS_PARAM);
float32 float64_to_float32( float64 STATUS_PARAM );
-#ifdef FLOATX80
floatx80 float64_to_floatx80( float64 STATUS_PARAM );
-#endif
-#ifdef FLOAT128
float128 float64_to_float128( float64 STATUS_PARAM );
-#endif
/*----------------------------------------------------------------------------
| Software IEC/IEEE double-precision operations.
@@ -510,8 +475,6 @@ INLINE float64 float64_set_sign(float64 a, int sign)
#define float64_default_nan make_float64(LIT64( 0xFFF8000000000000 ))
#endif
-#ifdef FLOATX80
-
/*----------------------------------------------------------------------------
| Software IEC/IEEE extended double-precision conversion routines.
*----------------------------------------------------------------------------*/
@@ -521,9 +484,7 @@ int64 floatx80_to_int64( floatx80 STATUS_PARAM );
int64 floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM );
float32 floatx80_to_float32( floatx80 STATUS_PARAM );
float64 floatx80_to_float64( floatx80 STATUS_PARAM );
-#ifdef FLOAT128
float128 floatx80_to_float128( floatx80 STATUS_PARAM );
-#endif
/*----------------------------------------------------------------------------
| Software IEC/IEEE extended double-precision operations.
@@ -602,10 +563,6 @@ INLINE int floatx80_is_any_nan(floatx80 a)
#define floatx80_default_nan_low LIT64( 0xC000000000000000 )
#endif
-#endif
-
-#ifdef FLOAT128
-
/*----------------------------------------------------------------------------
| Software IEC/IEEE quadruple-precision conversion routines.
*----------------------------------------------------------------------------*/
@@ -615,9 +572,7 @@ int64 float128_to_int64( float128 STATUS_PARAM );
int64 float128_to_int64_round_to_zero( float128 STATUS_PARAM );
float32 float128_to_float32( float128 STATUS_PARAM );
float64 float128_to_float64( float128 STATUS_PARAM );
-#ifdef FLOATX80
floatx80 float128_to_floatx80( float128 STATUS_PARAM );
-#endif
/*----------------------------------------------------------------------------
| Software IEC/IEEE quadruple-precision operations.
@@ -689,6 +644,4 @@ INLINE int float128_is_any_nan(float128 a)
#define float128_default_nan_low LIT64( 0x0000000000000000 )
#endif
-#endif
-
#endif /* !SOFTFLOAT_H */
commit cf67c6bad56d43e6d60df22d6d43371813a6e2b8
Author: Aurelien Jarno <aurelien at aurel32.net>
Date: Sun May 15 14:09:18 2011 +0200
softfloat-native: remove
Remove softfloat-native support, all targets are now using softfloat
instead.
Reviewed-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/Makefile.target b/Makefile.target
index 602d50d..8b6e271 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -71,8 +71,7 @@ all: $(PROGS) stap
# cpu emulator library
libobj-y = exec.o translate-all.o cpu-exec.o translate.o
libobj-y += tcg/tcg.o
-libobj-$(CONFIG_SOFTFLOAT) += fpu/softfloat.o
-libobj-$(CONFIG_NOSOFTFLOAT) += fpu/softfloat-native.o
+libobj-y += fpu/softfloat.o
libobj-y += op_helper.o helper.o
ifeq ($(TARGET_BASE_ARCH), i386)
libobj-y += cpuid.o
diff --git a/configure b/configure
index a318d37..0c26a26 100755
--- a/configure
+++ b/configure
@@ -3385,8 +3385,6 @@ if test ! -z "$gdb_xml_files" ; then
echo "TARGET_XML_FILES=$list" >> $config_target_mak
fi
-echo "CONFIG_SOFTFLOAT=y" >> $config_target_mak
-
if test "$target_user_only" = "yes" -a "$bflt" = "yes"; then
echo "TARGET_HAS_BFLT=y" >> $config_target_mak
fi
diff --git a/cpu-all.h b/cpu-all.h
index 54df1d3..fc252ba 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -123,8 +123,7 @@ typedef union {
endian ! */
typedef union {
float64 d;
-#if defined(HOST_WORDS_BIGENDIAN) \
- || (defined(__arm__) && !defined(__VFP_FP__) && !defined(CONFIG_SOFTFLOAT))
+#if defined(HOST_WORDS_BIGENDIAN)
struct {
uint32_t upper;
uint32_t lower;
@@ -148,7 +147,6 @@ typedef union {
} CPU_LDoubleU;
#endif
-#if defined(CONFIG_SOFTFLOAT)
typedef union {
float128 q;
#if defined(HOST_WORDS_BIGENDIAN)
@@ -175,7 +173,6 @@ typedef union {
} ll;
#endif
} CPU_QuadU;
-#endif
/* CPU memory access without any memory or io remapping */
diff --git a/fpu/softfloat-native.c b/fpu/softfloat-native.c
deleted file mode 100644
index 8848651..0000000
--- a/fpu/softfloat-native.c
+++ /dev/null
@@ -1,540 +0,0 @@
-/* Native implementation of soft float functions. Only a single status
- context is supported */
-#include "softfloat.h"
-#include <math.h>
-#if defined(CONFIG_SOLARIS)
-#include <fenv.h>
-#endif
-
-void set_float_rounding_mode(int val STATUS_PARAM)
-{
- STATUS(float_rounding_mode) = val;
-#if (defined(CONFIG_BSD) && !defined(__APPLE__) && !defined(__GLIBC__)) || \
- (defined(CONFIG_SOLARIS) && CONFIG_SOLARIS_VERSION < 10)
- fpsetround(val);
-#else
- fesetround(val);
-#endif
-}
-
-#ifdef FLOATX80
-void set_floatx80_rounding_precision(int val STATUS_PARAM)
-{
- STATUS(floatx80_rounding_precision) = val;
-}
-#endif
-
-#if defined(CONFIG_BSD) || \
- (defined(CONFIG_SOLARIS) && CONFIG_SOLARIS_VERSION < 10)
-#define lrint(d) ((int32_t)rint(d))
-#define llrint(d) ((int64_t)rint(d))
-#define lrintf(f) ((int32_t)rint(f))
-#define llrintf(f) ((int64_t)rint(f))
-#define sqrtf(f) ((float)sqrt(f))
-#define remainderf(fa, fb) ((float)remainder(fa, fb))
-#define rintf(f) ((float)rint(f))
-#if !defined(__sparc__) && \
- (defined(CONFIG_SOLARIS) && CONFIG_SOLARIS_VERSION < 10)
-extern long double rintl(long double);
-extern long double scalbnl(long double, int);
-
-long long
-llrintl(long double x) {
- return ((long long) rintl(x));
-}
-
-long
-lrintl(long double x) {
- return ((long) rintl(x));
-}
-
-long double
-ldexpl(long double x, int n) {
- return (scalbnl(x, n));
-}
-#endif
-#endif
-
-#if defined(_ARCH_PPC)
-
-/* correct (but slow) PowerPC rint() (glibc version is incorrect) */
-static double qemu_rint(double x)
-{
- double y = 4503599627370496.0;
- if (fabs(x) >= y)
- return x;
- if (x < 0)
- y = -y;
- y = (x + y) - y;
- if (y == 0.0)
- y = copysign(y, x);
- return y;
-}
-
-#define rint qemu_rint
-#endif
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE integer-to-floating-point conversion routines.
-*----------------------------------------------------------------------------*/
-float32 int32_to_float32(int v STATUS_PARAM)
-{
- return (float32)v;
-}
-
-float32 uint32_to_float32(unsigned int v STATUS_PARAM)
-{
- return (float32)v;
-}
-
-float64 int32_to_float64(int v STATUS_PARAM)
-{
- return (float64)v;
-}
-
-float64 uint32_to_float64(unsigned int v STATUS_PARAM)
-{
- return (float64)v;
-}
-
-#ifdef FLOATX80
-floatx80 int32_to_floatx80(int v STATUS_PARAM)
-{
- return (floatx80)v;
-}
-#endif
-float32 int64_to_float32( int64_t v STATUS_PARAM)
-{
- return (float32)v;
-}
-float32 uint64_to_float32( uint64_t v STATUS_PARAM)
-{
- return (float32)v;
-}
-float64 int64_to_float64( int64_t v STATUS_PARAM)
-{
- return (float64)v;
-}
-float64 uint64_to_float64( uint64_t v STATUS_PARAM)
-{
- return (float64)v;
-}
-#ifdef FLOATX80
-floatx80 int64_to_floatx80( int64_t v STATUS_PARAM)
-{
- return (floatx80)v;
-}
-#endif
-
-/* XXX: this code implements the x86 behaviour, not the IEEE one. */
-#if HOST_LONG_BITS == 32
-static inline int long_to_int32(long a)
-{
- return a;
-}
-#else
-static inline int long_to_int32(long a)
-{
- if (a != (int32_t)a)
- a = 0x80000000;
- return a;
-}
-#endif
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE single-precision conversion routines.
-*----------------------------------------------------------------------------*/
-int float32_to_int32( float32 a STATUS_PARAM)
-{
- return long_to_int32(lrintf(a));
-}
-int float32_to_int32_round_to_zero( float32 a STATUS_PARAM)
-{
- return (int)a;
-}
-int64_t float32_to_int64( float32 a STATUS_PARAM)
-{
- return llrintf(a);
-}
-
-int64_t float32_to_int64_round_to_zero( float32 a STATUS_PARAM)
-{
- return (int64_t)a;
-}
-
-float64 float32_to_float64( float32 a STATUS_PARAM)
-{
- return a;
-}
-#ifdef FLOATX80
-floatx80 float32_to_floatx80( float32 a STATUS_PARAM)
-{
- return a;
-}
-#endif
-
-unsigned int float32_to_uint32( float32 a STATUS_PARAM)
-{
- int64_t v;
- unsigned int res;
-
- v = llrintf(a);
- if (v < 0) {
- res = 0;
- } else if (v > 0xffffffff) {
- res = 0xffffffff;
- } else {
- res = v;
- }
- return res;
-}
-unsigned int float32_to_uint32_round_to_zero( float32 a STATUS_PARAM)
-{
- int64_t v;
- unsigned int res;
-
- v = (int64_t)a;
- if (v < 0) {
- res = 0;
- } else if (v > 0xffffffff) {
- res = 0xffffffff;
- } else {
- res = v;
- }
- return res;
-}
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE single-precision operations.
-*----------------------------------------------------------------------------*/
-float32 float32_round_to_int( float32 a STATUS_PARAM)
-{
- return rintf(a);
-}
-
-float32 float32_rem( float32 a, float32 b STATUS_PARAM)
-{
- return remainderf(a, b);
-}
-
-float32 float32_sqrt( float32 a STATUS_PARAM)
-{
- return sqrtf(a);
-}
-int float32_compare( float32 a, float32 b STATUS_PARAM )
-{
- if (a < b) {
- return float_relation_less;
- } else if (a == b) {
- return float_relation_equal;
- } else if (a > b) {
- return float_relation_greater;
- } else {
- return float_relation_unordered;
- }
-}
-int float32_compare_quiet( float32 a, float32 b STATUS_PARAM )
-{
- if (isless(a, b)) {
- return float_relation_less;
- } else if (a == b) {
- return float_relation_equal;
- } else if (isgreater(a, b)) {
- return float_relation_greater;
- } else {
- return float_relation_unordered;
- }
-}
-int float32_is_signaling_nan( float32 a1)
-{
- float32u u;
- uint32_t a;
- u.f = a1;
- a = u.i;
- return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
-}
-
-int float32_is_quiet_nan( float32 a1 )
-{
- float32u u;
- uint64_t a;
- u.f = a1;
- a = u.i;
- return ( 0xFF800000 < ( a<<1 ) );
-}
-
-int float32_is_any_nan( float32 a1 )
-{
- float32u u;
- uint32_t a;
- u.f = a1;
- a = u.i;
- return (a & ~(1 << 31)) > 0x7f800000U;
-}
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE double-precision conversion routines.
-*----------------------------------------------------------------------------*/
-int float64_to_int32( float64 a STATUS_PARAM)
-{
- return long_to_int32(lrint(a));
-}
-int float64_to_int32_round_to_zero( float64 a STATUS_PARAM)
-{
- return (int)a;
-}
-int64_t float64_to_int64( float64 a STATUS_PARAM)
-{
- return llrint(a);
-}
-int64_t float64_to_int64_round_to_zero( float64 a STATUS_PARAM)
-{
- return (int64_t)a;
-}
-float32 float64_to_float32( float64 a STATUS_PARAM)
-{
- return a;
-}
-#ifdef FLOATX80
-floatx80 float64_to_floatx80( float64 a STATUS_PARAM)
-{
- return a;
-}
-#endif
-#ifdef FLOAT128
-float128 float64_to_float128( float64 a STATUS_PARAM)
-{
- return a;
-}
-#endif
-
-unsigned int float64_to_uint32( float64 a STATUS_PARAM)
-{
- int64_t v;
- unsigned int res;
-
- v = llrint(a);
- if (v < 0) {
- res = 0;
- } else if (v > 0xffffffff) {
- res = 0xffffffff;
- } else {
- res = v;
- }
- return res;
-}
-unsigned int float64_to_uint32_round_to_zero( float64 a STATUS_PARAM)
-{
- int64_t v;
- unsigned int res;
-
- v = (int64_t)a;
- if (v < 0) {
- res = 0;
- } else if (v > 0xffffffff) {
- res = 0xffffffff;
- } else {
- res = v;
- }
- return res;
-}
-uint64_t float64_to_uint64 (float64 a STATUS_PARAM)
-{
- int64_t v;
-
- v = llrint(a + (float64)INT64_MIN);
-
- return v - INT64_MIN;
-}
-uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM)
-{
- int64_t v;
-
- v = (int64_t)(a + (float64)INT64_MIN);
-
- return v - INT64_MIN;
-}
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE double-precision operations.
-*----------------------------------------------------------------------------*/
-#if defined(__sun__) && \
- (defined(CONFIG_SOLARIS) && CONFIG_SOLARIS_VERSION < 10)
-static inline float64 trunc(float64 x)
-{
- return x < 0 ? -floor(-x) : floor(x);
-}
-#endif
-float64 float64_trunc_to_int( float64 a STATUS_PARAM )
-{
- return trunc(a);
-}
-
-float64 float64_round_to_int( float64 a STATUS_PARAM )
-{
- return rint(a);
-}
-
-float64 float64_rem( float64 a, float64 b STATUS_PARAM)
-{
- return remainder(a, b);
-}
-
-float64 float64_sqrt( float64 a STATUS_PARAM)
-{
- return sqrt(a);
-}
-int float64_compare( float64 a, float64 b STATUS_PARAM )
-{
- if (a < b) {
- return float_relation_less;
- } else if (a == b) {
- return float_relation_equal;
- } else if (a > b) {
- return float_relation_greater;
- } else {
- return float_relation_unordered;
- }
-}
-int float64_compare_quiet( float64 a, float64 b STATUS_PARAM )
-{
- if (isless(a, b)) {
- return float_relation_less;
- } else if (a == b) {
- return float_relation_equal;
- } else if (isgreater(a, b)) {
- return float_relation_greater;
- } else {
- return float_relation_unordered;
- }
-}
-int float64_is_signaling_nan( float64 a1)
-{
- float64u u;
- uint64_t a;
- u.f = a1;
- a = u.i;
- return
- ( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
- && ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
-
-}
-
-int float64_is_quiet_nan( float64 a1 )
-{
- float64u u;
- uint64_t a;
- u.f = a1;
- a = u.i;
-
- return ( LIT64( 0xFFF0000000000000 ) < (uint64_t) ( a<<1 ) );
-
-}
-
-int float64_is_any_nan( float64 a1 )
-{
- float64u u;
- uint64_t a;
- u.f = a1;
- a = u.i;
-
- return (a & ~(1ULL << 63)) > LIT64 (0x7FF0000000000000 );
-}
-
-#ifdef FLOATX80
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE extended double-precision conversion routines.
-*----------------------------------------------------------------------------*/
-int floatx80_to_int32( floatx80 a STATUS_PARAM)
-{
- return long_to_int32(lrintl(a));
-}
-int floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM)
-{
- return (int)a;
-}
-int64_t floatx80_to_int64( floatx80 a STATUS_PARAM)
-{
- return llrintl(a);
-}
-int64_t floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM)
-{
- return (int64_t)a;
-}
-float32 floatx80_to_float32( floatx80 a STATUS_PARAM)
-{
- return a;
-}
-float64 floatx80_to_float64( floatx80 a STATUS_PARAM)
-{
- return a;
-}
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE extended double-precision operations.
-*----------------------------------------------------------------------------*/
-floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM)
-{
- return rintl(a);
-}
-floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM)
-{
- return remainderl(a, b);
-}
-floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM)
-{
- return sqrtl(a);
-}
-int floatx80_compare( floatx80 a, floatx80 b STATUS_PARAM )
-{
- if (a < b) {
- return float_relation_less;
- } else if (a == b) {
- return float_relation_equal;
- } else if (a > b) {
- return float_relation_greater;
- } else {
- return float_relation_unordered;
- }
-}
-int floatx80_compare_quiet( floatx80 a, floatx80 b STATUS_PARAM )
-{
- if (isless(a, b)) {
- return float_relation_less;
- } else if (a == b) {
- return float_relation_equal;
- } else if (isgreater(a, b)) {
- return float_relation_greater;
- } else {
- return float_relation_unordered;
- }
-}
-int floatx80_is_signaling_nan( floatx80 a1)
-{
- floatx80u u;
- uint64_t aLow;
- u.f = a1;
-
- aLow = u.i.low & ~ LIT64( 0x4000000000000000 );
- return
- ( ( u.i.high & 0x7FFF ) == 0x7FFF )
- && (uint64_t) ( aLow<<1 )
- && ( u.i.low == aLow );
-}
-
-int floatx80_is_quiet_nan( floatx80 a1 )
-{
- floatx80u u;
- u.f = a1;
- return ( ( u.i.high & 0x7FFF ) == 0x7FFF ) && (uint64_t) ( u.i.low<<1 );
-}
-
-int floatx80_is_any_nan( floatx80 a1 )
-{
- floatx80u u;
- u.f = a1;
- return ((u.i.high & 0x7FFF) == 0x7FFF) && ( u.i.low<<1 );
-}
-
-#endif
diff --git a/fpu/softfloat-native.h b/fpu/softfloat-native.h
deleted file mode 100644
index 6afb74a..0000000
--- a/fpu/softfloat-native.h
+++ /dev/null
@@ -1,531 +0,0 @@
-/* Native implementation of soft float functions */
-#include <math.h>
-
-#if (defined(CONFIG_BSD) && !defined(__APPLE__) && !defined(__GLIBC__)) \
- || defined(CONFIG_SOLARIS)
-#include <ieeefp.h>
-#define fabsf(f) ((float)fabs(f))
-#else
-#include <fenv.h>
-#endif
-
-#if defined(__OpenBSD__) || defined(__NetBSD__)
-#include <sys/param.h>
-#endif
-
-/*
- * Define some C99-7.12.3 classification macros and
- * some C99-.12.4 for Solaris systems OS less than 10,
- * or Solaris 10 systems running GCC 3.x or less.
- * Solaris 10 with GCC4 does not need these macros as they
- * are defined in <iso/math_c99.h> with a compiler directive
- */
-#if defined(CONFIG_SOLARIS) && \
- ((CONFIG_SOLARIS_VERSION <= 9 ) || \
- ((CONFIG_SOLARIS_VERSION == 10) && (__GNUC__ < 4))) \
- || (defined(__OpenBSD__) && (OpenBSD < 200811))
-/*
- * C99 7.12.3 classification macros
- * and
- * C99 7.12.14 comparison macros
- *
- * ... do not work on Solaris 10 using GNU CC 3.4.x.
- * Try to workaround the missing / broken C99 math macros.
- */
-#if defined(__OpenBSD__)
-#define unordered(x, y) (isnan(x) || isnan(y))
-#endif
-
-#ifdef __NetBSD__
-#ifndef isgreater
-#define isgreater(x, y) __builtin_isgreater(x, y)
-#endif
-#ifndef isgreaterequal
-#define isgreaterequal(x, y) __builtin_isgreaterequal(x, y)
-#endif
-#ifndef isless
-#define isless(x, y) __builtin_isless(x, y)
-#endif
-#ifndef islessequal
-#define islessequal(x, y) __builtin_islessequal(x, y)
-#endif
-#ifndef isunordered
-#define isunordered(x, y) __builtin_isunordered(x, y)
-#endif
-#endif
-
-
-#define isnormal(x) (fpclass(x) >= FP_NZERO)
-#define isgreater(x, y) ((!unordered(x, y)) && ((x) > (y)))
-#define isgreaterequal(x, y) ((!unordered(x, y)) && ((x) >= (y)))
-#define isless(x, y) ((!unordered(x, y)) && ((x) < (y)))
-#define islessequal(x, y) ((!unordered(x, y)) && ((x) <= (y)))
-#define isunordered(x,y) unordered(x, y)
-#endif
-
-#if defined(__sun__) && !defined(CONFIG_NEEDS_LIBSUNMATH)
-
-#ifndef isnan
-# define isnan(x) \
- (sizeof (x) == sizeof (long double) ? isnan_ld (x) \
- : sizeof (x) == sizeof (double) ? isnan_d (x) \
- : isnan_f (x))
-static inline int isnan_f (float x) { return x != x; }
-static inline int isnan_d (double x) { return x != x; }
-static inline int isnan_ld (long double x) { return x != x; }
-#endif
-
-#ifndef isinf
-# define isinf(x) \
- (sizeof (x) == sizeof (long double) ? isinf_ld (x) \
- : sizeof (x) == sizeof (double) ? isinf_d (x) \
- : isinf_f (x))
-static inline int isinf_f (float x) { return isnan (x - x); }
-static inline int isinf_d (double x) { return isnan (x - x); }
-static inline int isinf_ld (long double x) { return isnan (x - x); }
-#endif
-#endif
-
-typedef float float32;
-typedef double float64;
-#ifdef FLOATX80
-typedef long double floatx80;
-#endif
-
-typedef union {
- float32 f;
- uint32_t i;
-} float32u;
-typedef union {
- float64 f;
- uint64_t i;
-} float64u;
-#ifdef FLOATX80
-typedef union {
- floatx80 f;
- struct {
- uint64_t low;
- uint16_t high;
- } i;
-} floatx80u;
-#endif
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE floating-point rounding mode.
-*----------------------------------------------------------------------------*/
-#if (defined(CONFIG_BSD) && !defined(__APPLE__) && !defined(__GLIBC__)) \
- || defined(CONFIG_SOLARIS)
-#if defined(__OpenBSD__)
-#define FE_RM FP_RM
-#define FE_RP FP_RP
-#define FE_RZ FP_RZ
-#endif
-enum {
- float_round_nearest_even = FP_RN,
- float_round_down = FP_RM,
- float_round_up = FP_RP,
- float_round_to_zero = FP_RZ
-};
-#else
-enum {
- float_round_nearest_even = FE_TONEAREST,
- float_round_down = FE_DOWNWARD,
- float_round_up = FE_UPWARD,
- float_round_to_zero = FE_TOWARDZERO
-};
-#endif
-
-typedef struct float_status {
- int float_rounding_mode;
-#ifdef FLOATX80
- int floatx80_rounding_precision;
-#endif
-} float_status;
-
-void set_float_rounding_mode(int val STATUS_PARAM);
-#ifdef FLOATX80
-void set_floatx80_rounding_precision(int val STATUS_PARAM);
-#endif
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE integer-to-floating-point conversion routines.
-*----------------------------------------------------------------------------*/
-float32 int32_to_float32( int STATUS_PARAM);
-float32 uint32_to_float32( unsigned int STATUS_PARAM);
-float64 int32_to_float64( int STATUS_PARAM);
-float64 uint32_to_float64( unsigned int STATUS_PARAM);
-#ifdef FLOATX80
-floatx80 int32_to_floatx80( int STATUS_PARAM);
-#endif
-#ifdef FLOAT128
-float128 int32_to_float128( int STATUS_PARAM);
-#endif
-float32 int64_to_float32( int64_t STATUS_PARAM);
-float32 uint64_to_float32( uint64_t STATUS_PARAM);
-float64 int64_to_float64( int64_t STATUS_PARAM);
-float64 uint64_to_float64( uint64_t v STATUS_PARAM);
-#ifdef FLOATX80
-floatx80 int64_to_floatx80( int64_t STATUS_PARAM);
-#endif
-#ifdef FLOAT128
-float128 int64_to_float128( int64_t STATUS_PARAM);
-#endif
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE single-precision conversion constants.
-*----------------------------------------------------------------------------*/
-#define float32_zero (0.0)
-#define float32_one (1.0)
-#define float32_ln2 (0.6931471)
-#define float32_pi (3.1415926)
-#define float32_half (0.5)
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE single-precision conversion routines.
-*----------------------------------------------------------------------------*/
-int float32_to_int32( float32 STATUS_PARAM);
-int float32_to_int32_round_to_zero( float32 STATUS_PARAM);
-unsigned int float32_to_uint32( float32 a STATUS_PARAM);
-unsigned int float32_to_uint32_round_to_zero( float32 a STATUS_PARAM);
-int64_t float32_to_int64( float32 STATUS_PARAM);
-int64_t float32_to_int64_round_to_zero( float32 STATUS_PARAM);
-float64 float32_to_float64( float32 STATUS_PARAM);
-#ifdef FLOATX80
-floatx80 float32_to_floatx80( float32 STATUS_PARAM);
-#endif
-#ifdef FLOAT128
-float128 float32_to_float128( float32 STATUS_PARAM);
-#endif
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE single-precision operations.
-*----------------------------------------------------------------------------*/
-float32 float32_round_to_int( float32 STATUS_PARAM);
-INLINE float32 float32_add( float32 a, float32 b STATUS_PARAM)
-{
- return a + b;
-}
-INLINE float32 float32_sub( float32 a, float32 b STATUS_PARAM)
-{
- return a - b;
-}
-INLINE float32 float32_mul( float32 a, float32 b STATUS_PARAM)
-{
- return a * b;
-}
-INLINE float32 float32_div( float32 a, float32 b STATUS_PARAM)
-{
- return a / b;
-}
-float32 float32_rem( float32, float32 STATUS_PARAM);
-float32 float32_sqrt( float32 STATUS_PARAM);
-INLINE int float32_eq_quiet( float32 a, float32 b STATUS_PARAM)
-{
- return a == b;
-}
-INLINE int float32_le( float32 a, float32 b STATUS_PARAM)
-{
- return a <= b;
-}
-INLINE int float32_lt( float32 a, float32 b STATUS_PARAM)
-{
- return a < b;
-}
-INLINE int float32_eq( float32 a, float32 b STATUS_PARAM)
-{
- return a <= b && a >= b;
-}
-INLINE int float32_le_quiet( float32 a, float32 b STATUS_PARAM)
-{
- return islessequal(a, b);
-}
-INLINE int float32_lt_quiet( float32 a, float32 b STATUS_PARAM)
-{
- return isless(a, b);
-}
-INLINE int float32_unordered( float32 a, float32 b STATUS_PARAM)
-{
- return isunordered(a, b);
-}
-INLINE int float32_unordered_quiet( float32 a, float32 b STATUS_PARAM)
-{
- return isunordered(a, b);
-}
-int float32_compare( float32, float32 STATUS_PARAM );
-int float32_compare_quiet( float32, float32 STATUS_PARAM );
-int float32_is_signaling_nan( float32 );
-int float32_is_quiet_nan( float32 );
-int float32_is_any_nan( float32 );
-
-INLINE float32 float32_abs(float32 a)
-{
- return fabsf(a);
-}
-
-INLINE float32 float32_chs(float32 a)
-{
- return -a;
-}
-
-INLINE float32 float32_is_infinity(float32 a)
-{
- return fpclassify(a) == FP_INFINITE;
-}
-
-INLINE float32 float32_is_neg(float32 a)
-{
- float32u u;
- u.f = a;
- return u.i >> 31;
-}
-
-INLINE float32 float32_is_zero(float32 a)
-{
- return fpclassify(a) == FP_ZERO;
-}
-
-INLINE float32 float32_scalbn(float32 a, int n STATUS_PARAM)
-{
- return scalbnf(a, n);
-}
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE double-precision conversion constants.
-*----------------------------------------------------------------------------*/
-#define float64_zero (0.0)
-#define float64_one (1.0)
-#define float64_ln2 (0.693147180559945)
-#define float64_pi (3.141592653589793)
-#define float64_half (0.5)
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE double-precision conversion routines.
-*----------------------------------------------------------------------------*/
-int float64_to_int32( float64 STATUS_PARAM );
-int float64_to_int32_round_to_zero( float64 STATUS_PARAM );
-unsigned int float64_to_uint32( float64 STATUS_PARAM );
-unsigned int float64_to_uint32_round_to_zero( float64 STATUS_PARAM );
-int64_t float64_to_int64( float64 STATUS_PARAM );
-int64_t float64_to_int64_round_to_zero( float64 STATUS_PARAM );
-uint64_t float64_to_uint64( float64 STATUS_PARAM );
-uint64_t float64_to_uint64_round_to_zero( float64 STATUS_PARAM );
-float32 float64_to_float32( float64 STATUS_PARAM );
-#ifdef FLOATX80
-floatx80 float64_to_floatx80( float64 STATUS_PARAM );
-#endif
-#ifdef FLOAT128
-float128 float64_to_float128( float64 STATUS_PARAM );
-#endif
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE double-precision operations.
-*----------------------------------------------------------------------------*/
-float64 float64_round_to_int( float64 STATUS_PARAM );
-float64 float64_trunc_to_int( float64 STATUS_PARAM );
-INLINE float64 float64_add( float64 a, float64 b STATUS_PARAM)
-{
- return a + b;
-}
-INLINE float64 float64_sub( float64 a, float64 b STATUS_PARAM)
-{
- return a - b;
-}
-INLINE float64 float64_mul( float64 a, float64 b STATUS_PARAM)
-{
- return a * b;
-}
-INLINE float64 float64_div( float64 a, float64 b STATUS_PARAM)
-{
- return a / b;
-}
-float64 float64_rem( float64, float64 STATUS_PARAM );
-float64 float64_sqrt( float64 STATUS_PARAM );
-INLINE int float64_eq_quiet( float64 a, float64 b STATUS_PARAM)
-{
- return a == b;
-}
-INLINE int float64_le( float64 a, float64 b STATUS_PARAM)
-{
- return a <= b;
-}
-INLINE int float64_lt( float64 a, float64 b STATUS_PARAM)
-{
- return a < b;
-}
-INLINE int float64_eq( float64 a, float64 b STATUS_PARAM)
-{
- return a <= b && a >= b;
-}
-INLINE int float64_le_quiet( float64 a, float64 b STATUS_PARAM)
-{
- return islessequal(a, b);
-}
-INLINE int float64_lt_quiet( float64 a, float64 b STATUS_PARAM)
-{
- return isless(a, b);
-
-}
-INLINE int float64_unordered( float64 a, float64 b STATUS_PARAM)
-{
- return isunordered(a, b);
-}
-INLINE int float64_unordered_quiet( float64 a, float64 b STATUS_PARAM)
-{
- return isunordered(a, b);
-}
-int float64_compare( float64, float64 STATUS_PARAM );
-int float64_compare_quiet( float64, float64 STATUS_PARAM );
-int float64_is_signaling_nan( float64 );
-int float64_is_any_nan( float64 );
-int float64_is_quiet_nan( float64 );
-
-INLINE float64 float64_abs(float64 a)
-{
- return fabs(a);
-}
-
-INLINE float64 float64_chs(float64 a)
-{
- return -a;
-}
-
-INLINE float64 float64_is_infinity(float64 a)
-{
- return fpclassify(a) == FP_INFINITE;
-}
-
-INLINE float64 float64_is_neg(float64 a)
-{
- float64u u;
- u.f = a;
- return u.i >> 63;
-}
-
-INLINE float64 float64_is_zero(float64 a)
-{
- return fpclassify(a) == FP_ZERO;
-}
-
-INLINE float64 float64_scalbn(float64 a, int n STATUS_PARAM)
-{
- return scalbn(a, n);
-}
-
-#ifdef FLOATX80
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE extended double-precision conversion constants.
-*----------------------------------------------------------------------------*/
-#define floatx80_zero (0.0L)
-#define floatx80_one (1.0L)
-#define floatx80_ln2 (0.69314718055994530943L)
-#define floatx80_pi (3.14159265358979323851L)
-#define floatx80_half (0.5L)
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE extended double-precision conversion routines.
-*----------------------------------------------------------------------------*/
-int floatx80_to_int32( floatx80 STATUS_PARAM );
-int floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM );
-int64_t floatx80_to_int64( floatx80 STATUS_PARAM);
-int64_t floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM);
-float32 floatx80_to_float32( floatx80 STATUS_PARAM );
-float64 floatx80_to_float64( floatx80 STATUS_PARAM );
-#ifdef FLOAT128
-float128 floatx80_to_float128( floatx80 STATUS_PARAM );
-#endif
-
-/*----------------------------------------------------------------------------
-| Software IEC/IEEE extended double-precision operations.
-*----------------------------------------------------------------------------*/
-floatx80 floatx80_round_to_int( floatx80 STATUS_PARAM );
-INLINE floatx80 floatx80_add( floatx80 a, floatx80 b STATUS_PARAM)
-{
- return a + b;
-}
-INLINE floatx80 floatx80_sub( floatx80 a, floatx80 b STATUS_PARAM)
-{
- return a - b;
-}
-INLINE floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM)
-{
- return a * b;
-}
-INLINE floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM)
-{
- return a / b;
-}
-floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM );
-floatx80 floatx80_sqrt( floatx80 STATUS_PARAM );
-INLINE int floatx80_eq_quiet( floatx80 a, floatx80 b STATUS_PARAM)
-{
- return a == b;
-}
-INLINE int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM)
-{
- return a <= b;
-}
-INLINE int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM)
-{
- return a < b;
-}
-INLINE int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM)
-{
- return a <= b && a >= b;
-}
-INLINE int floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM)
-{
- return islessequal(a, b);
-}
-INLINE int floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM)
-{
- return isless(a, b);
-
-}
-INLINE int floatx80_unordered( floatx80 a, floatx80 b STATUS_PARAM)
-{
- return isunordered(a, b);
-}
-INLINE int floatx80_unordered_quiet( floatx80 a, floatx80 b STATUS_PARAM)
-{
- return isunordered(a, b);
-}
-int floatx80_compare( floatx80, floatx80 STATUS_PARAM );
-int floatx80_compare_quiet( floatx80, floatx80 STATUS_PARAM );
-int floatx80_is_signaling_nan( floatx80 );
-int floatx80_is_quiet_nan( floatx80 );
-int floatx80_is_any_nan( floatx80 );
-
-INLINE floatx80 floatx80_abs(floatx80 a)
-{
- return fabsl(a);
-}
-
-INLINE floatx80 floatx80_chs(floatx80 a)
-{
- return -a;
-}
-
-INLINE floatx80 floatx80_is_infinity(floatx80 a)
-{
- return fpclassify(a) == FP_INFINITE;
-}
-
-INLINE floatx80 floatx80_is_neg(floatx80 a)
-{
- floatx80u u;
- u.f = a;
- return u.i.high >> 15;
-}
-
-INLINE floatx80 floatx80_is_zero(floatx80 a)
-{
- return fpclassify(a) == FP_ZERO;
-}
-
-INLINE floatx80 floatx80_scalbn(floatx80 a, int n STATUS_PARAM)
-{
- return scalbnl(a, n);
-}
-
-#endif
diff --git a/fpu/softfloat.h b/fpu/softfloat.h
index 58c9b7b..999b95c 100644
--- a/fpu/softfloat.h
+++ b/fpu/softfloat.h
@@ -81,16 +81,9 @@ typedef int64_t int64;
| input or output the `floatx80' type will be defined. The same applies to
| the `FLOAT128' macro and the quadruple-precision format `float128'.
*----------------------------------------------------------------------------*/
-#ifdef CONFIG_SOFTFLOAT
/* bit exact soft float support */
#define FLOATX80
#define FLOAT128
-#else
-/* native float support */
-#if (defined(__i386__) || defined(__x86_64__)) && !defined(CONFIG_BSD)
-#define FLOATX80
-#endif
-#endif /* !CONFIG_SOFTFLOAT */
#define STATUS_PARAM , float_status *status
#define STATUS(field) status->field
@@ -106,7 +99,6 @@ enum {
float_relation_unordered = 2
};
-#ifdef CONFIG_SOFTFLOAT
/*----------------------------------------------------------------------------
| Software IEC/IEEE floating-point types.
*----------------------------------------------------------------------------*/
@@ -699,10 +691,4 @@ INLINE int float128_is_any_nan(float128 a)
#endif
-#else /* CONFIG_SOFTFLOAT */
-
-#include "softfloat-native.h"
-
-#endif /* !CONFIG_SOFTFLOAT */
-
#endif /* !SOFTFLOAT_H */
commit c31da136a0bf8caad70c348f5ffc283206e9c7fc
Author: Aurelien Jarno <aurelien at aurel32.net>
Date: Sun May 15 14:09:18 2011 +0200
target-i386: remove old code handling float64
Now that target-i386 uses softfloat, floatx80 is always available and
there is no need anymore to have code handling both float64 and floax80.
Reviewed-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 715828f..fe65886 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -532,16 +532,6 @@ enum {
CC_OP_NB,
};
-#ifdef FLOATX80
-#define USE_X86LDOUBLE
-#endif
-
-#ifdef USE_X86LDOUBLE
-typedef floatx80 CPU86_LDouble;
-#else
-typedef float64 CPU86_LDouble;
-#endif
-
typedef struct SegmentCache {
uint32_t selector;
target_ulong base;
@@ -594,11 +584,7 @@ typedef union {
#define MMX_Q(n) q
typedef union {
-#ifdef USE_X86LDOUBLE
- CPU86_LDouble d __attribute__((aligned(16)));
-#else
- CPU86_LDouble d;
-#endif
+ floatx80 d __attribute__((aligned(16)));
MMXReg mmx;
} FPReg;
@@ -654,7 +640,7 @@ typedef struct CPUX86State {
/* emulator internal variables */
float_status fp_status;
- CPU86_LDouble ft0;
+ floatx80 ft0;
float_status mmx_status; /* for 3DNow! float ops */
float_status sse_status;
@@ -865,8 +851,8 @@ static inline void cpu_x86_set_cpl(CPUX86State *s, int cpl)
/* op_helper.c */
/* used for debug or cpu save/restore */
-void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, CPU86_LDouble f);
-CPU86_LDouble cpu_set_fp80(uint64_t mant, uint16_t upper);
+void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, floatx80 f);
+floatx80 cpu_set_fp80(uint64_t mant, uint16_t upper);
/* cpu-exec.c */
/* the following helpers are only usable in user mode simulation as
diff --git a/target-i386/exec.h b/target-i386/exec.h
index ee36a71..9bd080e 100644
--- a/target-i386/exec.h
+++ b/target-i386/exec.h
@@ -98,67 +98,6 @@ static inline void svm_check_intercept(uint32_t type)
#endif /* !defined(CONFIG_USER_ONLY) */
-#ifdef USE_X86LDOUBLE
-/* use long double functions */
-#define floatx_to_int32 floatx80_to_int32
-#define floatx_to_int64 floatx80_to_int64
-#define floatx_to_int32_round_to_zero floatx80_to_int32_round_to_zero
-#define floatx_to_int64_round_to_zero floatx80_to_int64_round_to_zero
-#define int32_to_floatx int32_to_floatx80
-#define int64_to_floatx int64_to_floatx80
-#define float32_to_floatx float32_to_floatx80
-#define float64_to_floatx float64_to_floatx80
-#define floatx_to_float32 floatx80_to_float32
-#define floatx_to_float64 floatx80_to_float64
-#define floatx_add floatx80_add
-#define floatx_div floatx80_div
-#define floatx_mul floatx80_mul
-#define floatx_sub floatx80_sub
-#define floatx_sqrt floatx80_sqrt
-#define floatx_abs floatx80_abs
-#define floatx_chs floatx80_chs
-#define floatx_scalbn floatx80_scalbn
-#define floatx_round_to_int floatx80_round_to_int
-#define floatx_compare floatx80_compare
-#define floatx_compare_quiet floatx80_compare_quiet
-#define floatx_is_any_nan floatx80_is_any_nan
-#define floatx_is_neg floatx80_is_neg
-#define floatx_is_zero floatx80_is_zero
-#define floatx_zero floatx80_zero
-#define floatx_one floatx80_one
-#define floatx_ln2 floatx80_ln2
-#define floatx_pi floatx80_pi
-#else
-#define floatx_to_int32 float64_to_int32
-#define floatx_to_int64 float64_to_int64
-#define floatx_to_int32_round_to_zero float64_to_int32_round_to_zero
-#define floatx_to_int64_round_to_zero float64_to_int64_round_to_zero
-#define int32_to_floatx int32_to_float64
-#define int64_to_floatx int64_to_float64
-#define float32_to_floatx float32_to_float64
-#define float64_to_floatx(x, e) (x)
-#define floatx_to_float32 float64_to_float32
-#define floatx_to_float64(x, e) (x)
-#define floatx_add float64_add
-#define floatx_div float64_div
-#define floatx_mul float64_mul
-#define floatx_sub float64_sub
-#define floatx_sqrt float64_sqrt
-#define floatx_abs float64_abs
-#define floatx_chs float64_chs
-#define floatx_scalbn float64_scalbn
-#define floatx_round_to_int float64_round_to_int
-#define floatx_compare float64_compare
-#define floatx_compare_quiet float64_compare_quiet
-#define floatx_is_any_nan float64_is_any_nan
-#define floatx_is_neg float64_is_neg
-#define floatx_is_zero float64_is_zero
-#define floatx_zero float64_zero
-#define floatx_one float64_one
-#define floatx_ln2 float64_ln2
-#define floatx_pi float64_pi
-#endif
-
#define RC_MASK 0xc00
#define RC_NEAR 0x000
#define RC_DOWN 0x400
@@ -167,11 +106,6 @@ static inline void svm_check_intercept(uint32_t type)
#define MAXTAN 9223372036854775808.0
-#ifdef USE_X86LDOUBLE
-
-/* only for x86 */
-typedef CPU_LDoubleU CPU86_LDoubleU;
-
/* the following deal with x86 long double-precision numbers */
#define MAXEXPD 0x7fff
#define EXPBIAS 16383
@@ -180,23 +114,6 @@ typedef CPU_LDoubleU CPU86_LDoubleU;
#define MANTD(fp) (fp.l.lower)
#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7fff)) | EXPBIAS
-#else
-
-typedef CPU_DoubleU CPU86_LDoubleU;
-
-/* the following deal with IEEE double-precision numbers */
-#define MAXEXPD 0x7ff
-#define EXPBIAS 1023
-#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF)
-#define SIGND(fp) ((fp.l.upper) & 0x80000000)
-#ifdef __arm__
-#define MANTD(fp) (fp.l.lower | ((uint64_t)(fp.l.upper & ((1 << 20) - 1)) << 32))
-#else
-#define MANTD(fp) (fp.ll & ((1LL << 52) - 1))
-#endif
-#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7ff << 20)) | (EXPBIAS << 20)
-#endif
-
static inline void fpush(void)
{
env->fpstt = (env->fpstt - 1) & 7;
@@ -209,65 +126,24 @@ static inline void fpop(void)
env->fpstt = (env->fpstt + 1) & 7;
}
-#ifndef USE_X86LDOUBLE
-static inline CPU86_LDouble helper_fldt(target_ulong ptr)
-{
- CPU86_LDoubleU temp;
- int upper, e;
- uint64_t ll;
-
- /* mantissa */
- upper = lduw(ptr + 8);
- /* XXX: handle overflow ? */
- e = (upper & 0x7fff) - 16383 + EXPBIAS; /* exponent */
- e |= (upper >> 4) & 0x800; /* sign */
- ll = (ldq(ptr) >> 11) & ((1LL << 52) - 1);
-#ifdef __arm__
- temp.l.upper = (e << 20) | (ll >> 32);
- temp.l.lower = ll;
-#else
- temp.ll = ll | ((uint64_t)e << 52);
-#endif
- return temp.d;
-}
-
-static inline void helper_fstt(CPU86_LDouble f, target_ulong ptr)
+static inline floatx80 helper_fldt(target_ulong ptr)
{
- CPU86_LDoubleU temp;
- int e;
-
- temp.d = f;
- /* mantissa */
- stq(ptr, (MANTD(temp) << 11) | (1LL << 63));
- /* exponent + sign */
- e = EXPD(temp) - EXPBIAS + 16383;
- e |= SIGND(temp) >> 16;
- stw(ptr + 8, e);
-}
-#else
-
-/* we use memory access macros */
-
-static inline CPU86_LDouble helper_fldt(target_ulong ptr)
-{
- CPU86_LDoubleU temp;
+ CPU_LDoubleU temp;
temp.l.lower = ldq(ptr);
temp.l.upper = lduw(ptr + 8);
return temp.d;
}
-static inline void helper_fstt(CPU86_LDouble f, target_ulong ptr)
+static inline void helper_fstt(floatx80 f, target_ulong ptr)
{
- CPU86_LDoubleU temp;
+ CPU_LDoubleU temp;
temp.d = f;
stq(ptr, temp.l.lower);
stw(ptr + 8, temp.l.upper);
}
-#endif /* USE_X86LDOUBLE */
-
#define FPUS_IE (1 << 0)
#define FPUS_DE (1 << 1)
#define FPUS_ZE (1 << 2)
diff --git a/target-i386/helper.c b/target-i386/helper.c
index 89df997..5c4b288 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -403,15 +403,10 @@ void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
fptag,
env->mxcsr);
for(i=0;i<8;i++) {
-#if defined(USE_X86LDOUBLE)
CPU_LDoubleU u;
u.d = env->fpregs[i].d;
cpu_fprintf(f, "FPR%d=%016" PRIx64 " %04x",
i, u.l.lower, u.l.upper);
-#else
- cpu_fprintf(f, "FPR%d=%016" PRIx64,
- i, env->fpregs[i].mmx.q);
-#endif
if ((i & 1) == 1)
cpu_fprintf(f, "\n");
else
diff --git a/target-i386/machine.c b/target-i386/machine.c
index d78eceb..bbeae88 100644
--- a/target-i386/machine.c
+++ b/target-i386/machine.c
@@ -84,7 +84,6 @@ static void put_fpreg_error(QEMUFile *f, void *opaque, size_t size)
exit(0);
}
-#ifdef USE_X86LDOUBLE
/* XXX: add that in a FPU generic layer */
union x86_longdouble {
uint64_t mant;
@@ -202,102 +201,6 @@ static bool fpregs_is_1_no_mmx(void *opaque, int version_id)
VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_1_mmx, vmstate_fpreg_1_mmx, FPReg), \
VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_1_no_mmx, vmstate_fpreg_1_no_mmx, FPReg)
-#else
-static int get_fpreg(QEMUFile *f, void *opaque, size_t size)
-{
- FPReg *fp_reg = opaque;
-
- qemu_get_be64s(f, &fp_reg->mmx.MMX_Q(0));
- return 0;
-}
-
-static void put_fpreg(QEMUFile *f, void *opaque, size_t size)
-{
- FPReg *fp_reg = opaque;
- /* if we use doubles for float emulation, we save the doubles to
- avoid losing information in case of MMX usage. It can give
- problems if the image is restored on a CPU where long
- doubles are used instead. */
- qemu_put_be64s(f, &fp_reg->mmx.MMX_Q(0));
-}
-
-const VMStateInfo vmstate_fpreg = {
- .name = "fpreg",
- .get = get_fpreg,
- .put = put_fpreg,
-};
-
-static int get_fpreg_0_mmx(QEMUFile *f, void *opaque, size_t size)
-{
- FPReg *fp_reg = opaque;
- uint64_t mant;
- uint16_t exp;
-
- qemu_get_be64s(f, &mant);
- qemu_get_be16s(f, &exp);
- fp_reg->mmx.MMX_Q(0) = mant;
- return 0;
-}
-
-const VMStateInfo vmstate_fpreg_0_mmx = {
- .name = "fpreg_0_mmx",
- .get = get_fpreg_0_mmx,
- .put = put_fpreg_error,
-};
-
-static int get_fpreg_0_no_mmx(QEMUFile *f, void *opaque, size_t size)
-{
- FPReg *fp_reg = opaque;
- uint64_t mant;
- uint16_t exp;
-
- qemu_get_be64s(f, &mant);
- qemu_get_be16s(f, &exp);
-
- fp_reg->d = cpu_set_fp80(mant, exp);
- return 0;
-}
-
-const VMStateInfo vmstate_fpreg_0_no_mmx = {
- .name = "fpreg_0_no_mmx",
- .get = get_fpreg_0_no_mmx,
- .put = put_fpreg_error,
-};
-
-static bool fpregs_is_1(void *opaque, int version_id)
-{
- CPUState *env = opaque;
-
- return env->fpregs_format_vmstate == 1;
-}
-
-static bool fpregs_is_0_mmx(void *opaque, int version_id)
-{
- CPUState *env = opaque;
- int guess_mmx;
-
- guess_mmx = ((env->fptag_vmstate == 0xff) &&
- (env->fpus_vmstate & 0x3800) == 0);
- return guess_mmx && env->fpregs_format_vmstate == 0;
-}
-
-static bool fpregs_is_0_no_mmx(void *opaque, int version_id)
-{
- CPUState *env = opaque;
- int guess_mmx;
-
- guess_mmx = ((env->fptag_vmstate == 0xff) &&
- (env->fpus_vmstate & 0x3800) == 0);
- return !guess_mmx && env->fpregs_format_vmstate == 0;
-}
-
-#define VMSTATE_FP_REGS(_field, _state, _n) \
- VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_1, vmstate_fpreg, FPReg), \
- VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_0_mmx, vmstate_fpreg_0_mmx, FPReg), \
- VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_0_no_mmx, vmstate_fpreg_0_no_mmx, FPReg)
-
-#endif /* USE_X86LDOUBLE */
-
static bool version_is_5(void *opaque, int version_id)
{
return version_id == 5;
@@ -344,11 +247,7 @@ static void cpu_pre_save(void *opaque)
env->fptag_vmstate |= ((!env->fptags[i]) << i);
}
-#ifdef USE_X86LDOUBLE
env->fpregs_format_vmstate = 0;
-#else
- env->fpregs_format_vmstate = 1;
-#endif
}
static int cpu_post_load(void *opaque, int version_id)
diff --git a/target-i386/op_helper.c b/target-i386/op_helper.c
index 3c539f3..4d309ab 100644
--- a/target-i386/op_helper.c
+++ b/target-i386/op_helper.c
@@ -95,25 +95,19 @@ static const uint8_t rclb_table[32] = {
6, 7, 8, 0, 1, 2, 3, 4,
};
-#if defined(CONFIG_SOFTFLOAT)
-# define floatx_lg2 make_floatx80( 0x3ffd, 0x9a209a84fbcff799LL )
-# define floatx_l2e make_floatx80( 0x3fff, 0xb8aa3b295c17f0bcLL )
-# define floatx_l2t make_floatx80( 0x4000, 0xd49a784bcd1b8afeLL )
-#else
-# define floatx_lg2 (0.30102999566398119523L)
-# define floatx_l2e (1.44269504088896340739L)
-# define floatx_l2t (3.32192809488736234781L)
-#endif
-
-static const CPU86_LDouble f15rk[7] =
-{
- floatx_zero,
- floatx_one,
- floatx_pi,
- floatx_lg2,
- floatx_ln2,
- floatx_l2e,
- floatx_l2t,
+#define floatx80_lg2 make_floatx80( 0x3ffd, 0x9a209a84fbcff799LL )
+#define floatx80_l2e make_floatx80( 0x3fff, 0xb8aa3b295c17f0bcLL )
+#define floatx80_l2t make_floatx80( 0x4000, 0xd49a784bcd1b8afeLL )
+
+static const floatx80 f15rk[7] =
+{
+ floatx80_zero,
+ floatx80_one,
+ floatx80_pi,
+ floatx80_lg2,
+ floatx80_ln2,
+ floatx80_l2e,
+ floatx80_l2t,
};
/* broken thread support */
@@ -3442,18 +3436,18 @@ void helper_verw(target_ulong selector1)
/* x87 FPU helpers */
-static inline double CPU86_LDouble_to_double(CPU86_LDouble a)
+static inline double floatx80_to_double(floatx80 a)
{
union {
float64 f64;
double d;
} u;
- u.f64 = floatx_to_float64(a, &env->fp_status);
+ u.f64 = floatx80_to_float64(a, &env->fp_status);
return u.d;
}
-static inline CPU86_LDouble double_to_CPU86_LDouble(double a)
+static inline floatx80 double_to_floatx80(double a)
{
union {
float64 f64;
@@ -3461,7 +3455,7 @@ static inline CPU86_LDouble double_to_CPU86_LDouble(double a)
} u;
u.d = a;
- return float64_to_floatx(u.f64, &env->fp_status);
+ return float64_to_floatx80(u.f64, &env->fp_status);
}
static void fpu_set_exception(int mask)
@@ -3471,12 +3465,12 @@ static void fpu_set_exception(int mask)
env->fpus |= FPUS_SE | FPUS_B;
}
-static inline CPU86_LDouble helper_fdiv(CPU86_LDouble a, CPU86_LDouble b)
+static inline floatx80 helper_fdiv(floatx80 a, floatx80 b)
{
- if (floatx_is_zero(b)) {
+ if (floatx80_is_zero(b)) {
fpu_set_exception(FPUS_ZE);
}
- return floatx_div(a, b, &env->fp_status);
+ return floatx80_div(a, b, &env->fp_status);
}
static void fpu_raise_exception(void)
@@ -3498,7 +3492,7 @@ void helper_flds_FT0(uint32_t val)
uint32_t i;
} u;
u.i = val;
- FT0 = float32_to_floatx(u.f, &env->fp_status);
+ FT0 = float32_to_floatx80(u.f, &env->fp_status);
}
void helper_fldl_FT0(uint64_t val)
@@ -3508,12 +3502,12 @@ void helper_fldl_FT0(uint64_t val)
uint64_t i;
} u;
u.i = val;
- FT0 = float64_to_floatx(u.f, &env->fp_status);
+ FT0 = float64_to_floatx80(u.f, &env->fp_status);
}
void helper_fildl_FT0(int32_t val)
{
- FT0 = int32_to_floatx(val, &env->fp_status);
+ FT0 = int32_to_floatx80(val, &env->fp_status);
}
void helper_flds_ST0(uint32_t val)
@@ -3525,7 +3519,7 @@ void helper_flds_ST0(uint32_t val)
} u;
new_fpstt = (env->fpstt - 1) & 7;
u.i = val;
- env->fpregs[new_fpstt].d = float32_to_floatx(u.f, &env->fp_status);
+ env->fpregs[new_fpstt].d = float32_to_floatx80(u.f, &env->fp_status);
env->fpstt = new_fpstt;
env->fptags[new_fpstt] = 0; /* validate stack entry */
}
@@ -3539,7 +3533,7 @@ void helper_fldl_ST0(uint64_t val)
} u;
new_fpstt = (env->fpstt - 1) & 7;
u.i = val;
- env->fpregs[new_fpstt].d = float64_to_floatx(u.f, &env->fp_status);
+ env->fpregs[new_fpstt].d = float64_to_floatx80(u.f, &env->fp_status);
env->fpstt = new_fpstt;
env->fptags[new_fpstt] = 0; /* validate stack entry */
}
@@ -3548,7 +3542,7 @@ void helper_fildl_ST0(int32_t val)
{
int new_fpstt;
new_fpstt = (env->fpstt - 1) & 7;
- env->fpregs[new_fpstt].d = int32_to_floatx(val, &env->fp_status);
+ env->fpregs[new_fpstt].d = int32_to_floatx80(val, &env->fp_status);
env->fpstt = new_fpstt;
env->fptags[new_fpstt] = 0; /* validate stack entry */
}
@@ -3557,7 +3551,7 @@ void helper_fildll_ST0(int64_t val)
{
int new_fpstt;
new_fpstt = (env->fpstt - 1) & 7;
- env->fpregs[new_fpstt].d = int64_to_floatx(val, &env->fp_status);
+ env->fpregs[new_fpstt].d = int64_to_floatx80(val, &env->fp_status);
env->fpstt = new_fpstt;
env->fptags[new_fpstt] = 0; /* validate stack entry */
}
@@ -3568,7 +3562,7 @@ uint32_t helper_fsts_ST0(void)
float32 f;
uint32_t i;
} u;
- u.f = floatx_to_float32(ST0, &env->fp_status);
+ u.f = floatx80_to_float32(ST0, &env->fp_status);
return u.i;
}
@@ -3578,14 +3572,14 @@ uint64_t helper_fstl_ST0(void)
float64 f;
uint64_t i;
} u;
- u.f = floatx_to_float64(ST0, &env->fp_status);
+ u.f = floatx80_to_float64(ST0, &env->fp_status);
return u.i;
}
int32_t helper_fist_ST0(void)
{
int32_t val;
- val = floatx_to_int32(ST0, &env->fp_status);
+ val = floatx80_to_int32(ST0, &env->fp_status);
if (val != (int16_t)val)
val = -32768;
return val;
@@ -3594,21 +3588,21 @@ int32_t helper_fist_ST0(void)
int32_t helper_fistl_ST0(void)
{
int32_t val;
- val = floatx_to_int32(ST0, &env->fp_status);
+ val = floatx80_to_int32(ST0, &env->fp_status);
return val;
}
int64_t helper_fistll_ST0(void)
{
int64_t val;
- val = floatx_to_int64(ST0, &env->fp_status);
+ val = floatx80_to_int64(ST0, &env->fp_status);
return val;
}
int32_t helper_fistt_ST0(void)
{
int32_t val;
- val = floatx_to_int32_round_to_zero(ST0, &env->fp_status);
+ val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status);
if (val != (int16_t)val)
val = -32768;
return val;
@@ -3617,14 +3611,14 @@ int32_t helper_fistt_ST0(void)
int32_t helper_fisttl_ST0(void)
{
int32_t val;
- val = floatx_to_int32_round_to_zero(ST0, &env->fp_status);
+ val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status);
return val;
}
int64_t helper_fisttll_ST0(void)
{
int64_t val;
- val = floatx_to_int64_round_to_zero(ST0, &env->fp_status);
+ val = floatx80_to_int64_round_to_zero(ST0, &env->fp_status);
return val;
}
@@ -3693,7 +3687,7 @@ void helper_fmov_STN_ST0(int st_index)
void helper_fxchg_ST0_STN(int st_index)
{
- CPU86_LDouble tmp;
+ floatx80 tmp;
tmp = ST(st_index);
ST(st_index) = ST0;
ST0 = tmp;
@@ -3707,7 +3701,7 @@ void helper_fcom_ST0_FT0(void)
{
int ret;
- ret = floatx_compare(ST0, FT0, &env->fp_status);
+ ret = floatx80_compare(ST0, FT0, &env->fp_status);
env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1];
}
@@ -3715,7 +3709,7 @@ void helper_fucom_ST0_FT0(void)
{
int ret;
- ret = floatx_compare_quiet(ST0, FT0, &env->fp_status);
+ ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status);
env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret+ 1];
}
@@ -3726,7 +3720,7 @@ void helper_fcomi_ST0_FT0(void)
int eflags;
int ret;
- ret = floatx_compare(ST0, FT0, &env->fp_status);
+ ret = floatx80_compare(ST0, FT0, &env->fp_status);
eflags = helper_cc_compute_all(CC_OP);
eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
CC_SRC = eflags;
@@ -3737,7 +3731,7 @@ void helper_fucomi_ST0_FT0(void)
int eflags;
int ret;
- ret = floatx_compare_quiet(ST0, FT0, &env->fp_status);
+ ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status);
eflags = helper_cc_compute_all(CC_OP);
eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1];
CC_SRC = eflags;
@@ -3745,22 +3739,22 @@ void helper_fucomi_ST0_FT0(void)
void helper_fadd_ST0_FT0(void)
{
- ST0 = floatx_add(ST0, FT0, &env->fp_status);
+ ST0 = floatx80_add(ST0, FT0, &env->fp_status);
}
void helper_fmul_ST0_FT0(void)
{
- ST0 = floatx_mul(ST0, FT0, &env->fp_status);
+ ST0 = floatx80_mul(ST0, FT0, &env->fp_status);
}
void helper_fsub_ST0_FT0(void)
{
- ST0 = floatx_sub(ST0, FT0, &env->fp_status);
+ ST0 = floatx80_sub(ST0, FT0, &env->fp_status);
}
void helper_fsubr_ST0_FT0(void)
{
- ST0 = floatx_sub(FT0, ST0, &env->fp_status);
+ ST0 = floatx80_sub(FT0, ST0, &env->fp_status);
}
void helper_fdiv_ST0_FT0(void)
@@ -3777,34 +3771,34 @@ void helper_fdivr_ST0_FT0(void)
void helper_fadd_STN_ST0(int st_index)
{
- ST(st_index) = floatx_add(ST(st_index), ST0, &env->fp_status);
+ ST(st_index) = floatx80_add(ST(st_index), ST0, &env->fp_status);
}
void helper_fmul_STN_ST0(int st_index)
{
- ST(st_index) = floatx_mul(ST(st_index), ST0, &env->fp_status);
+ ST(st_index) = floatx80_mul(ST(st_index), ST0, &env->fp_status);
}
void helper_fsub_STN_ST0(int st_index)
{
- ST(st_index) = floatx_sub(ST(st_index), ST0, &env->fp_status);
+ ST(st_index) = floatx80_sub(ST(st_index), ST0, &env->fp_status);
}
void helper_fsubr_STN_ST0(int st_index)
{
- ST(st_index) = floatx_sub(ST0, ST(st_index), &env->fp_status);
+ ST(st_index) = floatx80_sub(ST0, ST(st_index), &env->fp_status);
}
void helper_fdiv_STN_ST0(int st_index)
{
- CPU86_LDouble *p;
+ floatx80 *p;
p = &ST(st_index);
*p = helper_fdiv(*p, ST0);
}
void helper_fdivr_STN_ST0(int st_index)
{
- CPU86_LDouble *p;
+ floatx80 *p;
p = &ST(st_index);
*p = helper_fdiv(ST0, *p);
}
@@ -3812,12 +3806,12 @@ void helper_fdivr_STN_ST0(int st_index)
/* misc FPU operations */
void helper_fchs_ST0(void)
{
- ST0 = floatx_chs(ST0);
+ ST0 = floatx80_chs(ST0);
}
void helper_fabs_ST0(void)
{
- ST0 = floatx_abs(ST0);
+ ST0 = floatx80_abs(ST0);
}
void helper_fld1_ST0(void)
@@ -3891,7 +3885,6 @@ static void update_fp_status(void)
break;
}
set_float_rounding_mode(rnd_type, &env->fp_status);
-#ifdef FLOATX80
switch((env->fpuc >> 8) & 3) {
case 0:
rnd_type = 32;
@@ -3905,7 +3898,6 @@ static void update_fp_status(void)
break;
}
set_floatx80_rounding_precision(rnd_type, &env->fp_status);
-#endif
}
void helper_fldcw(uint32_t val)
@@ -3944,7 +3936,7 @@ void helper_fninit(void)
void helper_fbld_ST0(target_ulong ptr)
{
- CPU86_LDouble tmp;
+ floatx80 tmp;
uint64_t val;
unsigned int v;
int i;
@@ -3954,9 +3946,9 @@ void helper_fbld_ST0(target_ulong ptr)
v = ldub(ptr + i);
val = (val * 100) + ((v >> 4) * 10) + (v & 0xf);
}
- tmp = int64_to_floatx(val, &env->fp_status);
+ tmp = int64_to_floatx80(val, &env->fp_status);
if (ldub(ptr + 9) & 0x80) {
- floatx_chs(tmp);
+ floatx80_chs(tmp);
}
fpush();
ST0 = tmp;
@@ -3968,7 +3960,7 @@ void helper_fbst_ST0(target_ulong ptr)
target_ulong mem_ref, mem_end;
int64_t val;
- val = floatx_to_int64(ST0, &env->fp_status);
+ val = floatx80_to_int64(ST0, &env->fp_status);
mem_ref = ptr;
mem_end = mem_ref + 9;
if (val < 0) {
@@ -3992,19 +3984,19 @@ void helper_fbst_ST0(target_ulong ptr)
void helper_f2xm1(void)
{
- double val = CPU86_LDouble_to_double(ST0);
+ double val = floatx80_to_double(ST0);
val = pow(2.0, val) - 1.0;
- ST0 = double_to_CPU86_LDouble(val);
+ ST0 = double_to_floatx80(val);
}
void helper_fyl2x(void)
{
- double fptemp = CPU86_LDouble_to_double(ST0);
+ double fptemp = floatx80_to_double(ST0);
if (fptemp>0.0){
fptemp = log(fptemp)/log(2.0); /* log2(ST) */
- fptemp *= CPU86_LDouble_to_double(ST1);
- ST1 = double_to_CPU86_LDouble(fptemp);
+ fptemp *= floatx80_to_double(ST1);
+ ST1 = double_to_floatx80(fptemp);
fpop();
} else {
env->fpus &= (~0x4700);
@@ -4014,15 +4006,15 @@ void helper_fyl2x(void)
void helper_fptan(void)
{
- double fptemp = CPU86_LDouble_to_double(ST0);
+ double fptemp = floatx80_to_double(ST0);
if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
env->fpus |= 0x400;
} else {
fptemp = tan(fptemp);
- ST0 = double_to_CPU86_LDouble(fptemp);
+ ST0 = double_to_floatx80(fptemp);
fpush();
- ST0 = floatx_one;
+ ST0 = floatx80_one;
env->fpus &= (~0x400); /* C2 <-- 0 */
/* the above code is for |arg| < 2**52 only */
}
@@ -4032,21 +4024,21 @@ void helper_fpatan(void)
{
double fptemp, fpsrcop;
- fpsrcop = CPU86_LDouble_to_double(ST1);
- fptemp = CPU86_LDouble_to_double(ST0);
- ST1 = double_to_CPU86_LDouble(atan2(fpsrcop, fptemp));
+ fpsrcop = floatx80_to_double(ST1);
+ fptemp = floatx80_to_double(ST0);
+ ST1 = double_to_floatx80(atan2(fpsrcop, fptemp));
fpop();
}
void helper_fxtract(void)
{
- CPU86_LDoubleU temp;
+ CPU_LDoubleU temp;
temp.d = ST0;
- if (floatx_is_zero(ST0)) {
+ if (floatx80_is_zero(ST0)) {
/* Easy way to generate -inf and raising division by 0 exception */
- ST0 = floatx_div(floatx_chs(floatx_one), floatx_zero, &env->fp_status);
+ ST0 = floatx80_div(floatx80_chs(floatx80_one), floatx80_zero, &env->fp_status);
fpush();
ST0 = temp.d;
} else {
@@ -4054,7 +4046,7 @@ void helper_fxtract(void)
expdif = EXPD(temp) - EXPBIAS;
/*DP exponent bias*/
- ST0 = int32_to_floatx(expdif, &env->fp_status);
+ ST0 = int32_to_floatx80(expdif, &env->fp_status);
fpush();
BIASEXPONENT(temp);
ST0 = temp.d;
@@ -4064,15 +4056,15 @@ void helper_fxtract(void)
void helper_fprem1(void)
{
double st0, st1, dblq, fpsrcop, fptemp;
- CPU86_LDoubleU fpsrcop1, fptemp1;
+ CPU_LDoubleU fpsrcop1, fptemp1;
int expdif;
signed long long int q;
- st0 = CPU86_LDouble_to_double(ST0);
- st1 = CPU86_LDouble_to_double(ST1);
+ st0 = floatx80_to_double(ST0);
+ st1 = floatx80_to_double(ST1);
if (isinf(st0) || isnan(st0) || isnan(st1) || (st1 == 0.0)) {
- ST0 = double_to_CPU86_LDouble(0.0 / 0.0); /* NaN */
+ ST0 = double_to_floatx80(0.0 / 0.0); /* NaN */
env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
return;
}
@@ -4116,21 +4108,21 @@ void helper_fprem1(void)
-(floor(fabs(fpsrcop))) : floor(fpsrcop);
st0 -= (st1 * fpsrcop * fptemp);
}
- ST0 = double_to_CPU86_LDouble(st0);
+ ST0 = double_to_floatx80(st0);
}
void helper_fprem(void)
{
double st0, st1, dblq, fpsrcop, fptemp;
- CPU86_LDoubleU fpsrcop1, fptemp1;
+ CPU_LDoubleU fpsrcop1, fptemp1;
int expdif;
signed long long int q;
- st0 = CPU86_LDouble_to_double(ST0);
- st1 = CPU86_LDouble_to_double(ST1);
+ st0 = floatx80_to_double(ST0);
+ st1 = floatx80_to_double(ST1);
if (isinf(st0) || isnan(st0) || isnan(st1) || (st1 == 0.0)) {
- ST0 = double_to_CPU86_LDouble(0.0 / 0.0); /* NaN */
+ ST0 = double_to_floatx80(0.0 / 0.0); /* NaN */
env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
return;
}
@@ -4175,17 +4167,17 @@ void helper_fprem(void)
-(floor(fabs(fpsrcop))) : floor(fpsrcop);
st0 -= (st1 * fpsrcop * fptemp);
}
- ST0 = double_to_CPU86_LDouble(st0);
+ ST0 = double_to_floatx80(st0);
}
void helper_fyl2xp1(void)
{
- double fptemp = CPU86_LDouble_to_double(ST0);
+ double fptemp = floatx80_to_double(ST0);
if ((fptemp+1.0)>0.0) {
fptemp = log(fptemp+1.0) / log(2.0); /* log2(ST+1.0) */
- fptemp *= CPU86_LDouble_to_double(ST1);
- ST1 = double_to_CPU86_LDouble(fptemp);
+ fptemp *= floatx80_to_double(ST1);
+ ST1 = double_to_floatx80(fptemp);
fpop();
} else {
env->fpus &= (~0x4700);
@@ -4195,23 +4187,23 @@ void helper_fyl2xp1(void)
void helper_fsqrt(void)
{
- if (floatx_is_neg(ST0)) {
+ if (floatx80_is_neg(ST0)) {
env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
env->fpus |= 0x400;
}
- ST0 = floatx_sqrt(ST0, &env->fp_status);
+ ST0 = floatx80_sqrt(ST0, &env->fp_status);
}
void helper_fsincos(void)
{
- double fptemp = CPU86_LDouble_to_double(ST0);
+ double fptemp = floatx80_to_double(ST0);
if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
env->fpus |= 0x400;
} else {
- ST0 = double_to_CPU86_LDouble(sin(fptemp));
+ ST0 = double_to_floatx80(sin(fptemp));
fpush();
- ST0 = double_to_CPU86_LDouble(cos(fptemp));
+ ST0 = double_to_floatx80(cos(fptemp));
env->fpus &= (~0x400); /* C2 <-- 0 */
/* the above code is for |arg| < 2**63 only */
}
@@ -4219,27 +4211,27 @@ void helper_fsincos(void)
void helper_frndint(void)
{
- ST0 = floatx_round_to_int(ST0, &env->fp_status);
+ ST0 = floatx80_round_to_int(ST0, &env->fp_status);
}
void helper_fscale(void)
{
- if (floatx_is_any_nan(ST1)) {
+ if (floatx80_is_any_nan(ST1)) {
ST0 = ST1;
} else {
- int n = floatx_to_int32_round_to_zero(ST1, &env->fp_status);
- ST0 = floatx_scalbn(ST0, n, &env->fp_status);
+ int n = floatx80_to_int32_round_to_zero(ST1, &env->fp_status);
+ ST0 = floatx80_scalbn(ST0, n, &env->fp_status);
}
}
void helper_fsin(void)
{
- double fptemp = CPU86_LDouble_to_double(ST0);
+ double fptemp = floatx80_to_double(ST0);
if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
env->fpus |= 0x400;
} else {
- ST0 = double_to_CPU86_LDouble(sin(fptemp));
+ ST0 = double_to_floatx80(sin(fptemp));
env->fpus &= (~0x400); /* C2 <-- 0 */
/* the above code is for |arg| < 2**53 only */
}
@@ -4247,12 +4239,12 @@ void helper_fsin(void)
void helper_fcos(void)
{
- double fptemp = CPU86_LDouble_to_double(ST0);
+ double fptemp = floatx80_to_double(ST0);
if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
env->fpus |= 0x400;
} else {
- ST0 = double_to_CPU86_LDouble(cos(fptemp));
+ ST0 = double_to_floatx80(cos(fptemp));
env->fpus &= (~0x400); /* C2 <-- 0 */
/* the above code is for |arg5 < 2**63 only */
}
@@ -4260,7 +4252,7 @@ void helper_fcos(void)
void helper_fxam_ST0(void)
{
- CPU86_LDoubleU temp;
+ CPU_LDoubleU temp;
int expdif;
temp.d = ST0;
@@ -4272,11 +4264,7 @@ void helper_fxam_ST0(void)
/* XXX: test fptags too */
expdif = EXPD(temp);
if (expdif == MAXEXPD) {
-#ifdef USE_X86LDOUBLE
if (MANTD(temp) == 0x8000000000000000ULL)
-#else
- if (MANTD(temp) == 0)
-#endif
env->fpus |= 0x500 /*Infinity*/;
else
env->fpus |= 0x100 /*NaN*/;
@@ -4294,7 +4282,7 @@ void helper_fstenv(target_ulong ptr, int data32)
{
int fpus, fptag, exp, i;
uint64_t mant;
- CPU86_LDoubleU tmp;
+ CPU_LDoubleU tmp;
fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
fptag = 0;
@@ -4310,9 +4298,7 @@ void helper_fstenv(target_ulong ptr, int data32)
/* zero */
fptag |= 1;
} else if (exp == 0 || exp == MAXEXPD
-#ifdef USE_X86LDOUBLE
|| (mant & (1LL << 63)) == 0
-#endif
) {
/* NaNs, infinity, denormal */
fptag |= 2;
@@ -4364,7 +4350,7 @@ void helper_fldenv(target_ulong ptr, int data32)
void helper_fsave(target_ulong ptr, int data32)
{
- CPU86_LDouble tmp;
+ floatx80 tmp;
int i;
helper_fstenv(ptr, data32);
@@ -4392,7 +4378,7 @@ void helper_fsave(target_ulong ptr, int data32)
void helper_frstor(target_ulong ptr, int data32)
{
- CPU86_LDouble tmp;
+ floatx80 tmp;
int i;
helper_fldenv(ptr, data32);
@@ -4408,7 +4394,7 @@ void helper_frstor(target_ulong ptr, int data32)
void helper_fxsave(target_ulong ptr, int data64)
{
int fpus, fptag, i, nb_xmm_regs;
- CPU86_LDouble tmp;
+ floatx80 tmp;
target_ulong addr;
/* The operand must be 16 byte aligned */
@@ -4469,7 +4455,7 @@ void helper_fxsave(target_ulong ptr, int data64)
void helper_fxrstor(target_ulong ptr, int data64)
{
int i, fpus, fptag, nb_xmm_regs;
- CPU86_LDouble tmp;
+ floatx80 tmp;
target_ulong addr;
/* The operand must be 16 byte aligned */
@@ -4516,61 +4502,23 @@ void helper_fxrstor(target_ulong ptr, int data64)
}
}
-#ifndef USE_X86LDOUBLE
-
-void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, CPU86_LDouble f)
+void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, floatx80 f)
{
- CPU86_LDoubleU temp;
- int e;
-
- temp.d = f;
- /* mantissa */
- *pmant = (MANTD(temp) << 11) | (1LL << 63);
- /* exponent + sign */
- e = EXPD(temp) - EXPBIAS + 16383;
- e |= SIGND(temp) >> 16;
- *pexp = e;
-}
-
-CPU86_LDouble cpu_set_fp80(uint64_t mant, uint16_t upper)
-{
- CPU86_LDoubleU temp;
- int e;
- uint64_t ll;
-
- /* XXX: handle overflow ? */
- e = (upper & 0x7fff) - 16383 + EXPBIAS; /* exponent */
- e |= (upper >> 4) & 0x800; /* sign */
- ll = (mant >> 11) & ((1LL << 52) - 1);
-#ifdef __arm__
- temp.l.upper = (e << 20) | (ll >> 32);
- temp.l.lower = ll;
-#else
- temp.ll = ll | ((uint64_t)e << 52);
-#endif
- return temp.d;
-}
-
-#else
-
-void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, CPU86_LDouble f)
-{
- CPU86_LDoubleU temp;
+ CPU_LDoubleU temp;
temp.d = f;
*pmant = temp.l.lower;
*pexp = temp.l.upper;
}
-CPU86_LDouble cpu_set_fp80(uint64_t mant, uint16_t upper)
+floatx80 cpu_set_fp80(uint64_t mant, uint16_t upper)
{
- CPU86_LDoubleU temp;
+ CPU_LDoubleU temp;
temp.l.upper = upper;
temp.l.lower = mant;
return temp.d;
}
-#endif
#ifdef TARGET_X86_64
commit 142ab5bb872337054581d8067906a6541704a5e0
Author: Aurelien Jarno <aurelien at aurel32.net>
Date: Sun May 15 14:09:18 2011 +0200
target-mips/gdbstub: remove old CONFIG_SOFTFLOAT #ifndef
target-mips has been switched to softfloat only long ago, but
a #ifndef CONFIG_SOFTFLOAT has been forgotten. Remove it.
Reviewed-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/gdbstub.c b/gdbstub.c
index ae856f9..b9ae30d 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1105,10 +1105,6 @@ static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
env->active_fpu.fcr31 = tmp & 0xFF83FFFF;
/* set rounding mode */
RESTORE_ROUNDING_MODE;
-#ifndef CONFIG_SOFTFLOAT
- /* no floating point exception for native float */
- SET_FP_ENABLE(env->active_fpu.fcr31, 0);
-#endif
break;
case 71: env->active_fpu.fcr0 = tmp; break;
}
commit 2c0d18ddd9b05b3ab042243b678d8dd281c210ba
Author: Aurelien Jarno <aurelien at aurel32.net>
Date: Sun May 15 14:09:18 2011 +0200
target-ppc: remove old CONFIG_SOFTFLOAT #ifdef
target-ppc has been switched to softfloat only long ago, but a
few #ifdef CONFIG_SOFTFLOAT have been forgotten. Remove them.
Cc: Alexander Graf <agraf at suse.de>
Reviewed-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index 51c99c8..470e42f 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -51,9 +51,7 @@ DEF_HELPER_FLAGS_1(cntlzw32, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32)
DEF_HELPER_FLAGS_2(brinc, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl, tl)
DEF_HELPER_0(float_check_status, void)
-#ifdef CONFIG_SOFTFLOAT
DEF_HELPER_0(reset_fpstatus, void)
-#endif
DEF_HELPER_2(compute_fprf, i32, i64, i32)
DEF_HELPER_2(store_fpscr, void, i64, i32)
DEF_HELPER_1(fpscr_clrbit, void, i32)
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
index c52a371..15d9222 100644
--- a/target-ppc/op_helper.c
+++ b/target-ppc/op_helper.c
@@ -971,7 +971,6 @@ void helper_store_fpscr (uint64_t arg, uint32_t mask)
void helper_float_check_status (void)
{
-#ifdef CONFIG_SOFTFLOAT
if (env->exception_index == POWERPC_EXCP_PROGRAM &&
(env->error_code & POWERPC_EXCP_FP)) {
/* Differred floating-point exception after target FPR update */
@@ -989,22 +988,12 @@ void helper_float_check_status (void)
float_inexact_excp();
}
}
-#else
- if (env->exception_index == POWERPC_EXCP_PROGRAM &&
- (env->error_code & POWERPC_EXCP_FP)) {
- /* Differred floating-point exception after target FPR update */
- if (msr_fe0 != 0 || msr_fe1 != 0)
- helper_raise_exception_err(env->exception_index, env->error_code);
- }
-#endif
}
-#ifdef CONFIG_SOFTFLOAT
void helper_reset_fpstatus (void)
{
set_float_exception_flags(0, &env->fp_status);
}
-#endif
/* fadd - fadd. */
uint64_t helper_fadd (uint64_t arg1, uint64_t arg2)
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 9b3f90c..59aef85 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -215,9 +215,7 @@ struct opc_handler_t {
static inline void gen_reset_fpstatus(void)
{
-#ifdef CONFIG_SOFTFLOAT
gen_helper_reset_fpstatus();
-#endif
}
static inline void gen_compute_fprf(TCGv_i64 arg, int set_fprf, int set_rc)
commit e1b45cca620bf33168914283f81f6f3d8847f76b
Author: Alexander Graf <agraf at suse.de>
Date: Mon May 30 12:09:12 2011 +0200
s390x: implement lrvgr
The LRVGR instruction was missing. Implement it, so everyone's happy.
Reported-by: Balazs Kutil <bkutil at novell.com>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index afeb5e6..eda4624 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -3473,6 +3473,9 @@ static void disas_b9(DisasContext *s, int op, int r1, int r2)
tcg_temp_free_i64(tmp2);
tcg_temp_free_i64(tmp3);
break;
+ case 0x0f: /* LRVGR R1,R2 [RRE] */
+ tcg_gen_bswap64_i64(regs[r1], regs[r2]);
+ break;
case 0x1f: /* LRVR R1,R2 [RRE] */
tmp32_1 = load_reg32(r2);
tcg_gen_bswap32_i32(tmp32_1, tmp32_1);
commit 5b185639c5740998de403415c749ac98e13418fd
Author: Alexander Graf <agraf at suse.de>
Date: Mon May 30 10:49:45 2011 +0200
s390x: fix cksm instruction
The cksm instruction was implemented incorrectly, rendering UDP and TCP
checksum calculation wrong, making an emulated s390x Linux guest break
in most networking operations.
This patch fixes odd end checksum calculation, takes the input register
as input for the checksum and optimizes the overflow pieces by a bit.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/op_helper.c b/target-s390x/op_helper.c
index 49760a4..db03a79 100644
--- a/target-s390x/op_helper.c
+++ b/target-s390x/op_helper.c
@@ -1731,25 +1731,15 @@ void HELPER(sqdbr)(uint32_t f1, uint32_t f2)
env->fregs[f1].d = float64_sqrt(env->fregs[f2].d, &env->fpu_status);
}
-static inline uint64_t cksm_overflow(uint64_t cksm)
-{
- if (cksm > 0xffffffffULL) {
- cksm &= 0xffffffffULL;
- cksm++;
- }
- return cksm;
-}
-
/* checksum */
void HELPER(cksm)(uint32_t r1, uint32_t r2)
{
uint64_t src = get_address_31fix(r2);
uint64_t src_len = env->regs[(r2 + 1) & 15];
- uint64_t cksm = 0;
+ uint64_t cksm = (uint32_t)env->regs[r1];
while (src_len >= 4) {
cksm += ldl(src);
- cksm = cksm_overflow(cksm);
/* move to next word */
src_len -= 4;
@@ -1760,26 +1750,24 @@ void HELPER(cksm)(uint32_t r1, uint32_t r2)
case 0:
break;
case 1:
- cksm += ldub(src);
- cksm = cksm_overflow(cksm);
+ cksm += ldub(src) << 24;
break;
case 2:
- cksm += lduw(src);
- cksm = cksm_overflow(cksm);
+ cksm += lduw(src) << 16;
break;
case 3:
- /* XXX check if this really is correct */
- cksm += lduw(src) << 8;
- cksm += ldub(src + 2);
- cksm = cksm_overflow(cksm);
+ cksm += lduw(src) << 16;
+ cksm += ldub(src + 2) << 8;
break;
}
/* indicate we've processed everything */
+ env->regs[r2] = src + src_len;
env->regs[(r2 + 1) & 15] = 0;
/* store result */
- env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | (uint32_t)cksm;
+ env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) |
+ ((uint32_t)cksm + (cksm >> 32));
}
static inline uint32_t cc_calc_ltgt_32(CPUState *env, int32_t src,
commit 87b0b70513fb554ff20374707d7e90b0bc6ffb2d
Author: Alexander Graf <agraf at suse.de>
Date: Sat May 28 02:12:33 2011 +0200
s390x: free tmp explicitly in every opcode for disas_a5()
The disas_a5() function provided a TCG tmp variable which was populated
by the respective opcode implementations, but freed at the end of the
function in generic code.
That makes it really hard for code review, so let's move the freeing
to the same scope as the actual allocation.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index 5828b5f..afeb5e6 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -2334,18 +2334,22 @@ static void disas_a5(DisasContext *s, int op, int r1, int i2)
case 0x0: /* IIHH R1,I2 [RI] */
tmp = tcg_const_i64(i2);
tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 48, 16);
+ tcg_temp_free_i64(tmp);
break;
case 0x1: /* IIHL R1,I2 [RI] */
tmp = tcg_const_i64(i2);
tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 32, 16);
+ tcg_temp_free_i64(tmp);
break;
case 0x2: /* IILH R1,I2 [RI] */
tmp = tcg_const_i64(i2);
tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 16, 16);
+ tcg_temp_free_i64(tmp);
break;
case 0x3: /* IILL R1,I2 [RI] */
tmp = tcg_const_i64(i2);
tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 0, 16);
+ tcg_temp_free_i64(tmp);
break;
case 0x4: /* NIHH R1,I2 [RI] */
case 0x8: /* OIHH R1,I2 [RI] */
@@ -2370,6 +2374,7 @@ static void disas_a5(DisasContext *s, int op, int r1, int i2)
set_cc_nz_u32(s, tmp32);
tcg_temp_free_i64(tmp2);
tcg_temp_free_i32(tmp32);
+ tcg_temp_free_i64(tmp);
break;
case 0x5: /* NIHL R1,I2 [RI] */
case 0x9: /* OIHL R1,I2 [RI] */
@@ -2395,6 +2400,7 @@ static void disas_a5(DisasContext *s, int op, int r1, int i2)
set_cc_nz_u32(s, tmp32);
tcg_temp_free_i64(tmp2);
tcg_temp_free_i32(tmp32);
+ tcg_temp_free_i64(tmp);
break;
case 0x6: /* NILH R1,I2 [RI] */
case 0xa: /* OILH R1,I2 [RI] */
@@ -2420,6 +2426,7 @@ static void disas_a5(DisasContext *s, int op, int r1, int i2)
set_cc_nz_u32(s, tmp32);
tcg_temp_free_i64(tmp2);
tcg_temp_free_i32(tmp32);
+ tcg_temp_free_i64(tmp);
break;
case 0x7: /* NILL R1,I2 [RI] */
case 0xb: /* OILL R1,I2 [RI] */
@@ -2443,29 +2450,33 @@ static void disas_a5(DisasContext *s, int op, int r1, int i2)
set_cc_nz_u32(s, tmp32); /* signedness should not matter here */
tcg_temp_free_i64(tmp2);
tcg_temp_free_i32(tmp32);
+ tcg_temp_free_i64(tmp);
break;
case 0xc: /* LLIHH R1,I2 [RI] */
tmp = tcg_const_i64( ((uint64_t)i2) << 48 );
store_reg(r1, tmp);
+ tcg_temp_free_i64(tmp);
break;
case 0xd: /* LLIHL R1,I2 [RI] */
tmp = tcg_const_i64( ((uint64_t)i2) << 32 );
store_reg(r1, tmp);
+ tcg_temp_free_i64(tmp);
break;
case 0xe: /* LLILH R1,I2 [RI] */
tmp = tcg_const_i64( ((uint64_t)i2) << 16 );
store_reg(r1, tmp);
+ tcg_temp_free_i64(tmp);
break;
case 0xf: /* LLILL R1,I2 [RI] */
tmp = tcg_const_i64(i2);
store_reg(r1, tmp);
+ tcg_temp_free_i64(tmp);
break;
default:
LOG_DISAS("illegal a5 operation 0x%x\n", op);
gen_illegal_opcode(s, 2);
return;
}
- tcg_temp_free_i64(tmp);
}
static void disas_a7(DisasContext *s, int op, int r1, int i2)
commit 2497a67fed5d4844ec0ea053d27b538712245dc2
Author: Stefan Weil <weil at mail.berlios.de>
Date: Fri May 27 19:03:36 2011 +0200
target-s390x: Add missing tcg_temp_free_i32()
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index 81b8c5b..5828b5f 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -1078,9 +1078,12 @@ static void gen_jcc(DisasContext *s, uint32_t mask, int skip)
tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, skip);
break;
default:
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(tmp2);
goto do_dynamic;
}
tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(tmp2);
account_inline_branch(s);
break;
case CC_OP_TM_64:
commit bbf9f3b4d4327421f8d415bed9c5f50ca28cab1f
Author: Stefan Weil <weil at mail.berlios.de>
Date: Fri May 27 19:03:31 2011 +0200
target-s390x: Add missing tcg_temp_free_i64() in disas_s390_insn(), opc == 0x90
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index c5a3930..81b8c5b 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -4621,6 +4621,7 @@ static void disas_s390_insn(DisasContext *s)
}
tcg_gen_add_i64(tmp, tmp, tmp3);
}
+ tcg_temp_free_i64(tmp);
tcg_temp_free_i64(tmp2);
tcg_temp_free_i64(tmp3);
tcg_temp_free_i64(tmp4);
commit 225b6af7cd504bc8430aa8ef04782845a62ee7b1
Author: Stefan Weil <weil at mail.berlios.de>
Date: Fri May 27 19:03:30 2011 +0200
target-s390x: Add missing tcg_temp_free_i64() in disas_s390_insn(), opc == 0x8e
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index f3f42a9..c5a3930 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -4596,6 +4596,8 @@ static void disas_s390_insn(DisasContext *s)
store_reg32(r1, tmp32_1);
tcg_gen_trunc_i64_i32(tmp32_2, tmp2);
store_reg32(r1 + 1, tmp32_2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
break;
case 0x98: /* LM R1,R3,D2(B2) [RS] */
case 0x90: /* STM R1,R3,D2(B2) [RS] */
commit e32a18320a48c5379a27185ad66f71999c53b430
Author: Stefan Weil <weil at mail.berlios.de>
Date: Fri May 27 19:03:29 2011 +0200
target-s390x: Add missing tcg_temp_free_i64() in disas_b2()
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index a11cb19..f3f42a9 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -2964,6 +2964,8 @@ static void disas_b2(DisasContext *s, int op, uint32_t insn)
/* we need to keep cc_op intact */
s->is_jmp = DISAS_JUMP;
tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
break;
case 0x20: /* SERVC R1,R2 [RRE] */
/* SCLP Service call (PV hypercall) */
commit 21de37a778667ad551a6ce1cabd1867e715e53c4
Author: Stefan Weil <weil at mail.berlios.de>
Date: Fri May 27 19:03:28 2011 +0200
target-s390x: Add missing tcg_temp_free_i64() in do_mh()
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index 6ec77ec..a11cb19 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -2094,6 +2094,7 @@ do_mh:
tcg_gen_add_i64(tmp, tmp, tmp3);
}
tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp3);
tcg_temp_free_i64(tmp4);
break;
case 0x2c: /* STCMH R1,M3,D2(B2) [RSY] */
commit a825aefbc2ade367327785feb0e28bb2d8ca3988
Author: Stefan Weil <weil at mail.berlios.de>
Date: Fri May 27 19:03:27 2011 +0200
target-s390x: Add missing tcg_temp_free_i64() in gen_jcc()
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index 141a72f..6ec77ec 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -1095,6 +1095,7 @@ static void gen_jcc(DisasContext *s, uint32_t mask, int skip)
tcg_gen_brcondi_i64(TCG_COND_EQ, tmp64, 0, skip);
break;
default:
+ tcg_temp_free_i64(tmp64);
goto do_dynamic;
}
tcg_temp_free_i64(tmp64);
commit 640239b26d6120724f6c1ec99f64ddb1df9314c3
Author: Stefan Weil <weil at mail.berlios.de>
Date: Fri May 27 19:03:26 2011 +0200
target-s390x: Fix duplicate call of tcg_temp_new_i64
tmp2 = tcg_temp_new_i64() is already executed unconditionally,
so there is no need to call it a second time for 64 bit hosts.
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index 865a9df..141a72f 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -2068,7 +2068,6 @@ do_mh:
tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s));
tcg_gen_trunc_i64_i32(TCGV_HIGH(regs[i]), tmp2);
#else
- tmp2 = tcg_temp_new_i64();
tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s));
tcg_gen_shl_i64(tmp2, tmp2, tmp4);
tcg_gen_ext32u_i64(regs[i], regs[i]);
commit be82ee2aca363753888fe777215849a86f4e737c
Author: Stefan Weil <weil at mail.berlios.de>
Date: Fri May 27 19:03:25 2011 +0200
target-s390x: Fix wrong argument in call of tcg_gen_shl_i64()
tcg_gen_shl_i64 needs a 3rd argument of type TCGv_i64.
Set tmp4 so it can be used here.
v2:
Don't call tcg_const_i64() inside of the loop
because it creates additional code.
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index 8e71df3..865a9df 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -2056,7 +2056,7 @@ do_mh:
even for very long ones... */
tmp = get_address(s, 0, b2, d2);
tmp3 = tcg_const_i64(stm_len);
- tmp4 = tcg_const_i64(32);
+ tmp4 = tcg_const_i64(op == 0x26 ? 32 : 4);
for (i = r1;; i = (i + 1) % 16) {
switch (op) {
case 0x4:
@@ -2070,7 +2070,7 @@ do_mh:
#else
tmp2 = tcg_temp_new_i64();
tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s));
- tcg_gen_shl_i64(tmp2, tmp2, 4);
+ tcg_gen_shl_i64(tmp2, tmp2, tmp4);
tcg_gen_ext32u_i64(regs[i], regs[i]);
tcg_gen_or_i64(regs[i], regs[i], tmp2);
#endif
commit d4a3ef69703e289af7b08122d969e72af799d15f
Author: Stefan Weil <weil at mail.berlios.de>
Date: Tue May 24 19:42:51 2011 +0200
target-s390x: Fix build for non-linux hosts
linux/kvm.h is not always available for compilation.
Neither linux/kvm.h nor kvm.h are needed, so remove both
which also fixes the build problem for non-linux hosts.
Cc: Alexander Graf <agraf at suse.de>
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/helper.c b/target-s390x/helper.c
index c79af46..745d8c5 100644
--- a/target-s390x/helper.c
+++ b/target-s390x/helper.c
@@ -28,11 +28,6 @@
#include "qemu-common.h"
#include "qemu-timer.h"
-#if !defined(CONFIG_USER_ONLY)
-#include <linux/kvm.h>
-#include "kvm.h"
-#endif
-
//#define DEBUG_S390
//#define DEBUG_S390_PTE
//#define DEBUG_S390_STDOUT
commit 39f4107981c9c9a5c9af6067cfbb4272a8112923
Author: Alexander Graf <agraf at suse.de>
Date: Tue May 24 19:52:28 2011 +0200
s390x: update zipl rom
The zipl bootloader rom we have has seen some dramatic speedups upstream,
so let's update it to improve the experience when booting a guest image.
This binary is based on commit id 9a0842dd9823d529f721b418d554f17c72e009e3.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/pc-bios/s390-zipl.rom b/pc-bios/s390-zipl.rom
index f7af9b1..3115128 100644
Binary files a/pc-bios/s390-zipl.rom and b/pc-bios/s390-zipl.rom differ
commit d461e3b9296c706043002cd2a63a7ae8ecdc431c
Author: Alexander Graf <agraf at suse.de>
Date: Fri May 27 03:23:26 2011 +0200
PPC: fix mpc8544ds pci default devices
After the Qdev'ification of the MPC8544DS board and PCI bus, the internal
PCI bus name changed from "pci" to "pci.0". Reflect this change in the
search for that bus.
This patch enables networking on e500 guests again.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c
index 17b0165..6b57fbf 100644
--- a/hw/ppce500_mpc8544ds.c
+++ b/hw/ppce500_mpc8544ds.c
@@ -275,7 +275,7 @@ static void mpc8544ds_init(ram_addr_t ram_size,
mpic[pci_irq_nrs[0]], mpic[pci_irq_nrs[1]],
mpic[pci_irq_nrs[2]], mpic[pci_irq_nrs[3]],
NULL);
- pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci");
+ pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0");
if (!pci_bus)
printf("couldn't create PCI controller!\n");
commit e34b12ae98b6851da8acc791d6df05f4482ae416
Author: Alexander Graf <agraf at suse.de>
Date: Thu May 26 23:50:33 2011 +0200
Fix segfault on screendump with -nographic
When running -nographic and calling "screendump" on the monitor, qemu
segfaults. Fix the invalid pointer dereference by checking for NULL.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/console.c b/console.c
index 871c1d4..9c6addf 100644
--- a/console.c
+++ b/console.c
@@ -180,7 +180,7 @@ void vga_hw_screen_dump(const char *filename)
active_console = consoles[0];
/* There is currently no way of specifying which screen we want to dump,
so always dump the first one. */
- if (consoles[0]->hw_screen_dump)
+ if (consoles[0] && consoles[0]->hw_screen_dump)
consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
active_console = previous_active_console;
}
commit fbd659b76c0601efc49a4a3291730ca47f36c12c
Author: Alexander Graf <agraf at suse.de>
Date: Wed May 25 23:49:41 2011 +0200
PPC: install mpc8544ds.dtb
We don't install mpc8544ds.dtb, which means that -M mpc8544ds doesn't
work when installed. Fix it by installing the file.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/Makefile b/Makefile
index 2b0438c..b6466e7 100644
--- a/Makefile
+++ b/Makefile
@@ -185,6 +185,7 @@ ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc \
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
+mpc8544ds.dtb \
multiboot.bin linuxboot.bin \
s390-zipl.rom \
spapr-rtas.bin slof.bin
commit fafc0b6afed9d913ddbcd2da87e5d39da9bf04c5
Author: Alexander Graf <agraf at suse.de>
Date: Wed May 25 15:04:42 2011 +0200
PPC: fix sregs usage on booke
When compiling qemu with kvm support on BookE PPC machines, I get
the following error:
cc1: warnings being treated as errors
/tmp/qemu/target-ppc/kvm.c: In function 'kvm_arch_get_registers':
/tmp/qemu/target-ppc/kvm.c:188: error: unused variable 'sregs'
This is due to overly ambitious #ifdef'ery introduced in 90dc88.
Fix it by keeping code that doesn't depend on new headers alive
for the compiler, but never executed due to failing capability
checks.
CC: Scott Wood <scottwood at freescale.com>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
index ccf4668..e7b1b10 100644
--- a/target-ppc/kvm.c
+++ b/target-ppc/kvm.c
@@ -45,9 +45,7 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
static int cap_interrupt_unset = false;
static int cap_interrupt_level = false;
static int cap_segstate;
-#ifdef KVM_CAP_PPC_BOOKE_SREGS
static int cap_booke_sregs;
-#endif
/* XXX We have a race condition where we actually have a level triggered
* interrupt, but the infrastructure can't expose that yet, so the guest
@@ -222,13 +220,13 @@ int kvm_arch_get_registers(CPUState *env)
for (i = 0;i < 32; i++)
env->gpr[i] = regs.gpr[i];
-#ifdef KVM_CAP_PPC_BOOKE_SREGS
if (cap_booke_sregs) {
ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs);
if (ret < 0) {
return ret;
}
+#ifdef KVM_CAP_PPC_BOOKE_SREGS
if (sregs.u.e.features & KVM_SREGS_E_BASE) {
env->spr[SPR_BOOKE_CSRR0] = sregs.u.e.csrr0;
env->spr[SPR_BOOKE_CSRR1] = sregs.u.e.csrr1;
@@ -325,16 +323,16 @@ int kvm_arch_get_registers(CPUState *env)
env->spr[SPR_BOOKE_PID2] = sregs.u.e.impl.fsl.pid2;
}
}
- }
#endif
+ }
-#ifdef KVM_CAP_PPC_SEGSTATE
if (cap_segstate) {
ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs);
if (ret < 0) {
return ret;
}
+#ifdef KVM_CAP_PPC_SEGSTATE
ppc_store_sdr1(env, sregs.u.s.sdr1);
/* Sync SLB */
@@ -357,8 +355,8 @@ int kvm_arch_get_registers(CPUState *env)
env->IBAT[0][i] = sregs.u.s.ppc32.ibat[i] & 0xffffffff;
env->IBAT[1][i] = sregs.u.s.ppc32.ibat[i] >> 32;
}
- }
#endif
+ }
return 0;
}
commit 1ff7854e8899266085aea923b032274d15d7fe58
Author: Stefan Weil <weil at mail.berlios.de>
Date: Fri May 20 22:30:19 2011 +0200
ppc: Fix compilation for ppc64-softmmu
When QEMU was configured with --enable-debug-tcg,
compilation fails in spr_write_booke206_mmucsr0() and in
spr_write_booke_pid(). Similar changes are also needed
in conditional code which is normally unused.
Cc: Alexander Graf <agraf at suse.de>
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index b511afa..fc50ae3 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -73,7 +73,7 @@ static void spr_read_generic (void *opaque, int gprn, int sprn)
gen_load_spr(cpu_gpr[gprn], sprn);
#ifdef PPC_DUMP_SPR_ACCESSES
{
- TCGv t0 = tcg_const_i32(sprn);
+ TCGv_i32 t0 = tcg_const_i32(sprn);
gen_helper_load_dump_spr(t0);
tcg_temp_free_i32(t0);
}
@@ -85,7 +85,7 @@ static void spr_write_generic (void *opaque, int sprn, int gprn)
gen_store_spr(sprn, cpu_gpr[gprn]);
#ifdef PPC_DUMP_SPR_ACCESSES
{
- TCGv t0 = tcg_const_i32(sprn);
+ TCGv_i32 t0 = tcg_const_i32(sprn);
gen_helper_store_dump_spr(t0);
tcg_temp_free_i32(t0);
}
@@ -1367,16 +1367,16 @@ static void spr_write_e500_l1csr0 (void *opaque, int sprn, int gprn)
static void spr_write_booke206_mmucsr0 (void *opaque, int sprn, int gprn)
{
- TCGv t0 = tcg_const_i32(sprn);
+ TCGv_i32 t0 = tcg_const_i32(sprn);
gen_helper_booke206_tlbflush(t0);
- tcg_temp_free(t0);
+ tcg_temp_free_i32(t0);
}
static void spr_write_booke_pid (void *opaque, int sprn, int gprn)
{
- TCGv t0 = tcg_const_i32(sprn);
+ TCGv_i32 t0 = tcg_const_i32(sprn);
gen_helper_booke_setpid(t0, cpu_gpr[gprn]);
- tcg_temp_free(t0);
+ tcg_temp_free_i32(t0);
}
#endif
commit 578c7b2ca8ee9e97fa8693b1a83d517e8e3f962e
Author: Juha Riihim?ki <juha.riihimaki at nokia.com>
Date: Tue May 31 19:40:21 2011 +0100
audio: fix integer overflow expression
Fix an integer overflow that can happen for signed 32 bit types
when using FLOAT_MIXENG. (Note that at the moment this is only true
when using the MacOSX coreaudio audio driver.)
Signed-off-by: Juha Riihim?ki <juha.riihimaki at nokia.com>
[Peter Maydell: Removed unnecessary casts]
Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: malc <av1474 at comtv.ru>
diff --git a/audio/mixeng_template.h b/audio/mixeng_template.h
index a2d0ef8..e644c23 100644
--- a/audio/mixeng_template.h
+++ b/audio/mixeng_template.h
@@ -46,7 +46,7 @@ static mixeng_real inline glue (conv_, ET) (IN_T v)
#endif
#else /* !RECIPROCAL */
#ifdef SIGNED
- return nv / (mixeng_real) (IN_MAX - IN_MIN);
+ return nv / (mixeng_real) ((mixeng_real) IN_MAX - IN_MIN);
#else
return (nv - HALF) / (mixeng_real) IN_MAX;
#endif
@@ -63,7 +63,7 @@ static IN_T inline glue (clip_, ET) (mixeng_real v)
}
#ifdef SIGNED
- return ENDIAN_CONVERT ((IN_T) (v * (IN_MAX - IN_MIN)));
+ return ENDIAN_CONVERT ((IN_T) (v * ((mixeng_real) IN_MAX - IN_MIN)));
#else
return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF));
#endif
commit b1d7d2b93a1d6b2d2848b616cc35acdf521c923c
Merge: ede77d2... 06ea77b...
Author: Anthony Liguori <aliguori at us.ibm.com>
Date: Tue May 31 08:23:11 2011 -0500
Merge remote-tracking branch 'stefanha/trivial-patches' into staging
commit ede77d297fb79263af866d302dd307af7ceb04fd
Merge: ed7ec84... 7edfe65...
Author: Anthony Liguori <aliguori at us.ibm.com>
Date: Tue May 31 08:22:03 2011 -0500
Merge remote-tracking branch 'amit/for-anthony' into staging
commit ed7ec8400707fe42f4a0f40db2f2d5827ecea789
Merge: f590f4c... 1455084...
Author: Anthony Liguori <aliguori at us.ibm.com>
Date: Tue May 31 08:20:56 2011 -0500
Merge remote-tracking branch 'bonzini/scsi.2' into staging
Conflicts:
hw/usb-msd.c
diff --cc hw/usb-msd.c
index 141da2c,6ec2255..c59797b
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@@ -213,47 -208,21 +210,21 @@@ static void usb_msd_send_status(MSDStat
memcpy(p->data, &csw, len);
}
- static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
- uint32_t arg)
+ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
{
- MSDState *s = DO_UPCAST(MSDState, dev.qdev, bus->qbus.parent);
+ MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet;
- if (tag != s->tag) {
- fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", tag);
- }
- if (reason == SCSI_REASON_DONE) {
- DPRINTF("Command complete %d\n", arg);
- s->residue = s->data_len;
- s->result = arg != 0;
- if (s->packet) {
- if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) {
- /* A deferred packet with no write data remaining must be
- the status read packet. */
- usb_msd_send_status(s, p);
- s->mode = USB_MSDM_CBW;
- } else {
- if (s->data_len) {
- s->data_len -= s->usb_len;
- if (s->mode == USB_MSDM_DATAIN)
- memset(s->usb_buf, 0, s->usb_len);
- s->usb_len = 0;
- }
- if (s->data_len == 0)
- s->mode = USB_MSDM_CSW;
- }
- s->packet = NULL;
- usb_packet_complete(&s->dev, p);
- } else if (s->data_len == 0) {
- s->mode = USB_MSDM_CSW;
- }
- return;
+ if (req->tag != s->tag) {
+ fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag);
}
- s->scsi_len = arg;
- s->scsi_buf = s->scsi_dev->info->get_buf(s->scsi_dev, tag);
+
+ assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
+ s->scsi_len = len;
+ s->scsi_buf = scsi_req_get_buf(req);
if (p) {
usb_msd_copy_data(s);
- if (s->usb_len == 0) {
+ if (s->packet && s->usb_len == 0) {
/* Set s->packet to NULL before calling usb_packet_complete
because another request may be issued before
usb_packet_complete returns. */
@@@ -315,12 -334,10 +336,10 @@@ static int usb_msd_handle_control(USBDe
return ret;
}
-static void usb_msd_cancel_io(USBPacket *p, void *opaque)
+static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
{
- MSDState *s = opaque;
+ MSDState *s = DO_UPCAST(MSDState, dev, dev);
- s->scsi_dev->info->cancel_io(s->scsi_dev, s->tag);
- s->packet = NULL;
- s->scsi_len = 0;
+ scsi_req_cancel(s->req);
}
static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
commit f590f4c4b6b1adcbcec1780f85466ea10c8c6123
Merge: 2eb9f24... 94527ea...
Author: Anthony Liguori <aliguori at us.ibm.com>
Date: Tue May 31 08:17:15 2011 -0500
Merge remote-tracking branch 'kraxel/usb.14.pull' into staging
commit 06ea77bc505b2e41cc42c8c4b39d08ec638a82e8
Author: Stefan Weil <weil at mail.berlios.de>
Date: Sun May 22 14:02:40 2011 +0200
Fix spelling in comment (additon -> addition)
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Signed-off-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
diff --git a/tcg/tcg.h b/tcg/tcg.h
index 2b985ac..746378a 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -129,7 +129,7 @@ typedef tcg_target_ulong TCGArg;
We use plain int by default to avoid this runtime overhead.
Users of tcg_gen_* don't need to know about any of this, and should
treat TCGv as an opaque type.
- In additon we do typechecking for different types of variables. TCGv_i32
+ In addition we do typechecking for different types of variables. TCGv_i32
and TCGv_i64 are 32/64-bit variables respectively. TCGv and TCGv_ptr
are aliases for target_ulong and host pointer sized values respectively.
*/
commit b9055c3ccafae4624f9caca8aad9305f8b2be8c3
Author: Stefan Weil <weil at mail.berlios.de>
Date: Sun May 22 14:02:39 2011 +0200
pflash_cfi02: Fix a typo in debug code (TARGET_FMT_pld -> TARGET_FMT_plx)
Thanks to Tobias Hoffmann <th55 at gmx.de> for this patch.
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Signed-off-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
diff --git a/hw/pflash_cfi02.c b/hw/pflash_cfi02.c
index 8fdafe6..725cd1e 100644
--- a/hw/pflash_cfi02.c
+++ b/hw/pflash_cfi02.c
@@ -188,7 +188,7 @@ static uint32_t pflash_read (pflash_t *pfl, target_phys_addr_t offset,
default:
goto flash_read;
}
- DPRINTF("%s: ID " TARGET_FMT_pld " %x\n", __func__, boff, ret);
+ DPRINTF("%s: ID " TARGET_FMT_plx " %x\n", __func__, boff, ret);
break;
case 0xA0:
case 0x10:
commit 2eb9f241824d000fcd90bd7f4b49e40b88e62975
Author: Marcus Comstedt <marcus at mc.pp.se>
Date: Sat May 28 16:55:52 2011 +0200
bitbang_i2c: Fix spurious slave read after NACK
After NACKing a read operation, a raising SCL should not trigger a new
read from the slave. Introduce a new state which just waits for a stop
or start condition after NACK.
Signed-off-by: Marcus Comstedt <marcus at mc.pp.se>
Signed-off-by: Andrzej Zaborowski <andrew.zaborowski at intel.com>
diff --git a/hw/bitbang_i2c.c b/hw/bitbang_i2c.c
index 4ee99a1..2937b5c 100644
--- a/hw/bitbang_i2c.c
+++ b/hw/bitbang_i2c.c
@@ -38,7 +38,8 @@ typedef enum bitbang_i2c_state {
RECEIVING_BIT2,
RECEIVING_BIT1,
RECEIVING_BIT0,
- SENDING_ACK
+ SENDING_ACK,
+ SENT_NACK
} bitbang_i2c_state;
struct bitbang_i2c_interface {
@@ -115,6 +116,7 @@ int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level)
}
switch (i2c->state) {
case STOPPED:
+ case SENT_NACK:
return bitbang_i2c_ret(i2c, 1);
case SENDING_BIT7 ... SENDING_BIT0:
@@ -155,6 +157,7 @@ int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level)
i2c->state = RECEIVING_BIT7;
if (data != 0) {
DPRINTF("NACKED\n");
+ i2c->state = SENT_NACK;
i2c_nack(i2c->bus);
} else {
DPRINTF("ACKED\n");
commit 42a623c7db82865a9224c15cbace27b2acd65834
Author: Blue Swirl <blauwirbel at gmail.com>
Date: Sun May 8 11:22:38 2011 +0000
Move user emulator stuff from cpu-exec.c to user-exec.c
Simplify cpu-exec.c by refactoring.
Signed-off-by: Blue Swirl <blauwirbel at gmail.com>
diff --git a/Makefile.target b/Makefile.target
index 2e281a4..602d50d 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -94,10 +94,10 @@ tcg/tcg.o: cpu.h
# HELPER_CFLAGS is used for all the code compiled with static register
# variables
-%_helper.o cpu-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
+%_helper.o cpu-exec.o user-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
# Note: this is a workaround. The real fix is to avoid compiling
-# cpu_signal_handler() in cpu-exec.c.
+# cpu_signal_handler() in user-exec.c.
signal.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
#########################################################
@@ -110,7 +110,7 @@ $(call set-vpath, $(SRC_PATH)/linux-user:$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR
QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) -I$(SRC_PATH)/linux-user
obj-y = main.o syscall.o strace.o mmap.o signal.o thunk.o \
elfload.o linuxload.o uaccess.o gdbstub.o cpu-uname.o \
- qemu-malloc.o $(oslib-obj-y)
+ qemu-malloc.o user-exec.o $(oslib-obj-y)
obj-$(TARGET_HAS_BFLT) += flatload.o
@@ -148,7 +148,7 @@ LDFLAGS+=-Wl,-segaddr,__STD_PROG_ZONE,0x1000 -image_base 0x0e000000
LIBS+=-lmx
obj-y = main.o commpage.o machload.o mmap.o signal.o syscall.o thunk.o \
- gdbstub.o
+ gdbstub.o user-exec.o
obj-i386-y += ioport-user.o
@@ -170,7 +170,7 @@ $(call set-vpath, $(SRC_PATH)/bsd-user)
QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ARCH)
obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \
- gdbstub.o uaccess.o
+ gdbstub.o uaccess.o user-exec.o
obj-i386-y += ioport-user.o
diff --git a/cpu-exec.c b/cpu-exec.c
index f197ff9..6ddd8dd 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -23,22 +23,6 @@
#include "kvm.h"
#include "qemu-barrier.h"
-#if !defined(CONFIG_SOFTMMU)
-#undef EAX
-#undef ECX
-#undef EDX
-#undef EBX
-#undef ESP
-#undef EBP
-#undef ESI
-#undef EDI
-#undef EIP
-#include <signal.h>
-#ifdef __linux__
-#include <sys/ucontext.h>
-#endif
-#endif
-
#if defined(__sparc__) && !defined(CONFIG_SOLARIS)
// Work around ugly bugs in glibc that mangle global register contents
#undef env
@@ -48,7 +32,6 @@
int tb_invalidated_flag;
//#define CONFIG_DEBUG_EXEC
-//#define DEBUG_SIGNAL
int qemu_cpu_has_work(CPUState *env)
{
@@ -74,36 +57,6 @@ void cpu_resume_from_signal(CPUState *env1, void *puc)
env->exception_index = -1;
longjmp(env->jmp_env, 1);
}
-
-#else
-
-void cpu_resume_from_signal(CPUState *env1, void *puc)
-{
-#ifdef __linux__
- struct ucontext *uc = puc;
-#elif defined(__OpenBSD__)
- struct sigcontext *uc = puc;
-#endif
-
- env = env1;
-
- /* XXX: restore cpu registers saved in host registers */
-
- if (puc) {
- /* XXX: use siglongjmp ? */
-#ifdef __linux__
-#ifdef __ia64
- sigprocmask(SIG_SETMASK, (sigset_t *)&uc->uc_sigmask, NULL);
-#else
- sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL);
-#endif
-#elif defined(__OpenBSD__)
- sigprocmask(SIG_SETMASK, &uc->sc_mask, NULL);
-#endif
- }
- env->exception_index = -1;
- longjmp(env->jmp_env, 1);
-}
#endif
/* Execute the code without caching the generated code. An interpreter
@@ -713,611 +666,3 @@ int cpu_exec(CPUState *env1)
cpu_single_env = NULL;
return ret;
}
-
-#if defined(TARGET_I386) && defined(CONFIG_USER_ONLY)
-
-void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
-{
- CPUX86State *saved_env;
-
- saved_env = env;
- env = s;
- if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
- selector &= 0xffff;
- cpu_x86_load_seg_cache(env, seg_reg, selector,
- (selector << 4), 0xffff, 0);
- } else {
- helper_load_seg(seg_reg, selector);
- }
- env = saved_env;
-}
-
-void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32)
-{
- CPUX86State *saved_env;
-
- saved_env = env;
- env = s;
-
- helper_fsave(ptr, data32);
-
- env = saved_env;
-}
-
-void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32)
-{
- CPUX86State *saved_env;
-
- saved_env = env;
- env = s;
-
- helper_frstor(ptr, data32);
-
- env = saved_env;
-}
-
-#endif /* TARGET_I386 */
-
-#if !defined(CONFIG_SOFTMMU)
-
-#if defined(TARGET_I386)
-#define EXCEPTION_ACTION \
- raise_exception_err(env->exception_index, env->error_code)
-#else
-#define EXCEPTION_ACTION \
- cpu_loop_exit()
-#endif
-
-/* 'pc' is the host PC at which the exception was raised. 'address' is
- the effective address of the memory exception. 'is_write' is 1 if a
- write caused the exception and otherwise 0'. 'old_set' is the
- signal set which should be restored */
-static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
- int is_write, sigset_t *old_set,
- void *puc)
-{
- TranslationBlock *tb;
- int ret;
-
- if (cpu_single_env) {
- env = cpu_single_env; /* XXX: find a correct solution for multithread */
- }
-#if defined(DEBUG_SIGNAL)
- qemu_printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
- pc, address, is_write, *(unsigned long *)old_set);
-#endif
- /* XXX: locking issue */
- if (is_write && page_unprotect(h2g(address), pc, puc)) {
- return 1;
- }
-
- /* see if it is an MMU fault */
- ret = cpu_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0);
- if (ret < 0) {
- return 0; /* not an MMU fault */
- }
- if (ret == 0) {
- return 1; /* the MMU fault was handled without causing real CPU fault */
- }
- /* now we have a real cpu fault */
- tb = tb_find_pc(pc);
- if (tb) {
- /* the PC is inside the translated code. It means that we have
- a virtual CPU fault */
- cpu_restore_state(tb, env, pc);
- }
-
- /* we restore the process signal mask as the sigreturn should
- do it (XXX: use sigsetjmp) */
- sigprocmask(SIG_SETMASK, old_set, NULL);
- EXCEPTION_ACTION;
-
- /* never comes here */
- return 1;
-}
-
-#if defined(__i386__)
-
-#if defined(__APPLE__)
-#include <sys/ucontext.h>
-
-#define EIP_sig(context) (*((unsigned long *)&(context)->uc_mcontext->ss.eip))
-#define TRAP_sig(context) ((context)->uc_mcontext->es.trapno)
-#define ERROR_sig(context) ((context)->uc_mcontext->es.err)
-#define MASK_sig(context) ((context)->uc_sigmask)
-#elif defined(__NetBSD__)
-#include <ucontext.h>
-
-#define EIP_sig(context) ((context)->uc_mcontext.__gregs[_REG_EIP])
-#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO])
-#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR])
-#define MASK_sig(context) ((context)->uc_sigmask)
-#elif defined(__FreeBSD__) || defined(__DragonFly__)
-#include <ucontext.h>
-
-#define EIP_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_eip))
-#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno)
-#define ERROR_sig(context) ((context)->uc_mcontext.mc_err)
-#define MASK_sig(context) ((context)->uc_sigmask)
-#elif defined(__OpenBSD__)
-#define EIP_sig(context) ((context)->sc_eip)
-#define TRAP_sig(context) ((context)->sc_trapno)
-#define ERROR_sig(context) ((context)->sc_err)
-#define MASK_sig(context) ((context)->sc_mask)
-#else
-#define EIP_sig(context) ((context)->uc_mcontext.gregs[REG_EIP])
-#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
-#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
-#define MASK_sig(context) ((context)->uc_sigmask)
-#endif
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
-#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
- ucontext_t *uc = puc;
-#elif defined(__OpenBSD__)
- struct sigcontext *uc = puc;
-#else
- struct ucontext *uc = puc;
-#endif
- unsigned long pc;
- int trapno;
-
-#ifndef REG_EIP
-/* for glibc 2.1 */
-#define REG_EIP EIP
-#define REG_ERR ERR
-#define REG_TRAPNO TRAPNO
-#endif
- pc = EIP_sig(uc);
- trapno = TRAP_sig(uc);
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- trapno == 0xe ?
- (ERROR_sig(uc) >> 1) & 1 : 0,
- &MASK_sig(uc), puc);
-}
-
-#elif defined(__x86_64__)
-
-#ifdef __NetBSD__
-#define PC_sig(context) _UC_MACHINE_PC(context)
-#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO])
-#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR])
-#define MASK_sig(context) ((context)->uc_sigmask)
-#elif defined(__OpenBSD__)
-#define PC_sig(context) ((context)->sc_rip)
-#define TRAP_sig(context) ((context)->sc_trapno)
-#define ERROR_sig(context) ((context)->sc_err)
-#define MASK_sig(context) ((context)->sc_mask)
-#elif defined(__FreeBSD__) || defined(__DragonFly__)
-#include <ucontext.h>
-
-#define PC_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_rip))
-#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno)
-#define ERROR_sig(context) ((context)->uc_mcontext.mc_err)
-#define MASK_sig(context) ((context)->uc_sigmask)
-#else
-#define PC_sig(context) ((context)->uc_mcontext.gregs[REG_RIP])
-#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
-#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
-#define MASK_sig(context) ((context)->uc_sigmask)
-#endif
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
- unsigned long pc;
-#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
- ucontext_t *uc = puc;
-#elif defined(__OpenBSD__)
- struct sigcontext *uc = puc;
-#else
- struct ucontext *uc = puc;
-#endif
-
- pc = PC_sig(uc);
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- TRAP_sig(uc) == 0xe ?
- (ERROR_sig(uc) >> 1) & 1 : 0,
- &MASK_sig(uc), puc);
-}
-
-#elif defined(_ARCH_PPC)
-
-/***********************************************************************
- * signal context platform-specific definitions
- * From Wine
- */
-#ifdef linux
-/* All Registers access - only for local access */
-#define REG_sig(reg_name, context) \
- ((context)->uc_mcontext.regs->reg_name)
-/* Gpr Registers access */
-#define GPR_sig(reg_num, context) REG_sig(gpr[reg_num], context)
-/* Program counter */
-#define IAR_sig(context) REG_sig(nip, context)
-/* Machine State Register (Supervisor) */
-#define MSR_sig(context) REG_sig(msr, context)
-/* Count register */
-#define CTR_sig(context) REG_sig(ctr, context)
-/* User's integer exception register */
-#define XER_sig(context) REG_sig(xer, context)
-/* Link register */
-#define LR_sig(context) REG_sig(link, context)
-/* Condition register */
-#define CR_sig(context) REG_sig(ccr, context)
-
-/* Float Registers access */
-#define FLOAT_sig(reg_num, context) \
- (((double *)((char *)((context)->uc_mcontext.regs + 48 * 4)))[reg_num])
-#define FPSCR_sig(context) \
- (*(int *)((char *)((context)->uc_mcontext.regs + (48 + 32 * 2) * 4)))
-/* Exception Registers access */
-#define DAR_sig(context) REG_sig(dar, context)
-#define DSISR_sig(context) REG_sig(dsisr, context)
-#define TRAP_sig(context) REG_sig(trap, context)
-#endif /* linux */
-
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-#include <ucontext.h>
-#define IAR_sig(context) ((context)->uc_mcontext.mc_srr0)
-#define MSR_sig(context) ((context)->uc_mcontext.mc_srr1)
-#define CTR_sig(context) ((context)->uc_mcontext.mc_ctr)
-#define XER_sig(context) ((context)->uc_mcontext.mc_xer)
-#define LR_sig(context) ((context)->uc_mcontext.mc_lr)
-#define CR_sig(context) ((context)->uc_mcontext.mc_cr)
-/* Exception Registers access */
-#define DAR_sig(context) ((context)->uc_mcontext.mc_dar)
-#define DSISR_sig(context) ((context)->uc_mcontext.mc_dsisr)
-#define TRAP_sig(context) ((context)->uc_mcontext.mc_exc)
-#endif /* __FreeBSD__|| __FreeBSD_kernel__ */
-
-#ifdef __APPLE__
-#include <sys/ucontext.h>
-typedef struct ucontext SIGCONTEXT;
-/* All Registers access - only for local access */
-#define REG_sig(reg_name, context) \
- ((context)->uc_mcontext->ss.reg_name)
-#define FLOATREG_sig(reg_name, context) \
- ((context)->uc_mcontext->fs.reg_name)
-#define EXCEPREG_sig(reg_name, context) \
- ((context)->uc_mcontext->es.reg_name)
-#define VECREG_sig(reg_name, context) \
- ((context)->uc_mcontext->vs.reg_name)
-/* Gpr Registers access */
-#define GPR_sig(reg_num, context) REG_sig(r##reg_num, context)
-/* Program counter */
-#define IAR_sig(context) REG_sig(srr0, context)
-/* Machine State Register (Supervisor) */
-#define MSR_sig(context) REG_sig(srr1, context)
-#define CTR_sig(context) REG_sig(ctr, context)
-/* Link register */
-#define XER_sig(context) REG_sig(xer, context)
-/* User's integer exception register */
-#define LR_sig(context) REG_sig(lr, context)
-/* Condition register */
-#define CR_sig(context) REG_sig(cr, context)
-/* Float Registers access */
-#define FLOAT_sig(reg_num, context) \
- FLOATREG_sig(fpregs[reg_num], context)
-#define FPSCR_sig(context) \
- ((double)FLOATREG_sig(fpscr, context))
-/* Exception Registers access */
-/* Fault registers for coredump */
-#define DAR_sig(context) EXCEPREG_sig(dar, context)
-#define DSISR_sig(context) EXCEPREG_sig(dsisr, context)
-/* number of powerpc exception taken */
-#define TRAP_sig(context) EXCEPREG_sig(exception, context)
-#endif /* __APPLE__ */
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
- ucontext_t *uc = puc;
-#else
- struct ucontext *uc = puc;
-#endif
- unsigned long pc;
- int is_write;
-
- pc = IAR_sig(uc);
- is_write = 0;
-#if 0
- /* ppc 4xx case */
- if (DSISR_sig(uc) & 0x00800000) {
- is_write = 1;
- }
-#else
- if (TRAP_sig(uc) != 0x400 && (DSISR_sig(uc) & 0x02000000)) {
- is_write = 1;
- }
-#endif
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- is_write, &uc->uc_sigmask, puc);
-}
-
-#elif defined(__alpha__)
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
- struct ucontext *uc = puc;
- uint32_t *pc = uc->uc_mcontext.sc_pc;
- uint32_t insn = *pc;
- int is_write = 0;
-
- /* XXX: need kernel patch to get write flag faster */
- switch (insn >> 26) {
- case 0x0d: /* stw */
- case 0x0e: /* stb */
- case 0x0f: /* stq_u */
- case 0x24: /* stf */
- case 0x25: /* stg */
- case 0x26: /* sts */
- case 0x27: /* stt */
- case 0x2c: /* stl */
- case 0x2d: /* stq */
- case 0x2e: /* stl_c */
- case 0x2f: /* stq_c */
- is_write = 1;
- }
-
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- is_write, &uc->uc_sigmask, puc);
-}
-#elif defined(__sparc__)
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
- int is_write;
- uint32_t insn;
-#if !defined(__arch64__) || defined(CONFIG_SOLARIS)
- uint32_t *regs = (uint32_t *)(info + 1);
- void *sigmask = (regs + 20);
- /* XXX: is there a standard glibc define ? */
- unsigned long pc = regs[1];
-#else
-#ifdef __linux__
- struct sigcontext *sc = puc;
- unsigned long pc = sc->sigc_regs.tpc;
- void *sigmask = (void *)sc->sigc_mask;
-#elif defined(__OpenBSD__)
- struct sigcontext *uc = puc;
- unsigned long pc = uc->sc_pc;
- void *sigmask = (void *)(long)uc->sc_mask;
-#endif
-#endif
-
- /* XXX: need kernel patch to get write flag faster */
- is_write = 0;
- insn = *(uint32_t *)pc;
- if ((insn >> 30) == 3) {
- switch ((insn >> 19) & 0x3f) {
- case 0x05: /* stb */
- case 0x15: /* stba */
- case 0x06: /* sth */
- case 0x16: /* stha */
- case 0x04: /* st */
- case 0x14: /* sta */
- case 0x07: /* std */
- case 0x17: /* stda */
- case 0x0e: /* stx */
- case 0x1e: /* stxa */
- case 0x24: /* stf */
- case 0x34: /* stfa */
- case 0x27: /* stdf */
- case 0x37: /* stdfa */
- case 0x26: /* stqf */
- case 0x36: /* stqfa */
- case 0x25: /* stfsr */
- case 0x3c: /* casa */
- case 0x3e: /* casxa */
- is_write = 1;
- break;
- }
- }
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- is_write, sigmask, NULL);
-}
-
-#elif defined(__arm__)
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
- struct ucontext *uc = puc;
- unsigned long pc;
- int is_write;
-
-#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
- pc = uc->uc_mcontext.gregs[R15];
-#else
- pc = uc->uc_mcontext.arm_pc;
-#endif
- /* XXX: compute is_write */
- is_write = 0;
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- is_write,
- &uc->uc_sigmask, puc);
-}
-
-#elif defined(__mc68000)
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
- struct ucontext *uc = puc;
- unsigned long pc;
- int is_write;
-
- pc = uc->uc_mcontext.gregs[16];
- /* XXX: compute is_write */
- is_write = 0;
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- is_write,
- &uc->uc_sigmask, puc);
-}
-
-#elif defined(__ia64)
-
-#ifndef __ISR_VALID
- /* This ought to be in <bits/siginfo.h>... */
-# define __ISR_VALID 1
-#endif
-
-int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
-{
- siginfo_t *info = pinfo;
- struct ucontext *uc = puc;
- unsigned long ip;
- int is_write = 0;
-
- ip = uc->uc_mcontext.sc_ip;
- switch (host_signum) {
- case SIGILL:
- case SIGFPE:
- case SIGSEGV:
- case SIGBUS:
- case SIGTRAP:
- if (info->si_code && (info->si_segvflags & __ISR_VALID)) {
- /* ISR.W (write-access) is bit 33: */
- is_write = (info->si_isr >> 33) & 1;
- }
- break;
-
- default:
- break;
- }
- return handle_cpu_signal(ip, (unsigned long)info->si_addr,
- is_write,
- (sigset_t *)&uc->uc_sigmask, puc);
-}
-
-#elif defined(__s390__)
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
- struct ucontext *uc = puc;
- unsigned long pc;
- uint16_t *pinsn;
- int is_write = 0;
-
- pc = uc->uc_mcontext.psw.addr;
-
- /* ??? On linux, the non-rt signal handler has 4 (!) arguments instead
- of the normal 2 arguments. The 3rd argument contains the "int_code"
- from the hardware which does in fact contain the is_write value.
- The rt signal handler, as far as I can tell, does not give this value
- at all. Not that we could get to it from here even if it were. */
- /* ??? This is not even close to complete, since it ignores all
- of the read-modify-write instructions. */
- pinsn = (uint16_t *)pc;
- switch (pinsn[0] >> 8) {
- case 0x50: /* ST */
- case 0x42: /* STC */
- case 0x40: /* STH */
- is_write = 1;
- break;
- case 0xc4: /* RIL format insns */
- switch (pinsn[0] & 0xf) {
- case 0xf: /* STRL */
- case 0xb: /* STGRL */
- case 0x7: /* STHRL */
- is_write = 1;
- }
- break;
- case 0xe3: /* RXY format insns */
- switch (pinsn[2] & 0xff) {
- case 0x50: /* STY */
- case 0x24: /* STG */
- case 0x72: /* STCY */
- case 0x70: /* STHY */
- case 0x8e: /* STPQ */
- case 0x3f: /* STRVH */
- case 0x3e: /* STRV */
- case 0x2f: /* STRVG */
- is_write = 1;
- }
- break;
- }
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- is_write, &uc->uc_sigmask, puc);
-}
-
-#elif defined(__mips__)
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- siginfo_t *info = pinfo;
- struct ucontext *uc = puc;
- greg_t pc = uc->uc_mcontext.pc;
- int is_write;
-
- /* XXX: compute is_write */
- is_write = 0;
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- is_write, &uc->uc_sigmask, puc);
-}
-
-#elif defined(__hppa__)
-
-int cpu_signal_handler(int host_signum, void *pinfo,
- void *puc)
-{
- struct siginfo *info = pinfo;
- struct ucontext *uc = puc;
- unsigned long pc = uc->uc_mcontext.sc_iaoq[0];
- uint32_t insn = *(uint32_t *)pc;
- int is_write = 0;
-
- /* XXX: need kernel patch to get write flag faster. */
- switch (insn >> 26) {
- case 0x1a: /* STW */
- case 0x19: /* STH */
- case 0x18: /* STB */
- case 0x1b: /* STWM */
- is_write = 1;
- break;
-
- case 0x09: /* CSTWX, FSTWX, FSTWS */
- case 0x0b: /* CSTDX, FSTDX, FSTDS */
- /* Distinguish from coprocessor load ... */
- is_write = (insn >> 9) & 1;
- break;
-
- case 0x03:
- switch ((insn >> 6) & 15) {
- case 0xa: /* STWS */
- case 0x9: /* STHS */
- case 0x8: /* STBS */
- case 0xe: /* STWAS */
- case 0xc: /* STBYS */
- is_write = 1;
- }
- break;
- }
-
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
- is_write, &uc->uc_sigmask, puc);
-}
-
-#else
-
-#error host CPU specific signal handler needed
-
-#endif
-
-#endif /* !defined(CONFIG_SOFTMMU) */
diff --git a/user-exec.c b/user-exec.c
new file mode 100644
index 0000000..d4a6abb
--- /dev/null
+++ b/user-exec.c
@@ -0,0 +1,673 @@
+/*
+ * User emulator execution
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * 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 "config.h"
+#include "exec.h"
+#include "disas.h"
+#include "tcg.h"
+
+#undef EAX
+#undef ECX
+#undef EDX
+#undef EBX
+#undef ESP
+#undef EBP
+#undef ESI
+#undef EDI
+#undef EIP
+#include <signal.h>
+#ifdef __linux__
+#include <sys/ucontext.h>
+#endif
+
+//#define DEBUG_SIGNAL
+
+#if defined(TARGET_I386)
+#define EXCEPTION_ACTION \
+ raise_exception_err(env->exception_index, env->error_code)
+#else
+#define EXCEPTION_ACTION \
+ cpu_loop_exit()
+#endif
+
+/* exit the current TB from a signal handler. The host registers are
+ restored in a state compatible with the CPU emulator
+ */
+void cpu_resume_from_signal(CPUState *env1, void *puc)
+{
+#ifdef __linux__
+ struct ucontext *uc = puc;
+#elif defined(__OpenBSD__)
+ struct sigcontext *uc = puc;
+#endif
+
+ env = env1;
+
+ /* XXX: restore cpu registers saved in host registers */
+
+ if (puc) {
+ /* XXX: use siglongjmp ? */
+#ifdef __linux__
+#ifdef __ia64
+ sigprocmask(SIG_SETMASK, (sigset_t *)&uc->uc_sigmask, NULL);
+#else
+ sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL);
+#endif
+#elif defined(__OpenBSD__)
+ sigprocmask(SIG_SETMASK, &uc->sc_mask, NULL);
+#endif
+ }
+ env->exception_index = -1;
+ longjmp(env->jmp_env, 1);
+}
+
+/* 'pc' is the host PC at which the exception was raised. 'address' is
+ the effective address of the memory exception. 'is_write' is 1 if a
+ write caused the exception and otherwise 0'. 'old_set' is the
+ signal set which should be restored */
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+ int is_write, sigset_t *old_set,
+ void *puc)
+{
+ TranslationBlock *tb;
+ int ret;
+
+ if (cpu_single_env) {
+ env = cpu_single_env; /* XXX: find a correct solution for multithread */
+ }
+#if defined(DEBUG_SIGNAL)
+ qemu_printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+ pc, address, is_write, *(unsigned long *)old_set);
+#endif
+ /* XXX: locking issue */
+ if (is_write && page_unprotect(h2g(address), pc, puc)) {
+ return 1;
+ }
+
+ /* see if it is an MMU fault */
+ ret = cpu_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0);
+ if (ret < 0) {
+ return 0; /* not an MMU fault */
+ }
+ if (ret == 0) {
+ return 1; /* the MMU fault was handled without causing real CPU fault */
+ }
+ /* now we have a real cpu fault */
+ tb = tb_find_pc(pc);
+ if (tb) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc);
+ }
+
+ /* we restore the process signal mask as the sigreturn should
+ do it (XXX: use sigsetjmp) */
+ sigprocmask(SIG_SETMASK, old_set, NULL);
+ EXCEPTION_ACTION;
+
+ /* never comes here */
+ return 1;
+}
+
+#if defined(__i386__)
+
+#if defined(__APPLE__)
+#include <sys/ucontext.h>
+
+#define EIP_sig(context) (*((unsigned long *)&(context)->uc_mcontext->ss.eip))
+#define TRAP_sig(context) ((context)->uc_mcontext->es.trapno)
+#define ERROR_sig(context) ((context)->uc_mcontext->es.err)
+#define MASK_sig(context) ((context)->uc_sigmask)
+#elif defined(__NetBSD__)
+#include <ucontext.h>
+
+#define EIP_sig(context) ((context)->uc_mcontext.__gregs[_REG_EIP])
+#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO])
+#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR])
+#define MASK_sig(context) ((context)->uc_sigmask)
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+#include <ucontext.h>
+
+#define EIP_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_eip))
+#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno)
+#define ERROR_sig(context) ((context)->uc_mcontext.mc_err)
+#define MASK_sig(context) ((context)->uc_sigmask)
+#elif defined(__OpenBSD__)
+#define EIP_sig(context) ((context)->sc_eip)
+#define TRAP_sig(context) ((context)->sc_trapno)
+#define ERROR_sig(context) ((context)->sc_err)
+#define MASK_sig(context) ((context)->sc_mask)
+#else
+#define EIP_sig(context) ((context)->uc_mcontext.gregs[REG_EIP])
+#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
+#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
+#define MASK_sig(context) ((context)->uc_sigmask)
+#endif
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+ void *puc)
+{
+ siginfo_t *info = pinfo;
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
+ ucontext_t *uc = puc;
+#elif defined(__OpenBSD__)
+ struct sigcontext *uc = puc;
+#else
+ struct ucontext *uc = puc;
+#endif
+ unsigned long pc;
+ int trapno;
+
+#ifndef REG_EIP
+/* for glibc 2.1 */
+#define REG_EIP EIP
+#define REG_ERR ERR
+#define REG_TRAPNO TRAPNO
+#endif
+ pc = EIP_sig(uc);
+ trapno = TRAP_sig(uc);
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ trapno == 0xe ?
+ (ERROR_sig(uc) >> 1) & 1 : 0,
+ &MASK_sig(uc), puc);
+}
+
+#elif defined(__x86_64__)
+
+#ifdef __NetBSD__
+#define PC_sig(context) _UC_MACHINE_PC(context)
+#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO])
+#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR])
+#define MASK_sig(context) ((context)->uc_sigmask)
+#elif defined(__OpenBSD__)
+#define PC_sig(context) ((context)->sc_rip)
+#define TRAP_sig(context) ((context)->sc_trapno)
+#define ERROR_sig(context) ((context)->sc_err)
+#define MASK_sig(context) ((context)->sc_mask)
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+#include <ucontext.h>
+
+#define PC_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_rip))
+#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno)
+#define ERROR_sig(context) ((context)->uc_mcontext.mc_err)
+#define MASK_sig(context) ((context)->uc_sigmask)
+#else
+#define PC_sig(context) ((context)->uc_mcontext.gregs[REG_RIP])
+#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
+#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
+#define MASK_sig(context) ((context)->uc_sigmask)
+#endif
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+ void *puc)
+{
+ siginfo_t *info = pinfo;
+ unsigned long pc;
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
+ ucontext_t *uc = puc;
+#elif defined(__OpenBSD__)
+ struct sigcontext *uc = puc;
+#else
+ struct ucontext *uc = puc;
+#endif
+
+ pc = PC_sig(uc);
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ TRAP_sig(uc) == 0xe ?
+ (ERROR_sig(uc) >> 1) & 1 : 0,
+ &MASK_sig(uc), puc);
+}
+
+#elif defined(_ARCH_PPC)
+
+/***********************************************************************
+ * signal context platform-specific definitions
+ * From Wine
+ */
+#ifdef linux
+/* All Registers access - only for local access */
+#define REG_sig(reg_name, context) \
+ ((context)->uc_mcontext.regs->reg_name)
+/* Gpr Registers access */
+#define GPR_sig(reg_num, context) REG_sig(gpr[reg_num], context)
+/* Program counter */
+#define IAR_sig(context) REG_sig(nip, context)
+/* Machine State Register (Supervisor) */
+#define MSR_sig(context) REG_sig(msr, context)
+/* Count register */
+#define CTR_sig(context) REG_sig(ctr, context)
+/* User's integer exception register */
+#define XER_sig(context) REG_sig(xer, context)
+/* Link register */
+#define LR_sig(context) REG_sig(link, context)
+/* Condition register */
+#define CR_sig(context) REG_sig(ccr, context)
+
+/* Float Registers access */
+#define FLOAT_sig(reg_num, context) \
+ (((double *)((char *)((context)->uc_mcontext.regs + 48 * 4)))[reg_num])
+#define FPSCR_sig(context) \
+ (*(int *)((char *)((context)->uc_mcontext.regs + (48 + 32 * 2) * 4)))
+/* Exception Registers access */
+#define DAR_sig(context) REG_sig(dar, context)
+#define DSISR_sig(context) REG_sig(dsisr, context)
+#define TRAP_sig(context) REG_sig(trap, context)
+#endif /* linux */
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <ucontext.h>
+#define IAR_sig(context) ((context)->uc_mcontext.mc_srr0)
+#define MSR_sig(context) ((context)->uc_mcontext.mc_srr1)
+#define CTR_sig(context) ((context)->uc_mcontext.mc_ctr)
+#define XER_sig(context) ((context)->uc_mcontext.mc_xer)
+#define LR_sig(context) ((context)->uc_mcontext.mc_lr)
+#define CR_sig(context) ((context)->uc_mcontext.mc_cr)
+/* Exception Registers access */
+#define DAR_sig(context) ((context)->uc_mcontext.mc_dar)
+#define DSISR_sig(context) ((context)->uc_mcontext.mc_dsisr)
+#define TRAP_sig(context) ((context)->uc_mcontext.mc_exc)
+#endif /* __FreeBSD__|| __FreeBSD_kernel__ */
+
+#ifdef __APPLE__
+#include <sys/ucontext.h>
+typedef struct ucontext SIGCONTEXT;
+/* All Registers access - only for local access */
+#define REG_sig(reg_name, context) \
+ ((context)->uc_mcontext->ss.reg_name)
+#define FLOATREG_sig(reg_name, context) \
+ ((context)->uc_mcontext->fs.reg_name)
+#define EXCEPREG_sig(reg_name, context) \
+ ((context)->uc_mcontext->es.reg_name)
+#define VECREG_sig(reg_name, context) \
+ ((context)->uc_mcontext->vs.reg_name)
+/* Gpr Registers access */
+#define GPR_sig(reg_num, context) REG_sig(r##reg_num, context)
+/* Program counter */
+#define IAR_sig(context) REG_sig(srr0, context)
+/* Machine State Register (Supervisor) */
+#define MSR_sig(context) REG_sig(srr1, context)
+#define CTR_sig(context) REG_sig(ctr, context)
+/* Link register */
+#define XER_sig(context) REG_sig(xer, context)
+/* User's integer exception register */
+#define LR_sig(context) REG_sig(lr, context)
+/* Condition register */
+#define CR_sig(context) REG_sig(cr, context)
+/* Float Registers access */
+#define FLOAT_sig(reg_num, context) \
+ FLOATREG_sig(fpregs[reg_num], context)
+#define FPSCR_sig(context) \
+ ((double)FLOATREG_sig(fpscr, context))
+/* Exception Registers access */
+/* Fault registers for coredump */
+#define DAR_sig(context) EXCEPREG_sig(dar, context)
+#define DSISR_sig(context) EXCEPREG_sig(dsisr, context)
+/* number of powerpc exception taken */
+#define TRAP_sig(context) EXCEPREG_sig(exception, context)
+#endif /* __APPLE__ */
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+ void *puc)
+{
+ siginfo_t *info = pinfo;
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ ucontext_t *uc = puc;
+#else
+ struct ucontext *uc = puc;
+#endif
+ unsigned long pc;
+ int is_write;
+
+ pc = IAR_sig(uc);
+ is_write = 0;
+#if 0
+ /* ppc 4xx case */
+ if (DSISR_sig(uc) & 0x00800000) {
+ is_write = 1;
+ }
+#else
+ if (TRAP_sig(uc) != 0x400 && (DSISR_sig(uc) & 0x02000000)) {
+ is_write = 1;
+ }
+#endif
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ is_write, &uc->uc_sigmask, puc);
+}
+
+#elif defined(__alpha__)
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+ void *puc)
+{
+ siginfo_t *info = pinfo;
+ struct ucontext *uc = puc;
+ uint32_t *pc = uc->uc_mcontext.sc_pc;
+ uint32_t insn = *pc;
+ int is_write = 0;
+
+ /* XXX: need kernel patch to get write flag faster */
+ switch (insn >> 26) {
+ case 0x0d: /* stw */
+ case 0x0e: /* stb */
+ case 0x0f: /* stq_u */
+ case 0x24: /* stf */
+ case 0x25: /* stg */
+ case 0x26: /* sts */
+ case 0x27: /* stt */
+ case 0x2c: /* stl */
+ case 0x2d: /* stq */
+ case 0x2e: /* stl_c */
+ case 0x2f: /* stq_c */
+ is_write = 1;
+ }
+
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ is_write, &uc->uc_sigmask, puc);
+}
+#elif defined(__sparc__)
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+ void *puc)
+{
+ siginfo_t *info = pinfo;
+ int is_write;
+ uint32_t insn;
+#if !defined(__arch64__) || defined(CONFIG_SOLARIS)
+ uint32_t *regs = (uint32_t *)(info + 1);
+ void *sigmask = (regs + 20);
+ /* XXX: is there a standard glibc define ? */
+ unsigned long pc = regs[1];
+#else
+#ifdef __linux__
+ struct sigcontext *sc = puc;
+ unsigned long pc = sc->sigc_regs.tpc;
+ void *sigmask = (void *)sc->sigc_mask;
+#elif defined(__OpenBSD__)
+ struct sigcontext *uc = puc;
+ unsigned long pc = uc->sc_pc;
+ void *sigmask = (void *)(long)uc->sc_mask;
+#endif
+#endif
+
+ /* XXX: need kernel patch to get write flag faster */
+ is_write = 0;
+ insn = *(uint32_t *)pc;
+ if ((insn >> 30) == 3) {
+ switch ((insn >> 19) & 0x3f) {
+ case 0x05: /* stb */
+ case 0x15: /* stba */
+ case 0x06: /* sth */
+ case 0x16: /* stha */
+ case 0x04: /* st */
+ case 0x14: /* sta */
+ case 0x07: /* std */
+ case 0x17: /* stda */
+ case 0x0e: /* stx */
+ case 0x1e: /* stxa */
+ case 0x24: /* stf */
+ case 0x34: /* stfa */
+ case 0x27: /* stdf */
+ case 0x37: /* stdfa */
+ case 0x26: /* stqf */
+ case 0x36: /* stqfa */
+ case 0x25: /* stfsr */
+ case 0x3c: /* casa */
+ case 0x3e: /* casxa */
+ is_write = 1;
+ break;
+ }
+ }
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ is_write, sigmask, NULL);
+}
+
+#elif defined(__arm__)
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+ void *puc)
+{
+ siginfo_t *info = pinfo;
+ struct ucontext *uc = puc;
+ unsigned long pc;
+ int is_write;
+
+#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
+ pc = uc->uc_mcontext.gregs[R15];
+#else
+ pc = uc->uc_mcontext.arm_pc;
+#endif
+ /* XXX: compute is_write */
+ is_write = 0;
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ is_write,
+ &uc->uc_sigmask, puc);
+}
+
+#elif defined(__mc68000)
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+ void *puc)
+{
+ siginfo_t *info = pinfo;
+ struct ucontext *uc = puc;
+ unsigned long pc;
+ int is_write;
+
+ pc = uc->uc_mcontext.gregs[16];
+ /* XXX: compute is_write */
+ is_write = 0;
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ is_write,
+ &uc->uc_sigmask, puc);
+}
+
+#elif defined(__ia64)
+
+#ifndef __ISR_VALID
+ /* This ought to be in <bits/siginfo.h>... */
+# define __ISR_VALID 1
+#endif
+
+int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
+{
+ siginfo_t *info = pinfo;
+ struct ucontext *uc = puc;
+ unsigned long ip;
+ int is_write = 0;
+
+ ip = uc->uc_mcontext.sc_ip;
+ switch (host_signum) {
+ case SIGILL:
+ case SIGFPE:
+ case SIGSEGV:
+ case SIGBUS:
+ case SIGTRAP:
+ if (info->si_code && (info->si_segvflags & __ISR_VALID)) {
+ /* ISR.W (write-access) is bit 33: */
+ is_write = (info->si_isr >> 33) & 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return handle_cpu_signal(ip, (unsigned long)info->si_addr,
+ is_write,
+ (sigset_t *)&uc->uc_sigmask, puc);
+}
+
+#elif defined(__s390__)
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+ void *puc)
+{
+ siginfo_t *info = pinfo;
+ struct ucontext *uc = puc;
+ unsigned long pc;
+ uint16_t *pinsn;
+ int is_write = 0;
+
+ pc = uc->uc_mcontext.psw.addr;
+
+ /* ??? On linux, the non-rt signal handler has 4 (!) arguments instead
+ of the normal 2 arguments. The 3rd argument contains the "int_code"
+ from the hardware which does in fact contain the is_write value.
+ The rt signal handler, as far as I can tell, does not give this value
+ at all. Not that we could get to it from here even if it were. */
+ /* ??? This is not even close to complete, since it ignores all
+ of the read-modify-write instructions. */
+ pinsn = (uint16_t *)pc;
+ switch (pinsn[0] >> 8) {
+ case 0x50: /* ST */
+ case 0x42: /* STC */
+ case 0x40: /* STH */
+ is_write = 1;
+ break;
+ case 0xc4: /* RIL format insns */
+ switch (pinsn[0] & 0xf) {
+ case 0xf: /* STRL */
+ case 0xb: /* STGRL */
+ case 0x7: /* STHRL */
+ is_write = 1;
+ }
+ break;
+ case 0xe3: /* RXY format insns */
+ switch (pinsn[2] & 0xff) {
+ case 0x50: /* STY */
+ case 0x24: /* STG */
+ case 0x72: /* STCY */
+ case 0x70: /* STHY */
+ case 0x8e: /* STPQ */
+ case 0x3f: /* STRVH */
+ case 0x3e: /* STRV */
+ case 0x2f: /* STRVG */
+ is_write = 1;
+ }
+ break;
+ }
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ is_write, &uc->uc_sigmask, puc);
+}
+
+#elif defined(__mips__)
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+ void *puc)
+{
+ siginfo_t *info = pinfo;
+ struct ucontext *uc = puc;
+ greg_t pc = uc->uc_mcontext.pc;
+ int is_write;
+
+ /* XXX: compute is_write */
+ is_write = 0;
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ is_write, &uc->uc_sigmask, puc);
+}
+
+#elif defined(__hppa__)
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+ void *puc)
+{
+ struct siginfo *info = pinfo;
+ struct ucontext *uc = puc;
+ unsigned long pc = uc->uc_mcontext.sc_iaoq[0];
+ uint32_t insn = *(uint32_t *)pc;
+ int is_write = 0;
+
+ /* XXX: need kernel patch to get write flag faster. */
+ switch (insn >> 26) {
+ case 0x1a: /* STW */
+ case 0x19: /* STH */
+ case 0x18: /* STB */
+ case 0x1b: /* STWM */
+ is_write = 1;
+ break;
+
+ case 0x09: /* CSTWX, FSTWX, FSTWS */
+ case 0x0b: /* CSTDX, FSTDX, FSTDS */
+ /* Distinguish from coprocessor load ... */
+ is_write = (insn >> 9) & 1;
+ break;
+
+ case 0x03:
+ switch ((insn >> 6) & 15) {
+ case 0xa: /* STWS */
+ case 0x9: /* STHS */
+ case 0x8: /* STBS */
+ case 0xe: /* STWAS */
+ case 0xc: /* STBYS */
+ is_write = 1;
+ }
+ break;
+ }
+
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ is_write, &uc->uc_sigmask, puc);
+}
+
+#else
+
+#error host CPU specific signal handler needed
+
+#endif
+
+#if defined(TARGET_I386)
+
+void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
+{
+ CPUX86State *saved_env;
+
+ saved_env = env;
+ env = s;
+ if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
+ selector &= 0xffff;
+ cpu_x86_load_seg_cache(env, seg_reg, selector,
+ (selector << 4), 0xffff, 0);
+ } else {
+ helper_load_seg(seg_reg, selector);
+ }
+ env = saved_env;
+}
+
+void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32)
+{
+ CPUX86State *saved_env;
+
+ saved_env = env;
+ env = s;
+
+ helper_fsave(ptr, data32);
+
+ env = saved_env;
+}
+
+void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32)
+{
+ CPUX86State *saved_env;
+
+ saved_env = env;
+ env = s;
+
+ helper_frstor(ptr, data32);
+
+ env = saved_env;
+}
+
+#endif /* TARGET_I386 */
commit 9eff14f3d5a2950b864e9e6c3a79c53cc0eb1218
Author: Blue Swirl <blauwirbel at gmail.com>
Date: Sat May 21 08:42:35 2011 +0000
cpu-exec: prepare for user and softmmu split
There is little in common with user and softmmu versions of cpu_resume_signal(),
split them.
Fix coding style for the user emulator part.
Signed-off-by: Blue Swirl <blauwirbel at gmail.com>
diff --git a/cpu-exec.c b/cpu-exec.c
index 16a223e..f197ff9 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -64,21 +64,31 @@ void cpu_loop_exit(void)
/* exit the current TB from a signal handler. The host registers are
restored in a state compatible with the CPU emulator
*/
+#if defined(CONFIG_SOFTMMU)
+void cpu_resume_from_signal(CPUState *env1, void *puc)
+{
+ env = env1;
+
+ /* XXX: restore cpu registers saved in host registers */
+
+ env->exception_index = -1;
+ longjmp(env->jmp_env, 1);
+}
+
+#else
+
void cpu_resume_from_signal(CPUState *env1, void *puc)
{
-#if !defined(CONFIG_SOFTMMU)
#ifdef __linux__
struct ucontext *uc = puc;
#elif defined(__OpenBSD__)
struct sigcontext *uc = puc;
#endif
-#endif
env = env1;
/* XXX: restore cpu registers saved in host registers */
-#if !defined(CONFIG_SOFTMMU)
if (puc) {
/* XXX: use siglongjmp ? */
#ifdef __linux__
@@ -91,10 +101,10 @@ void cpu_resume_from_signal(CPUState *env1, void *puc)
sigprocmask(SIG_SETMASK, &uc->sc_mask, NULL);
#endif
}
-#endif
env->exception_index = -1;
longjmp(env->jmp_env, 1);
}
+#endif
/* Execute the code without caching the generated code. An interpreter
could be used if available. */
@@ -751,9 +761,11 @@ void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32)
#if !defined(CONFIG_SOFTMMU)
#if defined(TARGET_I386)
-#define EXCEPTION_ACTION raise_exception_err(env->exception_index, env->error_code)
+#define EXCEPTION_ACTION \
+ raise_exception_err(env->exception_index, env->error_code)
#else
-#define EXCEPTION_ACTION cpu_loop_exit()
+#define EXCEPTION_ACTION \
+ cpu_loop_exit()
#endif
/* 'pc' is the host PC at which the exception was raised. 'address' is
@@ -767,8 +779,9 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
TranslationBlock *tb;
int ret;
- if (cpu_single_env)
+ if (cpu_single_env) {
env = cpu_single_env; /* XXX: find a correct solution for multithread */
+ }
#if defined(DEBUG_SIGNAL)
qemu_printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
pc, address, is_write, *(unsigned long *)old_set);
@@ -780,10 +793,12 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
/* see if it is an MMU fault */
ret = cpu_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0);
- if (ret < 0)
+ if (ret < 0) {
return 0; /* not an MMU fault */
- if (ret == 0)
+ }
+ if (ret == 0) {
return 1; /* the MMU fault was handled without causing real CPU fault */
+ }
/* now we have a real cpu fault */
tb = tb_find_pc(pc);
if (tb) {
@@ -804,43 +819,43 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
#if defined(__i386__)
#if defined(__APPLE__)
-# include <sys/ucontext.h>
-
-# define EIP_sig(context) (*((unsigned long*)&(context)->uc_mcontext->ss.eip))
-# define TRAP_sig(context) ((context)->uc_mcontext->es.trapno)
-# define ERROR_sig(context) ((context)->uc_mcontext->es.err)
-# define MASK_sig(context) ((context)->uc_sigmask)
-#elif defined (__NetBSD__)
-# include <ucontext.h>
-
-# define EIP_sig(context) ((context)->uc_mcontext.__gregs[_REG_EIP])
-# define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO])
-# define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR])
-# define MASK_sig(context) ((context)->uc_sigmask)
-#elif defined (__FreeBSD__) || defined(__DragonFly__)
-# include <ucontext.h>
-
-# define EIP_sig(context) (*((unsigned long*)&(context)->uc_mcontext.mc_eip))
-# define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno)
-# define ERROR_sig(context) ((context)->uc_mcontext.mc_err)
-# define MASK_sig(context) ((context)->uc_sigmask)
+#include <sys/ucontext.h>
+
+#define EIP_sig(context) (*((unsigned long *)&(context)->uc_mcontext->ss.eip))
+#define TRAP_sig(context) ((context)->uc_mcontext->es.trapno)
+#define ERROR_sig(context) ((context)->uc_mcontext->es.err)
+#define MASK_sig(context) ((context)->uc_sigmask)
+#elif defined(__NetBSD__)
+#include <ucontext.h>
+
+#define EIP_sig(context) ((context)->uc_mcontext.__gregs[_REG_EIP])
+#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO])
+#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR])
+#define MASK_sig(context) ((context)->uc_sigmask)
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+#include <ucontext.h>
+
+#define EIP_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_eip))
+#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno)
+#define ERROR_sig(context) ((context)->uc_mcontext.mc_err)
+#define MASK_sig(context) ((context)->uc_sigmask)
#elif defined(__OpenBSD__)
-# define EIP_sig(context) ((context)->sc_eip)
-# define TRAP_sig(context) ((context)->sc_trapno)
-# define ERROR_sig(context) ((context)->sc_err)
-# define MASK_sig(context) ((context)->sc_mask)
+#define EIP_sig(context) ((context)->sc_eip)
+#define TRAP_sig(context) ((context)->sc_trapno)
+#define ERROR_sig(context) ((context)->sc_err)
+#define MASK_sig(context) ((context)->sc_mask)
#else
-# define EIP_sig(context) ((context)->uc_mcontext.gregs[REG_EIP])
-# define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
-# define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
-# define MASK_sig(context) ((context)->uc_sigmask)
+#define EIP_sig(context) ((context)->uc_mcontext.gregs[REG_EIP])
+#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
+#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
+#define MASK_sig(context) ((context)->uc_sigmask)
#endif
int cpu_signal_handler(int host_signum, void *pinfo,
void *puc)
{
siginfo_t *info = pinfo;
-#if defined(__NetBSD__) || defined (__FreeBSD__) || defined(__DragonFly__)
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
ucontext_t *uc = puc;
#elif defined(__OpenBSD__)
struct sigcontext *uc = puc;
@@ -876,10 +891,10 @@ int cpu_signal_handler(int host_signum, void *pinfo,
#define TRAP_sig(context) ((context)->sc_trapno)
#define ERROR_sig(context) ((context)->sc_err)
#define MASK_sig(context) ((context)->sc_mask)
-#elif defined (__FreeBSD__) || defined(__DragonFly__)
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
#include <ucontext.h>
-#define PC_sig(context) (*((unsigned long*)&(context)->uc_mcontext.mc_rip))
+#define PC_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_rip))
#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno)
#define ERROR_sig(context) ((context)->uc_mcontext.mc_err)
#define MASK_sig(context) ((context)->uc_sigmask)
@@ -895,7 +910,7 @@ int cpu_signal_handler(int host_signum, void *pinfo,
{
siginfo_t *info = pinfo;
unsigned long pc;
-#if defined(__NetBSD__) || defined (__FreeBSD__) || defined(__DragonFly__)
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
ucontext_t *uc = puc;
#elif defined(__OpenBSD__)
struct sigcontext *uc = puc;
@@ -918,61 +933,84 @@ int cpu_signal_handler(int host_signum, void *pinfo,
*/
#ifdef linux
/* All Registers access - only for local access */
-# define REG_sig(reg_name, context) ((context)->uc_mcontext.regs->reg_name)
+#define REG_sig(reg_name, context) \
+ ((context)->uc_mcontext.regs->reg_name)
/* Gpr Registers access */
-# define GPR_sig(reg_num, context) REG_sig(gpr[reg_num], context)
-# define IAR_sig(context) REG_sig(nip, context) /* Program counter */
-# define MSR_sig(context) REG_sig(msr, context) /* Machine State Register (Supervisor) */
-# define CTR_sig(context) REG_sig(ctr, context) /* Count register */
-# define XER_sig(context) REG_sig(xer, context) /* User's integer exception register */
-# define LR_sig(context) REG_sig(link, context) /* Link register */
-# define CR_sig(context) REG_sig(ccr, context) /* Condition register */
+#define GPR_sig(reg_num, context) REG_sig(gpr[reg_num], context)
+/* Program counter */
+#define IAR_sig(context) REG_sig(nip, context)
+/* Machine State Register (Supervisor) */
+#define MSR_sig(context) REG_sig(msr, context)
+/* Count register */
+#define CTR_sig(context) REG_sig(ctr, context)
+/* User's integer exception register */
+#define XER_sig(context) REG_sig(xer, context)
+/* Link register */
+#define LR_sig(context) REG_sig(link, context)
+/* Condition register */
+#define CR_sig(context) REG_sig(ccr, context)
+
/* Float Registers access */
-# define FLOAT_sig(reg_num, context) (((double*)((char*)((context)->uc_mcontext.regs+48*4)))[reg_num])
-# define FPSCR_sig(context) (*(int*)((char*)((context)->uc_mcontext.regs+(48+32*2)*4)))
+#define FLOAT_sig(reg_num, context) \
+ (((double *)((char *)((context)->uc_mcontext.regs + 48 * 4)))[reg_num])
+#define FPSCR_sig(context) \
+ (*(int *)((char *)((context)->uc_mcontext.regs + (48 + 32 * 2) * 4)))
/* Exception Registers access */
-# define DAR_sig(context) REG_sig(dar, context)
-# define DSISR_sig(context) REG_sig(dsisr, context)
-# define TRAP_sig(context) REG_sig(trap, context)
+#define DAR_sig(context) REG_sig(dar, context)
+#define DSISR_sig(context) REG_sig(dsisr, context)
+#define TRAP_sig(context) REG_sig(trap, context)
#endif /* linux */
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <ucontext.h>
-# define IAR_sig(context) ((context)->uc_mcontext.mc_srr0)
-# define MSR_sig(context) ((context)->uc_mcontext.mc_srr1)
-# define CTR_sig(context) ((context)->uc_mcontext.mc_ctr)
-# define XER_sig(context) ((context)->uc_mcontext.mc_xer)
-# define LR_sig(context) ((context)->uc_mcontext.mc_lr)
-# define CR_sig(context) ((context)->uc_mcontext.mc_cr)
+#define IAR_sig(context) ((context)->uc_mcontext.mc_srr0)
+#define MSR_sig(context) ((context)->uc_mcontext.mc_srr1)
+#define CTR_sig(context) ((context)->uc_mcontext.mc_ctr)
+#define XER_sig(context) ((context)->uc_mcontext.mc_xer)
+#define LR_sig(context) ((context)->uc_mcontext.mc_lr)
+#define CR_sig(context) ((context)->uc_mcontext.mc_cr)
/* Exception Registers access */
-# define DAR_sig(context) ((context)->uc_mcontext.mc_dar)
-# define DSISR_sig(context) ((context)->uc_mcontext.mc_dsisr)
-# define TRAP_sig(context) ((context)->uc_mcontext.mc_exc)
+#define DAR_sig(context) ((context)->uc_mcontext.mc_dar)
+#define DSISR_sig(context) ((context)->uc_mcontext.mc_dsisr)
+#define TRAP_sig(context) ((context)->uc_mcontext.mc_exc)
#endif /* __FreeBSD__|| __FreeBSD_kernel__ */
#ifdef __APPLE__
-# include <sys/ucontext.h>
+#include <sys/ucontext.h>
typedef struct ucontext SIGCONTEXT;
/* All Registers access - only for local access */
-# define REG_sig(reg_name, context) ((context)->uc_mcontext->ss.reg_name)
-# define FLOATREG_sig(reg_name, context) ((context)->uc_mcontext->fs.reg_name)
-# define EXCEPREG_sig(reg_name, context) ((context)->uc_mcontext->es.reg_name)
-# define VECREG_sig(reg_name, context) ((context)->uc_mcontext->vs.reg_name)
+#define REG_sig(reg_name, context) \
+ ((context)->uc_mcontext->ss.reg_name)
+#define FLOATREG_sig(reg_name, context) \
+ ((context)->uc_mcontext->fs.reg_name)
+#define EXCEPREG_sig(reg_name, context) \
+ ((context)->uc_mcontext->es.reg_name)
+#define VECREG_sig(reg_name, context) \
+ ((context)->uc_mcontext->vs.reg_name)
/* Gpr Registers access */
-# define GPR_sig(reg_num, context) REG_sig(r##reg_num, context)
-# define IAR_sig(context) REG_sig(srr0, context) /* Program counter */
-# define MSR_sig(context) REG_sig(srr1, context) /* Machine State Register (Supervisor) */
-# define CTR_sig(context) REG_sig(ctr, context)
-# define XER_sig(context) REG_sig(xer, context) /* Link register */
-# define LR_sig(context) REG_sig(lr, context) /* User's integer exception register */
-# define CR_sig(context) REG_sig(cr, context) /* Condition register */
+#define GPR_sig(reg_num, context) REG_sig(r##reg_num, context)
+/* Program counter */
+#define IAR_sig(context) REG_sig(srr0, context)
+/* Machine State Register (Supervisor) */
+#define MSR_sig(context) REG_sig(srr1, context)
+#define CTR_sig(context) REG_sig(ctr, context)
+/* Link register */
+#define XER_sig(context) REG_sig(xer, context)
+/* User's integer exception register */
+#define LR_sig(context) REG_sig(lr, context)
+/* Condition register */
+#define CR_sig(context) REG_sig(cr, context)
/* Float Registers access */
-# define FLOAT_sig(reg_num, context) FLOATREG_sig(fpregs[reg_num], context)
-# define FPSCR_sig(context) ((double)FLOATREG_sig(fpscr, context))
+#define FLOAT_sig(reg_num, context) \
+ FLOATREG_sig(fpregs[reg_num], context)
+#define FPSCR_sig(context) \
+ ((double)FLOATREG_sig(fpscr, context))
/* Exception Registers access */
-# define DAR_sig(context) EXCEPREG_sig(dar, context) /* Fault registers for coredump */
-# define DSISR_sig(context) EXCEPREG_sig(dsisr, context)
-# define TRAP_sig(context) EXCEPREG_sig(exception, context) /* number of powerpc exception taken */
+/* Fault registers for coredump */
+#define DAR_sig(context) EXCEPREG_sig(dar, context)
+#define DSISR_sig(context) EXCEPREG_sig(dsisr, context)
+/* number of powerpc exception taken */
+#define TRAP_sig(context) EXCEPREG_sig(exception, context)
#endif /* __APPLE__ */
int cpu_signal_handler(int host_signum, void *pinfo,
@@ -991,11 +1029,13 @@ int cpu_signal_handler(int host_signum, void *pinfo,
is_write = 0;
#if 0
/* ppc 4xx case */
- if (DSISR_sig(uc) & 0x00800000)
+ if (DSISR_sig(uc) & 0x00800000) {
is_write = 1;
+ }
#else
- if (TRAP_sig(uc) != 0x400 && (DSISR_sig(uc) & 0x02000000))
+ if (TRAP_sig(uc) != 0x400 && (DSISR_sig(uc) & 0x02000000)) {
is_write = 1;
+ }
#endif
return handle_cpu_signal(pc, (unsigned long)info->si_addr,
is_write, &uc->uc_sigmask, puc);
@@ -1014,18 +1054,18 @@ int cpu_signal_handler(int host_signum, void *pinfo,
/* XXX: need kernel patch to get write flag faster */
switch (insn >> 26) {
- case 0x0d: // stw
- case 0x0e: // stb
- case 0x0f: // stq_u
- case 0x24: // stf
- case 0x25: // stg
- case 0x26: // sts
- case 0x27: // stt
- case 0x2c: // stl
- case 0x2d: // stq
- case 0x2e: // stl_c
- case 0x2f: // stq_c
- is_write = 1;
+ case 0x0d: /* stw */
+ case 0x0e: /* stb */
+ case 0x0f: /* stq_u */
+ case 0x24: /* stf */
+ case 0x25: /* stg */
+ case 0x26: /* sts */
+ case 0x27: /* stt */
+ case 0x2c: /* stl */
+ case 0x2d: /* stq */
+ case 0x2e: /* stl_c */
+ case 0x2f: /* stq_c */
+ is_write = 1;
}
return handle_cpu_signal(pc, (unsigned long)info->si_addr,
@@ -1060,29 +1100,29 @@ int cpu_signal_handler(int host_signum, void *pinfo,
is_write = 0;
insn = *(uint32_t *)pc;
if ((insn >> 30) == 3) {
- switch((insn >> 19) & 0x3f) {
- case 0x05: // stb
- case 0x15: // stba
- case 0x06: // sth
- case 0x16: // stha
- case 0x04: // st
- case 0x14: // sta
- case 0x07: // std
- case 0x17: // stda
- case 0x0e: // stx
- case 0x1e: // stxa
- case 0x24: // stf
- case 0x34: // stfa
- case 0x27: // stdf
- case 0x37: // stdfa
- case 0x26: // stqf
- case 0x36: // stqfa
- case 0x25: // stfsr
- case 0x3c: // casa
- case 0x3e: // casxa
- is_write = 1;
- break;
- }
+ switch ((insn >> 19) & 0x3f) {
+ case 0x05: /* stb */
+ case 0x15: /* stba */
+ case 0x06: /* sth */
+ case 0x16: /* stha */
+ case 0x04: /* st */
+ case 0x14: /* sta */
+ case 0x07: /* std */
+ case 0x17: /* stda */
+ case 0x0e: /* stx */
+ case 0x1e: /* stxa */
+ case 0x24: /* stf */
+ case 0x34: /* stfa */
+ case 0x27: /* stdf */
+ case 0x37: /* stdfa */
+ case 0x26: /* stqf */
+ case 0x36: /* stqfa */
+ case 0x25: /* stfsr */
+ case 0x3c: /* casa */
+ case 0x3e: /* casxa */
+ is_write = 1;
+ break;
+ }
}
return handle_cpu_signal(pc, (unsigned long)info->si_addr,
is_write, sigmask, NULL);
@@ -1132,7 +1172,7 @@ int cpu_signal_handler(int host_signum, void *pinfo,
#ifndef __ISR_VALID
/* This ought to be in <bits/siginfo.h>... */
-# define __ISR_VALID 1
+# define __ISR_VALID 1
#endif
int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
@@ -1144,18 +1184,19 @@ int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
ip = uc->uc_mcontext.sc_ip;
switch (host_signum) {
- case SIGILL:
- case SIGFPE:
- case SIGSEGV:
- case SIGBUS:
- case SIGTRAP:
- if (info->si_code && (info->si_segvflags & __ISR_VALID))
- /* ISR.W (write-access) is bit 33: */
- is_write = (info->si_isr >> 33) & 1;
- break;
-
- default:
- break;
+ case SIGILL:
+ case SIGFPE:
+ case SIGSEGV:
+ case SIGBUS:
+ case SIGTRAP:
+ if (info->si_code && (info->si_segvflags & __ISR_VALID)) {
+ /* ISR.W (write-access) is bit 33: */
+ is_write = (info->si_isr >> 33) & 1;
+ }
+ break;
+
+ default:
+ break;
}
return handle_cpu_signal(ip, (unsigned long)info->si_addr,
is_write,
@@ -1269,7 +1310,7 @@ int cpu_signal_handler(int host_signum, void *pinfo,
break;
}
- return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+ return handle_cpu_signal(pc, (unsigned long)info->si_addr,
is_write, &uc->uc_sigmask, puc);
}
commit 7edfe65246e57c1970f72146c6ea11f8d3a71e2d
Author: Markus Armbruster <armbru at redhat.com>
Date: Wed May 25 14:21:14 2011 +0200
virtio-console: Simplify init callbacks
Signed-off-by: Markus Armbruster <armbru at redhat.com>
Signed-off-by: Amit Shah <amit.shah at redhat.com>
diff --git a/hw/virtio-console.c b/hw/virtio-console.c
index 713f6ef..b076331 100644
--- a/hw/virtio-console.c
+++ b/hw/virtio-console.c
@@ -74,11 +74,17 @@ static void chr_event(void *opaque, int event)
}
}
-static int generic_port_init(VirtConsole *vcon, VirtIOSerialPort *port)
+static int virtconsole_initfn(VirtIOSerialPort *port)
{
+ VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev,
vcon->port.dev.info);
+ if (port->id == 0 && !info->is_console) {
+ error_report("Port number 0 on virtio-serial devices reserved for virtconsole devices for backward compatibility.");
+ return -1;
+ }
+
if (vcon->chr) {
qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event,
vcon);
@@ -86,15 +92,8 @@ static int generic_port_init(VirtConsole *vcon, VirtIOSerialPort *port)
info->guest_open = guest_open;
info->guest_close = guest_close;
}
- return 0;
-}
-
-/* Virtio Console Ports */
-static int virtconsole_initfn(VirtIOSerialPort *port)
-{
- VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
- return generic_port_init(vcon, port);
+ return 0;
}
static int virtconsole_exitfn(VirtIOSerialPort *port)
@@ -132,26 +131,10 @@ static void virtconsole_register(void)
}
device_init(virtconsole_register)
-/* Generic Virtio Serial Ports */
-static int virtserialport_initfn(VirtIOSerialPort *port)
-{
- VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
-
- if (port->id == 0) {
- /*
- * Disallow a generic port at id 0, that's reserved for
- * console ports.
- */
- error_report("Port number 0 on virtio-serial devices reserved for virtconsole devices for backward compatibility.");
- return -1;
- }
- return generic_port_init(vcon, port);
-}
-
static VirtIOSerialPortInfo virtserialport_info = {
.qdev.name = "virtserialport",
.qdev.size = sizeof(VirtConsole),
- .init = virtserialport_initfn,
+ .init = virtconsole_initfn,
.exit = virtconsole_exitfn,
.qdev.props = (Property[]) {
DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID),
commit a15bb0d6a981de749452a5180fc8084d625671da
Author: Markus Armbruster <armbru at redhat.com>
Date: Wed May 25 14:21:13 2011 +0200
virtio-serial: Drop redundant VirtIOSerialPort member info
Signed-off-by: Markus Armbruster <armbru at redhat.com>
Signed-off-by: Amit Shah <amit.shah at redhat.com>
diff --git a/hw/virtio-console.c b/hw/virtio-console.c
index 180ac0a..713f6ef 100644
--- a/hw/virtio-console.c
+++ b/hw/virtio-console.c
@@ -76,12 +76,15 @@ static void chr_event(void *opaque, int event)
static int generic_port_init(VirtConsole *vcon, VirtIOSerialPort *port)
{
+ VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev,
+ vcon->port.dev.info);
+
if (vcon->chr) {
qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event,
vcon);
- vcon->port.info->have_data = flush_buf;
- vcon->port.info->guest_open = guest_open;
- vcon->port.info->guest_close = guest_close;
+ info->have_data = flush_buf;
+ info->guest_open = guest_open;
+ info->guest_close = guest_close;
}
return 0;
}
diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c
index ed44fab..9a12104 100644
--- a/hw/virtio-serial-bus.c
+++ b/hw/virtio-serial-bus.c
@@ -129,9 +129,13 @@ static void discard_vq_data(VirtQueue *vq, VirtIODevice *vdev)
static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq,
VirtIODevice *vdev)
{
+ VirtIOSerialPortInfo *info;
+
assert(port);
assert(virtio_queue_ready(vq));
+ info = DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info);
+
while (!port->throttled) {
unsigned int i;
@@ -149,10 +153,10 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq,
ssize_t ret;
buf_size = port->elem.out_sg[i].iov_len - port->iov_offset;
- ret = port->info->have_data(port,
- port->elem.out_sg[i].iov_base
- + port->iov_offset,
- buf_size);
+ ret = info->have_data(port,
+ port->elem.out_sg[i].iov_base
+ + port->iov_offset,
+ buf_size);
if (ret < 0 && ret != -EAGAIN) {
/* We don't handle any other type of errors here */
abort();
@@ -309,6 +313,7 @@ void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle)
static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len)
{
struct VirtIOSerialPort *port;
+ struct VirtIOSerialPortInfo *info;
struct virtio_console_control cpkt, *gcpkt;
uint8_t *buffer;
size_t buffer_len;
@@ -327,6 +332,8 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len)
if (!port && cpkt.event != VIRTIO_CONSOLE_DEVICE_READY)
return;
+ info = DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info);
+
switch(cpkt.event) {
case VIRTIO_CONSOLE_DEVICE_READY:
if (!cpkt.value) {
@@ -356,7 +363,7 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len)
* this port is a console port so that the guest can hook it
* up to hvc.
*/
- if (port->info->is_console) {
+ if (info->is_console) {
send_control_event(port, VIRTIO_CONSOLE_CONSOLE_PORT, 1);
}
@@ -385,21 +392,21 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len)
* initialised. If some app is interested in knowing about
* this event, let it know.
*/
- if (port->info->guest_ready) {
- port->info->guest_ready(port);
+ if (info->guest_ready) {
+ info->guest_ready(port);
}
break;
case VIRTIO_CONSOLE_PORT_OPEN:
port->guest_connected = cpkt.value;
- if (cpkt.value && port->info->guest_open) {
+ if (cpkt.value && info->guest_open) {
/* Send the guest opened notification if an app is interested */
- port->info->guest_open(port);
+ info->guest_open(port);
}
- if (!cpkt.value && port->info->guest_close) {
+ if (!cpkt.value && info->guest_close) {
/* Send the guest closed notification if an app is interested */
- port->info->guest_close(port);
+ info->guest_close(port);
}
break;
}
@@ -448,11 +455,13 @@ static void handle_output(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOSerial *vser;
VirtIOSerialPort *port;
+ VirtIOSerialPortInfo *info;
vser = DO_UPCAST(VirtIOSerial, vdev, vdev);
port = find_port_by_vq(vser, vq);
+ info = port ? DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info) : NULL;
- if (!port || !port->host_connected || !port->info->have_data) {
+ if (!port || !port->host_connected || !info->have_data) {
discard_vq_data(vq, vdev);
return;
}
@@ -756,7 +765,6 @@ static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base)
return -1;
}
- port->info = info;
ret = info->init(port);
if (ret) {
return ret;
@@ -787,6 +795,8 @@ static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base)
static int virtser_port_qdev_exit(DeviceState *qdev)
{
VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev);
+ VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev,
+ port->dev.info);
VirtIOSerial *vser = port->vser;
qemu_bh_delete(port->bh);
@@ -794,9 +804,9 @@ static int virtser_port_qdev_exit(DeviceState *qdev)
QTAILQ_REMOVE(&vser->ports, port, next);
- if (port->info->exit)
- port->info->exit(port);
-
+ if (info->exit) {
+ info->exit(port);
+ }
return 0;
}
diff --git a/hw/virtio-serial.h b/hw/virtio-serial.h
index ac612f2..36e9d22 100644
--- a/hw/virtio-serial.h
+++ b/hw/virtio-serial.h
@@ -75,7 +75,6 @@ typedef struct VirtIOSerialPortInfo VirtIOSerialPortInfo;
*/
struct VirtIOSerialPort {
DeviceState dev;
- VirtIOSerialPortInfo *info;
QTAILQ_ENTRY(VirtIOSerialPort) next;
commit 31d0f80f17b37a71ad4231daf05be9fab3c70292
Author: Markus Armbruster <armbru at redhat.com>
Date: Wed May 25 14:21:12 2011 +0200
virtio-serial: Drop useless property is_console
All you could ever achieve with it is break stuff, so removing it
should be safe.
Signed-off-by: Markus Armbruster <armbru at redhat.com>
Signed-off-by: Amit Shah <amit.shah at redhat.com>
diff --git a/hw/virtio-console.c b/hw/virtio-console.c
index 50b85f8..180ac0a 100644
--- a/hw/virtio-console.c
+++ b/hw/virtio-console.c
@@ -91,7 +91,6 @@ static int virtconsole_initfn(VirtIOSerialPort *port)
{
VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
- port->is_console_dummy = true;
return generic_port_init(vcon, port);
}
@@ -117,7 +116,6 @@ static VirtIOSerialPortInfo virtconsole_info = {
.init = virtconsole_initfn,
.exit = virtconsole_exitfn,
.qdev.props = (Property[]) {
- DEFINE_PROP_UINT8("is_console", VirtConsole, port.is_console_dummy, 1),
DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID),
DEFINE_PROP_CHR("chardev", VirtConsole, chr),
DEFINE_PROP_STRING("name", VirtConsole, port.name),
diff --git a/hw/virtio-serial.h b/hw/virtio-serial.h
index 350ed21..ac612f2 100644
--- a/hw/virtio-serial.h
+++ b/hw/virtio-serial.h
@@ -124,9 +124,6 @@ struct VirtIOSerialPort {
*/
QEMUBH *bh;
- /* For property backward compatibility, not used otherwise */
- uint8_t is_console_dummy;
-
/* Is the corresponding guest device open? */
bool guest_connected;
/* Is this device open for IO on the host? */
commit 2a3d57ce4278dfd898d8b5639ace21fa4a4fb9bd
Author: Markus Armbruster <armbru at redhat.com>
Date: Wed May 25 14:21:11 2011 +0200
virtio-serial: Clean up virtconsole detection
virtio-serial-bus needs to treat "virtconsole" devices specially. It
uses VirtIOSerialPort member is_console to recognize them. It gets
its value via property initialization. Cute hack, except it lets
users mess with it: "-device virtconsole,is_console=0" isn't plugged
into port 0 as it should.
Move the flag to VirtIOSerialPortInfo. Keep the property for backward
compatibility; its value has no effect.
Signed-off-by: Markus Armbruster <armbru at redhat.com>
Signed-off-by: Amit Shah <amit.shah at redhat.com>
diff --git a/hw/virtio-console.c b/hw/virtio-console.c
index de539c4..50b85f8 100644
--- a/hw/virtio-console.c
+++ b/hw/virtio-console.c
@@ -91,7 +91,7 @@ static int virtconsole_initfn(VirtIOSerialPort *port)
{
VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
- port->is_console = true;
+ port->is_console_dummy = true;
return generic_port_init(vcon, port);
}
@@ -113,10 +113,11 @@ static int virtconsole_exitfn(VirtIOSerialPort *port)
static VirtIOSerialPortInfo virtconsole_info = {
.qdev.name = "virtconsole",
.qdev.size = sizeof(VirtConsole),
+ .is_console = true,
.init = virtconsole_initfn,
.exit = virtconsole_exitfn,
.qdev.props = (Property[]) {
- DEFINE_PROP_UINT8("is_console", VirtConsole, port.is_console, 1),
+ DEFINE_PROP_UINT8("is_console", VirtConsole, port.is_console_dummy, 1),
DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID),
DEFINE_PROP_CHR("chardev", VirtConsole, chr),
DEFINE_PROP_STRING("name", VirtConsole, port.name),
diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c
index 812f481..ed44fab 100644
--- a/hw/virtio-serial-bus.c
+++ b/hw/virtio-serial-bus.c
@@ -356,7 +356,7 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len)
* this port is a console port so that the guest can hook it
* up to hvc.
*/
- if (port->is_console) {
+ if (port->info->is_console) {
send_control_event(port, VIRTIO_CONSOLE_CONSOLE_PORT, 1);
}
@@ -729,7 +729,7 @@ static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base)
* location 0. This is done for backward compatibility (old
* kernel, new qemu).
*/
- plugging_port0 = port->is_console && !find_port_by_id(port->vser, 0);
+ plugging_port0 = info->is_console && !find_port_by_id(port->vser, 0);
if (find_port_by_id(port->vser, port->id)) {
error_report("virtio-serial-bus: A port already exists at id %u\n",
diff --git a/hw/virtio-serial.h b/hw/virtio-serial.h
index b783ee2..350ed21 100644
--- a/hw/virtio-serial.h
+++ b/hw/virtio-serial.h
@@ -124,8 +124,8 @@ struct VirtIOSerialPort {
*/
QEMUBH *bh;
- /* Identify if this is a port that binds with hvc in the guest */
- uint8_t is_console;
+ /* For property backward compatibility, not used otherwise */
+ uint8_t is_console_dummy;
/* Is the corresponding guest device open? */
bool guest_connected;
@@ -137,6 +137,10 @@ struct VirtIOSerialPort {
struct VirtIOSerialPortInfo {
DeviceInfo qdev;
+
+ /* Is this a device that binds with hvc in the guest? */
+ bool is_console;
+
/*
* The per-port (or per-app) init function that's called when a
* new device is found on the bus.
commit 5e52e5f903b2648c59030637e1610b32e965d615
Author: Markus Armbruster <armbru at redhat.com>
Date: Wed May 25 14:21:10 2011 +0200
virtio-serial: Plug memory leak on qdev exit()
virtio_serial_init() allocates the VirtIOSerialBus dynamically, but
virtio_serial_exit() doesn't free it.
Fix by getting rid of the allocation.
Signed-off-by: Markus Armbruster <armbru at redhat.com>
Signed-off-by: Amit Shah <amit.shah at redhat.com>
diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c
index ca0581b..812f481 100644
--- a/hw/virtio-serial-bus.c
+++ b/hw/virtio-serial-bus.c
@@ -39,7 +39,7 @@ struct VirtIOSerial {
/* Arrays of ivqs and ovqs: one per port */
VirtQueue **ivqs, **ovqs;
- VirtIOSerialBus *bus;
+ VirtIOSerialBus bus;
DeviceState *qdev;
@@ -331,7 +331,7 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len)
case VIRTIO_CONSOLE_DEVICE_READY:
if (!cpkt.value) {
error_report("virtio-serial-bus: Guest failure in adding device %s\n",
- vser->bus->qbus.name);
+ vser->bus.qbus.name);
break;
}
/*
@@ -346,7 +346,7 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len)
case VIRTIO_CONSOLE_PORT_READY:
if (!cpkt.value) {
error_report("virtio-serial-bus: Guest failure in adding port %u for device %s\n",
- port->id, vser->bus->qbus.name);
+ port->id, vser->bus.qbus.name);
break;
}
/*
@@ -473,7 +473,7 @@ static uint32_t get_features(VirtIODevice *vdev, uint32_t features)
vser = DO_UPCAST(VirtIOSerial, vdev, vdev);
- if (vser->bus->max_nr_ports > 1) {
+ if (vser->bus.max_nr_ports > 1) {
features |= (1 << VIRTIO_CONSOLE_F_MULTIPORT);
}
return features;
@@ -650,16 +650,6 @@ static struct BusInfo virtser_bus_info = {
.print_dev = virtser_bus_dev_print,
};
-static VirtIOSerialBus *virtser_bus_new(DeviceState *dev)
-{
- VirtIOSerialBus *bus;
-
- bus = FROM_QBUS(VirtIOSerialBus, qbus_create(&virtser_bus_info, dev, NULL));
- bus->qbus.allow_hotplug = 1;
-
- return bus;
-}
-
static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
{
VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev);
@@ -843,11 +833,12 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *conf)
vser = DO_UPCAST(VirtIOSerial, vdev, vdev);
/* Spawn a new virtio-serial bus on which the ports will ride as devices */
- vser->bus = virtser_bus_new(dev);
- vser->bus->vser = vser;
+ qbus_create_inplace(&vser->bus.qbus, &virtser_bus_info, dev, NULL);
+ vser->bus.qbus.allow_hotplug = 1;
+ vser->bus.vser = vser;
QTAILQ_INIT(&vser->ports);
- vser->bus->max_nr_ports = conf->max_virtserial_ports;
+ vser->bus.max_nr_ports = conf->max_virtserial_ports;
vser->ivqs = qemu_malloc(conf->max_virtserial_ports * sizeof(VirtQueue *));
vser->ovqs = qemu_malloc(conf->max_virtserial_ports * sizeof(VirtQueue *));
@@ -867,7 +858,7 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *conf)
/* control queue: guest to host */
vser->c_ovq = virtio_add_queue(vdev, 32, control_out);
- for (i = 1; i < vser->bus->max_nr_ports; i++) {
+ for (i = 1; i < vser->bus.max_nr_ports; i++) {
/* Add a per-port queue for host to guest transfers */
vser->ivqs[i] = virtio_add_queue(vdev, 128, handle_input);
/* Add a per-per queue for guest to host transfers */
commit 199646d81522509ac2dba6d28c31e8c7d807bc93
Author: Alon Levy <alevy at redhat.com>
Date: Fri Apr 29 14:25:06 2011 +0300
virtio-serial-bus: use bh for unthrottling
Instead of calling flush_queued_data when unthrottling, schedule
a bh. That way we can return immediately to the caller, and the
flush uses the same call path as a have_data for callbackee.
No migration change is required because bh are called from vm_stop.
Signed-off-by: Alon Levy <alevy at redhat.com>
Signed-off-by: Amit Shah <amit.shah at redhat.com>
diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c
index f10d48f..ca0581b 100644
--- a/hw/virtio-serial-bus.c
+++ b/hw/virtio-serial-bus.c
@@ -285,6 +285,13 @@ size_t virtio_serial_guest_ready(VirtIOSerialPort *port)
return 0;
}
+static void flush_queued_data_bh(void *opaque)
+{
+ VirtIOSerialPort *port = opaque;
+
+ flush_queued_data(port);
+}
+
void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle)
{
if (!port) {
@@ -295,8 +302,7 @@ void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle)
if (throttle) {
return;
}
-
- flush_queued_data(port);
+ qemu_bh_schedule(port->bh);
}
/* Guest wants to notify us of some event */
@@ -726,6 +732,7 @@ static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base)
bool plugging_port0;
port->vser = bus->vser;
+ port->bh = qemu_bh_new(flush_queued_data_bh, port);
/*
* Is the first console port we're seeing? If so, put it up at
@@ -792,6 +799,7 @@ static int virtser_port_qdev_exit(DeviceState *qdev)
VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev);
VirtIOSerial *vser = port->vser;
+ qemu_bh_delete(port->bh);
remove_port(port->vser, port->id);
QTAILQ_REMOVE(&vser->ports, port, next);
diff --git a/hw/virtio-serial.h b/hw/virtio-serial.h
index 5eb948e..b783ee2 100644
--- a/hw/virtio-serial.h
+++ b/hw/virtio-serial.h
@@ -119,6 +119,11 @@ struct VirtIOSerialPort {
uint32_t iov_idx;
uint64_t iov_offset;
+ /*
+ * When unthrottling we use a bottom-half to call flush_queued_data.
+ */
+ QEMUBH *bh;
+
/* Identify if this is a port that binds with hvc in the guest */
uint8_t is_console;
commit 1455084ea2c48abf23c4e4e15e378ee43457f381
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Thu May 19 16:47:28 2011 +0200
scsi: ignore LUN field in the CDB
The LUN field in the CDB is a historical relic. Ignore it as reserved,
which is what modern SCSI specifications actually say.
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index e0c384f..a8c7372 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -518,7 +518,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
memset(outbuf, 0, buflen);
- if (req->lun || req->cmd.buf[1] >> 5) {
+ if (req->lun) {
outbuf[0] = 0x7f; /* LUN not supported */
return buflen;
}
@@ -1024,9 +1024,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
}
#endif
- if (req->lun || buf[1] >> 5) {
+ if (req->lun) {
/* Only LUN 0 supported. */
- DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : buf[1] >> 5);
+ DPRINTF("Unimplemented LUN %d\n", req->lun);
if (command != REQUEST_SENSE && command != INQUIRY) {
scsi_command_complete(r, CHECK_CONDITION,
SENSE_CODE(LUN_NOT_SUPPORTED));
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 7670606..8e59c7e 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -337,9 +337,8 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
int ret;
- if (cmd[0] != REQUEST_SENSE &&
- (req->lun != s->lun || (cmd[1] >> 5) != s->lun)) {
- DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : cmd[1] >> 5);
+ if (cmd[0] != REQUEST_SENSE && req->lun != s->lun) {
+ DPRINTF("Unimplemented LUN %d\n", req->lun);
scsi_set_sense(s, SENSE_CODE(LUN_NOT_SUPPORTED));
r->req.status = CHECK_CONDITION;
scsi_req_complete(&r->req);
commit aba1f023630146bd7150dd13e8786d1c3e5b2afb
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Fri May 20 20:18:07 2011 +0200
scsi: rename arguments to the new callbacks
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Cc: Christoph Hellwig <hch at lst.de>
diff --git a/hw/esp.c b/hw/esp.c
index 67f02ba..6d3f5d2 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -395,7 +395,7 @@ static void esp_do_dma(ESPState *s)
esp_dma_done(s);
}
-static void esp_command_complete(SCSIRequest *req, uint32_t arg)
+static void esp_command_complete(SCSIRequest *req, uint32_t status)
{
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
@@ -406,10 +406,10 @@ static void esp_command_complete(SCSIRequest *req, uint32_t arg)
s->ti_size = 0;
s->dma_left = 0;
s->async_len = 0;
- if (arg) {
+ if (status) {
DPRINTF("Command failed\n");
}
- s->status = arg;
+ s->status = status;
s->rregs[ESP_RSTAT] = STAT_ST;
esp_dma_done(s);
if (s->current_req) {
@@ -419,12 +419,12 @@ static void esp_command_complete(SCSIRequest *req, uint32_t arg)
}
}
-static void esp_transfer_data(SCSIRequest *req, uint32_t arg)
+static void esp_transfer_data(SCSIRequest *req, uint32_t len)
{
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size);
- s->async_len = arg;
+ s->async_len = len;
s->async_buf = scsi_req_get_buf(req);
if (s->dma_left) {
esp_do_dma(s);
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index c965ed4..83084b6 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -680,7 +680,7 @@ static void lsi_request_cancelled(SCSIRequest *req)
/* Record that data is available for a queued command. Returns zero if
the device was reselected, nonzero if the IO is deferred. */
-static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg)
+static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t len)
{
lsi_request *p;
@@ -693,7 +693,7 @@ static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg)
if (p->pending) {
BADF("Multiple IO pending for tag %d\n", tag);
}
- p->pending = arg;
+ p->pending = len;
/* Reselect if waiting for it, or if reselection triggers an IRQ
and the bus is free.
Since no interrupt stacking is implemented in the emulation, it
@@ -707,20 +707,20 @@ static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg)
return 0;
} else {
DPRINTF("Queueing IO tag=0x%x\n", tag);
- p->pending = arg;
+ p->pending = len;
return 1;
}
}
/* Callback to indicate that the SCSI layer has completed a command. */
-static void lsi_command_complete(SCSIRequest *req, uint32_t arg)
+static void lsi_command_complete(SCSIRequest *req, uint32_t status)
{
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
int out;
out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
- DPRINTF("Command complete status=%d\n", (int)arg);
- s->status = arg;
+ DPRINTF("Command complete status=%d\n", (int)status);
+ s->status = status;
s->command_complete = 2;
if (s->waiting && s->dbc != 0) {
/* Raise phase mismatch for short transfers. */
@@ -738,14 +738,14 @@ static void lsi_command_complete(SCSIRequest *req, uint32_t arg)
}
/* Callback to indicate that the SCSI layer has completed a transfer. */
-static void lsi_transfer_data(SCSIRequest *req, uint32_t arg)
+static void lsi_transfer_data(SCSIRequest *req, uint32_t len)
{
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
int out;
if (s->waiting == 1 || !s->current || req->tag != s->current->tag ||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
- if (lsi_queue_tag(s, req->tag, arg)) {
+ if (lsi_queue_tag(s, req->tag, len)) {
return;
}
}
@@ -753,8 +753,8 @@ static void lsi_transfer_data(SCSIRequest *req, uint32_t arg)
out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
/* host adapter (re)connected */
- DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, arg);
- s->current->dma_len = arg;
+ DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, len);
+ s->current->dma_len = len;
s->command_complete = 1;
if (s->waiting) {
if (s->waiting == 1 || s->dbc == 0) {
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index fea1f2f..1c901ef 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -480,15 +480,15 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
}
/* Callback to indicate that the SCSI layer has completed a transfer. */
-static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t arg)
+static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
vscsi_req *req = vscsi_find_req(s, sreq);
uint8_t *buf;
- int len, rc = 0;
+ int rc = 0;
- dprintf("VSCSI: SCSI xfer complete tag=0x%x arg=0x%x, req=%p\n",
- sreq->tag, arg, req);
+ dprintf("VSCSI: SCSI xfer complete tag=0x%x len=0x%x, req=%p\n",
+ sreq->tag, len, req);
if (req == NULL) {
fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag);
return;
@@ -497,7 +497,7 @@ static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t arg)
if (req->sensing) {
uint8_t *buf = scsi_req_get_buf(sreq);
- len = MIN(arg, SCSI_SENSE_BUF_SIZE);
+ len = MIN(len, SCSI_SENSE_BUF_SIZE);
dprintf("VSCSI: Sense data, %d bytes:\n", len);
dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
buf[0], buf[1], buf[2], buf[3],
@@ -511,12 +511,9 @@ static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t arg)
return;
}
- /* "arg" is how much we have read for reads and how much we want
- * to write for writes (ie, how much is to be DMA'd)
- */
- if (arg) {
+ if (len) {
buf = scsi_req_get_buf(sreq);
- rc = vscsi_srp_transfer_data(s, req, req->writing, buf, arg);
+ rc = vscsi_srp_transfer_data(s, req, req->writing, buf, len);
}
if (rc < 0) {
fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc);
@@ -531,30 +528,30 @@ static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t arg)
}
/* Callback to indicate that the SCSI layer has completed a transfer. */
-static void vscsi_command_complete(SCSIRequest *sreq, uint32_t arg)
+static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
vscsi_req *req = vscsi_find_req(s, sreq);
int32_t res_in = 0, res_out = 0;
- dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x arg=0x%x, req=%p\n",
- reason, sreq->tag, arg, req);
+ dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x status=0x%x, req=%p\n",
+ reason, sreq->tag, status, req);
if (req == NULL) {
fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag);
return;
}
- if (!req->sensing && arg == CHECK_CONDITION) {
+ if (!req->sensing && status == CHECK_CONDITION) {
vscsi_send_request_sense(s, req);
return;
}
if (req->sensing) {
dprintf("VSCSI: Sense done !\n");
- arg = CHECK_CONDITION;
+ status = CHECK_CONDITION;
} else {
- dprintf("VSCSI: Command complete err=%d\n", arg);
- if (arg == 0) {
+ dprintf("VSCSI: Command complete err=%d\n", status);
+ if (status == 0) {
/* We handle overflows, not underflows for normal commands,
* but hopefully nobody cares
*/
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 4ebf6eb..6ec2255 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -208,7 +208,7 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p)
memcpy(p->data, &csw, len);
}
-static void usb_msd_transfer_data(SCSIRequest *req, uint32_t arg)
+static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet;
@@ -218,7 +218,7 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t arg)
}
assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
- s->scsi_len = arg;
+ s->scsi_len = len;
s->scsi_buf = scsi_req_get_buf(req);
if (p) {
usb_msd_copy_data(s);
@@ -233,7 +233,7 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t arg)
}
}
-static void usb_msd_command_complete(SCSIRequest *req, uint32_t arg)
+static void usb_msd_command_complete(SCSIRequest *req, uint32_t status)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet;
@@ -241,9 +241,9 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t arg)
if (req->tag != s->tag) {
fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag);
}
- DPRINTF("Command complete %d\n", arg);
+ DPRINTF("Command complete %d\n", status);
s->residue = s->data_len;
- s->result = arg != 0;
+ s->result = status != 0;
if (s->packet) {
if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) {
/* A deferred packet with no write data remaining must be
commit c6df7102f5ebf3c9008718d044b78f1ae57aa627
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Fri Apr 22 12:27:30 2011 +0200
scsi: split command_complete callback in two
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Cc: Christoph Hellwig <hch at lst.de>
diff --git a/hw/esp.c b/hw/esp.c
index 879c8ad..67f02ba 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -395,38 +395,43 @@ static void esp_do_dma(ESPState *s)
esp_dma_done(s);
}
-static void esp_command_complete(SCSIRequest *req, int reason, uint32_t arg)
+static void esp_command_complete(SCSIRequest *req, uint32_t arg)
{
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
- if (reason == SCSI_REASON_DONE) {
- DPRINTF("SCSI Command complete\n");
- if (s->ti_size != 0)
- DPRINTF("SCSI command completed unexpectedly\n");
- s->ti_size = 0;
- s->dma_left = 0;
- s->async_len = 0;
- if (arg)
- DPRINTF("Command failed\n");
- s->status = arg;
- s->rregs[ESP_RSTAT] = STAT_ST;
+ DPRINTF("SCSI Command complete\n");
+ if (s->ti_size != 0) {
+ DPRINTF("SCSI command completed unexpectedly\n");
+ }
+ s->ti_size = 0;
+ s->dma_left = 0;
+ s->async_len = 0;
+ if (arg) {
+ DPRINTF("Command failed\n");
+ }
+ s->status = arg;
+ s->rregs[ESP_RSTAT] = STAT_ST;
+ esp_dma_done(s);
+ if (s->current_req) {
+ scsi_req_unref(s->current_req);
+ s->current_req = NULL;
+ s->current_dev = NULL;
+ }
+}
+
+static void esp_transfer_data(SCSIRequest *req, uint32_t arg)
+{
+ ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
+
+ DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size);
+ s->async_len = arg;
+ s->async_buf = scsi_req_get_buf(req);
+ if (s->dma_left) {
+ esp_do_dma(s);
+ } else if (s->dma_counter != 0 && s->ti_size <= 0) {
+ /* If this was the last part of a DMA transfer then the
+ completion interrupt is deferred to here. */
esp_dma_done(s);
- if (s->current_req) {
- scsi_req_unref(s->current_req);
- s->current_req = NULL;
- s->current_dev = NULL;
- }
- } else {
- DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size);
- s->async_len = arg;
- s->async_buf = scsi_req_get_buf(req);
- if (s->dma_left) {
- esp_do_dma(s);
- } else if (s->dma_counter != 0 && s->ti_size <= 0) {
- /* If this was the last part of a DMA transfer then the
- completion interrupt is deferred to here. */
- esp_dma_done(s);
- }
}
}
@@ -725,6 +730,7 @@ void esp_init(target_phys_addr_t espaddr, int it_shift,
}
static const struct SCSIBusOps esp_scsi_ops = {
+ .transfer_data = esp_transfer_data,
.complete = esp_command_complete,
.cancel = esp_request_cancelled
};
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 43113a1..c965ed4 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -711,32 +711,37 @@ static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg)
return 1;
}
}
- /* Callback to indicate that the SCSI layer has completed a transfer. */
-static void lsi_command_complete(SCSIRequest *req, int reason, uint32_t arg)
+
+ /* Callback to indicate that the SCSI layer has completed a command. */
+static void lsi_command_complete(SCSIRequest *req, uint32_t arg)
{
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
int out;
out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
- if (reason == SCSI_REASON_DONE) {
- DPRINTF("Command complete status=%d\n", (int)arg);
- s->status = arg;
- s->command_complete = 2;
- if (s->waiting && s->dbc != 0) {
- /* Raise phase mismatch for short transfers. */
- lsi_bad_phase(s, out, PHASE_ST);
- } else {
- lsi_set_phase(s, PHASE_ST);
- }
+ DPRINTF("Command complete status=%d\n", (int)arg);
+ s->status = arg;
+ s->command_complete = 2;
+ if (s->waiting && s->dbc != 0) {
+ /* Raise phase mismatch for short transfers. */
+ lsi_bad_phase(s, out, PHASE_ST);
+ } else {
+ lsi_set_phase(s, PHASE_ST);
+ }
- if (s->current && req == s->current->req) {
- scsi_req_unref(s->current->req);
- qemu_free(s->current);
- s->current = NULL;
- }
- lsi_resume_script(s);
- return;
+ if (s->current && req == s->current->req) {
+ scsi_req_unref(s->current->req);
+ qemu_free(s->current);
+ s->current = NULL;
}
+ lsi_resume_script(s);
+}
+
+ /* Callback to indicate that the SCSI layer has completed a transfer. */
+static void lsi_transfer_data(SCSIRequest *req, uint32_t arg)
+{
+ LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
+ int out;
if (s->waiting == 1 || !s->current || req->tag != s->current->tag ||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
@@ -745,16 +750,18 @@ static void lsi_command_complete(SCSIRequest *req, int reason, uint32_t arg)
}
}
+ out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
+
/* host adapter (re)connected */
DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, arg);
s->current->dma_len = arg;
s->command_complete = 1;
- if (!s->waiting)
- return;
- if (s->waiting == 1 || s->dbc == 0) {
- lsi_resume_script(s);
- } else {
- lsi_do_dma(s, out);
+ if (s->waiting) {
+ if (s->waiting == 1 || s->dbc == 0) {
+ lsi_resume_script(s);
+ } else {
+ lsi_do_dma(s, out);
+ }
}
}
@@ -2239,6 +2246,7 @@ static int lsi_scsi_uninit(PCIDevice *d)
}
static const struct SCSIBusOps lsi_scsi_ops = {
+ .transfer_data = lsi_transfer_data,
.complete = lsi_command_complete,
.cancel = lsi_request_cancelled
};
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index ae16a2d..837f24e 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -634,7 +634,7 @@ void scsi_req_continue(SCSIRequest *req)
void scsi_req_data(SCSIRequest *req, int len)
{
trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
- req->bus->ops->complete(req, SCSI_REASON_DATA, len);
+ req->bus->ops->transfer_data(req, len);
}
void scsi_req_print(SCSIRequest *req)
@@ -670,7 +670,7 @@ void scsi_req_complete(SCSIRequest *req)
assert(req->status != -1);
scsi_req_ref(req);
scsi_req_dequeue(req);
- req->bus->ops->complete(req, SCSI_REASON_DONE, req->status);
+ req->bus->ops->complete(req, req->status);
scsi_req_unref(req);
}
diff --git a/hw/scsi.h b/hw/scsi.h
index b56338d..c1dca35 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -9,12 +9,6 @@
#define SCSI_CMD_BUF_SIZE 16
-/* scsi-disk.c */
-enum scsi_reason {
- SCSI_REASON_DONE, /* Command complete. */
- SCSI_REASON_DATA /* Transfer complete, more data required. */
-};
-
typedef struct SCSIBus SCSIBus;
typedef struct SCSIBusOps SCSIBusOps;
typedef struct SCSIDevice SCSIDevice;
@@ -84,7 +78,8 @@ struct SCSIDeviceInfo {
};
struct SCSIBusOps {
- void (*complete)(SCSIRequest *req, int reason, uint32_t arg);
+ void (*transfer_data)(SCSIRequest *req, uint32_t arg);
+ void (*complete)(SCSIRequest *req, uint32_t arg);
void (*cancel)(SCSIRequest *req);
};
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index bae670a..fea1f2f 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -480,63 +480,34 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
}
/* Callback to indicate that the SCSI layer has completed a transfer. */
-static void vscsi_command_complete(SCSIRequest *sreq, int reason, uint32_t arg)
+static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t arg)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
vscsi_req *req = vscsi_find_req(s, sreq);
uint8_t *buf;
- int32_t res_in = 0, res_out = 0;
int len, rc = 0;
- dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x arg=0x%x, req=%p\n",
- reason, sreq->tag, arg, req);
+ dprintf("VSCSI: SCSI xfer complete tag=0x%x arg=0x%x, req=%p\n",
+ sreq->tag, arg, req);
if (req == NULL) {
fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag);
return;
}
if (req->sensing) {
- if (reason == SCSI_REASON_DONE) {
- dprintf("VSCSI: Sense done !\n");
- vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
- vscsi_put_req(s, req);
- } else {
- uint8_t *buf = scsi_req_get_buf(sreq);
-
- len = MIN(arg, SCSI_SENSE_BUF_SIZE);
- dprintf("VSCSI: Sense data, %d bytes:\n", len);
- dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
- buf[0], buf[1], buf[2], buf[3],
- buf[4], buf[5], buf[6], buf[7]);
- dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
- buf[8], buf[9], buf[10], buf[11],
- buf[12], buf[13], buf[14], buf[15]);
- memcpy(req->sense, buf, len);
- req->senselen = len;
- scsi_req_continue(req->sreq);
- }
- return;
- }
-
- if (reason == SCSI_REASON_DONE) {
- dprintf("VSCSI: Command complete err=%d\n", arg);
- if (arg == 0) {
- /* We handle overflows, not underflows for normal commands,
- * but hopefully nobody cares
- */
- if (req->writing) {
- res_out = req->data_len;
- } else {
- res_in = req->data_len;
- }
- vscsi_send_rsp(s, req, 0, res_in, res_out);
- } else if (arg == CHECK_CONDITION) {
- vscsi_send_request_sense(s, req);
- return;
- } else {
- vscsi_send_rsp(s, req, arg, 0, 0);
- }
- vscsi_put_req(s, req);
+ uint8_t *buf = scsi_req_get_buf(sreq);
+
+ len = MIN(arg, SCSI_SENSE_BUF_SIZE);
+ dprintf("VSCSI: Sense data, %d bytes:\n", len);
+ dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+ dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ buf[8], buf[9], buf[10], buf[11],
+ buf[12], buf[13], buf[14], buf[15]);
+ memcpy(req->sense, buf, len);
+ req->senselen = len;
+ scsi_req_continue(req->sreq);
return;
}
@@ -559,6 +530,45 @@ static void vscsi_command_complete(SCSIRequest *sreq, int reason, uint32_t arg)
scsi_req_continue(sreq);
}
+/* Callback to indicate that the SCSI layer has completed a transfer. */
+static void vscsi_command_complete(SCSIRequest *sreq, uint32_t arg)
+{
+ VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
+ vscsi_req *req = vscsi_find_req(s, sreq);
+ int32_t res_in = 0, res_out = 0;
+
+ dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x arg=0x%x, req=%p\n",
+ reason, sreq->tag, arg, req);
+ if (req == NULL) {
+ fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag);
+ return;
+ }
+
+ if (!req->sensing && arg == CHECK_CONDITION) {
+ vscsi_send_request_sense(s, req);
+ return;
+ }
+
+ if (req->sensing) {
+ dprintf("VSCSI: Sense done !\n");
+ arg = CHECK_CONDITION;
+ } else {
+ dprintf("VSCSI: Command complete err=%d\n", arg);
+ if (arg == 0) {
+ /* We handle overflows, not underflows for normal commands,
+ * but hopefully nobody cares
+ */
+ if (req->writing) {
+ res_out = req->data_len;
+ } else {
+ res_in = req->data_len;
+ }
+ }
+ }
+ vscsi_send_rsp(s, req, 0, res_in, res_out);
+ vscsi_put_req(s, req);
+}
+
static void vscsi_request_cancelled(SCSIRequest *sreq)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
@@ -916,6 +926,7 @@ static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data)
}
static const struct SCSIBusOps vscsi_scsi_ops = {
+ .transfer_data = vscsi_transfer_data,
.complete = vscsi_command_complete,
.cancel = vscsi_request_cancelled
};
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 78b57a6..4ebf6eb 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -208,7 +208,7 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p)
memcpy(p->data, &csw, len);
}
-static void usb_msd_command_complete(SCSIRequest *req, int reason, uint32_t arg)
+static void usb_msd_transfer_data(SCSIRequest *req, uint32_t arg)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet;
@@ -216,35 +216,7 @@ static void usb_msd_command_complete(SCSIRequest *req, int reason, uint32_t arg)
if (req->tag != s->tag) {
fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag);
}
- if (reason == SCSI_REASON_DONE) {
- DPRINTF("Command complete %d\n", arg);
- s->residue = s->data_len;
- s->result = arg != 0;
- if (s->packet) {
- if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) {
- /* A deferred packet with no write data remaining must be
- the status read packet. */
- usb_msd_send_status(s, p);
- s->mode = USB_MSDM_CBW;
- } else {
- if (s->data_len) {
- s->data_len -= s->usb_len;
- if (s->mode == USB_MSDM_DATAIN)
- memset(s->usb_buf, 0, s->usb_len);
- s->usb_len = 0;
- }
- if (s->data_len == 0)
- s->mode = USB_MSDM_CSW;
- }
- s->packet = NULL;
- usb_packet_complete(&s->dev, p);
- } else if (s->data_len == 0) {
- s->mode = USB_MSDM_CSW;
- }
- scsi_req_unref(req);
- s->req = NULL;
- return;
- }
+
assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
s->scsi_len = arg;
s->scsi_buf = scsi_req_get_buf(req);
@@ -261,6 +233,44 @@ static void usb_msd_command_complete(SCSIRequest *req, int reason, uint32_t arg)
}
}
+static void usb_msd_command_complete(SCSIRequest *req, uint32_t arg)
+{
+ MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
+ USBPacket *p = s->packet;
+
+ if (req->tag != s->tag) {
+ fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag);
+ }
+ DPRINTF("Command complete %d\n", arg);
+ s->residue = s->data_len;
+ s->result = arg != 0;
+ if (s->packet) {
+ if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) {
+ /* A deferred packet with no write data remaining must be
+ the status read packet. */
+ usb_msd_send_status(s, p);
+ s->mode = USB_MSDM_CBW;
+ } else {
+ if (s->data_len) {
+ s->data_len -= s->usb_len;
+ if (s->mode == USB_MSDM_DATAIN) {
+ memset(s->usb_buf, 0, s->usb_len);
+ }
+ s->usb_len = 0;
+ }
+ if (s->data_len == 0) {
+ s->mode = USB_MSDM_CSW;
+ }
+ }
+ s->packet = NULL;
+ usb_packet_complete(&s->dev, p);
+ } else if (s->data_len == 0) {
+ s->mode = USB_MSDM_CSW;
+ }
+ scsi_req_unref(req);
+ s->req = NULL;
+}
+
static void usb_msd_request_cancelled(SCSIRequest *req)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
@@ -494,6 +504,7 @@ static void usb_msd_password_cb(void *opaque, int err)
}
static const struct SCSIBusOps usb_msd_scsi_ops = {
+ .transfer_data = usb_msd_transfer_data,
.complete = usb_msd_command_complete,
.cancel = usb_msd_request_cancelled
};
commit 3944966d957c361a2c1eb853ebfaa51287a5f125
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Fri May 20 20:10:02 2011 +0200
esp: rename sense to status
This mirrors the LSI patch that was recently committed.
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Cc: Christoph Hellwig <hch at lst.de>
diff --git a/hw/esp.c b/hw/esp.c
index d4847db..879c8ad 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -61,7 +61,7 @@ struct ESPState {
int32_t ti_size;
uint32_t ti_rptr, ti_wptr;
uint8_t ti_buf[TI_BUFSZ];
- uint32_t sense;
+ uint32_t status;
uint32_t dma;
SCSIBus bus;
SCSIDevice *current_dev;
@@ -318,8 +318,8 @@ static void handle_satn_stop(ESPState *s)
static void write_response(ESPState *s)
{
- DPRINTF("Transfer status (sense=%d)\n", s->sense);
- s->ti_buf[0] = s->sense;
+ DPRINTF("Transfer status (status=%d)\n", s->status);
+ s->ti_buf[0] = s->status;
s->ti_buf[1] = 0;
if (s->dma) {
s->dma_memory_write(s->dma_opaque, s->ti_buf, 2);
@@ -408,7 +408,7 @@ static void esp_command_complete(SCSIRequest *req, int reason, uint32_t arg)
s->async_len = 0;
if (arg)
DPRINTF("Command failed\n");
- s->sense = arg;
+ s->status = arg;
s->rregs[ESP_RSTAT] = STAT_ST;
esp_dma_done(s);
if (s->current_req) {
@@ -688,7 +688,7 @@ static const VMStateDescription vmstate_esp = {
VMSTATE_UINT32(ti_rptr, ESPState),
VMSTATE_UINT32(ti_wptr, ESPState),
VMSTATE_BUFFER(ti_buf, ESPState),
- VMSTATE_UINT32(sense, ESPState),
+ VMSTATE_UINT32(status, ESPState),
VMSTATE_UINT32(dma, ESPState),
VMSTATE_BUFFER(cmdbuf, ESPState),
VMSTATE_UINT32(cmdlen, ESPState),
commit 2e7cc4d604206ce15d298686c0ffd77dcedc33a2
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Mon Apr 18 14:29:16 2011 +0200
scsi-generic: Handle queue full
The sg driver currently has a hardcoded limit of commands it
can handle simultaneously. When this limit is reached the
driver will return -EDOM. So we need to capture this to
enable proper return values here.
Signed-off-by: Hannes Reinecke <hare at suse.de>
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 579bab9..7670606 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -124,6 +124,9 @@ static void scsi_command_complete(void *opaque, int ret)
if (ret != 0) {
switch (ret) {
+ case -EDOM:
+ r->req.status = TASK_SET_FULL;
+ break;
case -EINVAL:
r->req.status = CHECK_CONDITION;
scsi_set_sense(s, SENSE_CODE(INVALID_FIELD));
commit 42741212ebe703a5b9273475e7c65829b8fa2e51
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Fri Apr 22 09:39:16 2011 +0200
scsi: make write_data return void
The return value is unused anyway.
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index f3eba52..e0c384f 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -269,7 +269,7 @@ static void scsi_write_complete(void * opaque, int ret)
}
}
-static int scsi_write_data(SCSIRequest *req)
+static void scsi_write_data(SCSIRequest *req)
{
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
@@ -281,7 +281,7 @@ static int scsi_write_data(SCSIRequest *req)
if (r->req.cmd.mode != SCSI_XFER_TO_DEV) {
DPRINTF("Data transfer direction invalid\n");
scsi_write_complete(r, -EINVAL);
- return 0;
+ return;
}
n = r->iov.iov_len / 512;
@@ -296,8 +296,6 @@ static int scsi_write_data(SCSIRequest *req)
/* Invoke completion routine to fetch data from host. */
scsi_write_complete(r, 0);
}
-
- return 0;
}
static void scsi_dma_restart_bh(void *opaque)
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index fc015e0..579bab9 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -277,7 +277,7 @@ static void scsi_write_complete(void * opaque, int ret)
/* Write data to a scsi device. Returns nonzero on failure.
The transfer may complete asynchronously. */
-static int scsi_write_data(SCSIRequest *req)
+static void scsi_write_data(SCSIRequest *req)
{
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
@@ -287,16 +287,13 @@ static int scsi_write_data(SCSIRequest *req)
if (r->len == 0) {
r->len = r->buflen;
scsi_req_data(&r->req, r->len);
- return 0;
+ return;
}
ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
if (ret < 0) {
scsi_command_complete(r, ret);
- return 1;
}
-
- return 0;
}
/* Return a pointer to the data buffer. */
diff --git a/hw/scsi.h b/hw/scsi.h
index 5730faa..b56338d 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -77,7 +77,7 @@ struct SCSIDeviceInfo {
void (*free_req)(SCSIRequest *req);
int32_t (*send_command)(SCSIRequest *req, uint8_t *buf);
void (*read_data)(SCSIRequest *req);
- int (*write_data)(SCSIRequest *req);
+ void (*write_data)(SCSIRequest *req);
void (*cancel_io)(SCSIRequest *req);
uint8_t *(*get_buf)(SCSIRequest *req);
int (*get_sense)(SCSIRequest *req, uint8_t *buf, int len);
commit efb9ee024845982a210bfe48a73298846adfe9da
Author: Hannes Reinecke <hare at suse.de>
Date: Mon Apr 18 12:57:22 2011 +0200
scsi-disk: add data direction checking
scsi_req_parse() already provides for a data direction setting,
so we should be using it to check for correct direction.
And we should return the sense code 'INVALID FIELD IN CDB'
in these cases.
Signed-off-by: Hannes Reinecke <hare at suse.de>
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 9567c7c..f3eba52 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -51,8 +51,6 @@ typedef struct SCSIDiskState SCSIDiskState;
typedef struct SCSIDiskReq {
SCSIRequest req;
- /* ??? We should probably keep track of whether the data transfer is
- a read or a write. Currently we rely on the host getting it right. */
/* Both sector and sector_count are in terms of qemu 512 byte blocks. */
uint64_t sector;
uint32_t sector_count;
@@ -180,6 +178,12 @@ static void scsi_read_data(SCSIRequest *req)
/* No data transfer may already be in progress */
assert(r->req.aiocb == NULL);
+ if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+ DPRINTF("Data transfer direction invalid\n");
+ scsi_read_complete(r, -EINVAL);
+ return;
+ }
+
n = r->sector_count;
if (n > SCSI_DMA_BUF_SIZE / 512)
n = SCSI_DMA_BUF_SIZE / 512;
@@ -216,16 +220,22 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type)
if (type == SCSI_REQ_STATUS_RETRY_READ) {
scsi_req_data(&r->req, 0);
}
- if (error == ENOMEM) {
+ switch (error) {
+ case ENOMEM:
scsi_command_complete(r, CHECK_CONDITION,
SENSE_CODE(TARGET_FAILURE));
- } else {
+ break;
+ case EINVAL:
+ scsi_command_complete(r, CHECK_CONDITION,
+ SENSE_CODE(INVALID_FIELD));
+ break;
+ default:
scsi_command_complete(r, CHECK_CONDITION,
SENSE_CODE(IO_ERROR));
+ break;
}
bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
}
-
return 1;
}
@@ -268,6 +278,12 @@ static int scsi_write_data(SCSIRequest *req)
/* No data transfer may already be in progress */
assert(r->req.aiocb == NULL);
+ if (r->req.cmd.mode != SCSI_XFER_TO_DEV) {
+ DPRINTF("Data transfer direction invalid\n");
+ scsi_write_complete(r, -EINVAL);
+ return 0;
+ }
+
n = r->iov.iov_len / 512;
if (n) {
qemu_iovec_init_external(&r->qiov, &r->iov, 1);
@@ -987,14 +1003,12 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
int32_t len;
- int is_write;
uint8_t command;
uint8_t *outbuf;
int rc;
command = buf[0];
outbuf = (uint8_t *)r->iov.iov_base;
- is_write = 0;
DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
if (scsi_req_parse(&r->req, buf) != 0) {
@@ -1074,7 +1088,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
goto illegal_lba;
r->sector = r->req.cmd.lba * s->cluster_size;
r->sector_count = len * s->cluster_size;
- is_write = 1;
break;
case MODE_SELECT:
DPRINTF("Mode Select(6) (len %lu)\n", (long)r->req.cmd.xfer);
@@ -1140,13 +1153,13 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
}
len = r->sector_count * 512 + r->iov.iov_len;
- if (is_write) {
- len = -len;
+ if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+ return -len;
} else {
if (!r->sector_count)
r->sector_count = -1;
+ return len;
}
- return len;
}
static void scsi_disk_reset(DeviceState *dev)
commit 74382217ca8f25a530c9f63e6b523e6259d7719a
Author: Hannes Reinecke <hare at suse.de>
Date: Mon Apr 18 13:36:02 2011 +0200
scsi: Implement 'get_sense' callback
The get_sense callback copies existing sense information into
the provided buffer. This is required if sense information
should be transferred together with the command response.
Signed-off-by: Hannes Reinecke <hare at suse.de>
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 686d59d..ae16a2d 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -156,6 +156,15 @@ uint8_t *scsi_req_get_buf(SCSIRequest *req)
return req->dev->info->get_buf(req);
}
+int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len)
+{
+ if (req->dev->info->get_sense) {
+ return req->dev->info->get_sense(req, buf, len);
+ } else {
+ return 0;
+ }
+}
+
int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf)
{
int32_t rc;
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index adee8fe..9567c7c 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -340,6 +340,14 @@ static uint8_t *scsi_get_buf(SCSIRequest *req)
return (uint8_t *)r->iov.iov_base;
}
+/* Copy sense information into the provided buffer */
+static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+
+ return scsi_build_sense(s->sense, outbuf, len, len > 14);
+}
+
static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
@@ -1257,6 +1265,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
+ .get_sense = scsi_get_sense,
.qdev.props = (Property[]) {
DEFINE_SCSI_DISK_PROPERTIES(),
DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
@@ -1277,6 +1286,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
+ .get_sense = scsi_get_sense,
.qdev.props = (Property[]) {
DEFINE_SCSI_DISK_PROPERTIES(),
DEFINE_PROP_END_OF_LIST(),
@@ -1296,6 +1306,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
+ .get_sense = scsi_get_sense,
.qdev.props = (Property[]) {
DEFINE_SCSI_DISK_PROPERTIES(),
DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 90f2a4a..fc015e0 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -79,6 +79,23 @@ static void scsi_clear_sense(SCSIGenericState *s)
s->driver_status = 0;
}
+static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len)
+{
+ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
+ int size = SCSI_SENSE_BUF_SIZE;
+
+ if (!(s->driver_status & SG_ERR_DRIVER_SENSE)) {
+ size = scsi_build_sense(SENSE_CODE(NO_SENSE), s->sensebuf,
+ SCSI_SENSE_BUF_SIZE, 0);
+ }
+ if (size > len) {
+ size = len;
+ }
+ memcpy(outbuf, s->sensebuf, size);
+
+ return size;
+}
+
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun)
{
SCSIRequest *req;
@@ -535,6 +552,7 @@ static SCSIDeviceInfo scsi_generic_info = {
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
+ .get_sense = scsi_get_sense,
.qdev.props = (Property[]) {
DEFINE_BLOCK_PROPERTIES(SCSIGenericState, qdev.conf),
DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/scsi.h b/hw/scsi.h
index edf6828..5730faa 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -80,6 +80,7 @@ struct SCSIDeviceInfo {
int (*write_data)(SCSIRequest *req);
void (*cancel_io)(SCSIRequest *req);
uint8_t *(*get_buf)(SCSIRequest *req);
+ int (*get_sense)(SCSIRequest *req, uint8_t *buf, int len);
};
struct SCSIBusOps {
@@ -155,6 +156,7 @@ void scsi_req_continue(SCSIRequest *req);
void scsi_req_data(SCSIRequest *req, int len);
void scsi_req_complete(SCSIRequest *req);
uint8_t *scsi_req_get_buf(SCSIRequest *req);
+int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len);
void scsi_req_abort(SCSIRequest *req, int status);
void scsi_req_cancel(SCSIRequest *req);
void scsi_device_purge_requests(SCSIDevice *sdev);
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 762a22e..bae670a 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -450,6 +450,15 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
uint8_t *cdb = req->iu.srp.cmd.cdb;
int n;
+ n = scsi_req_get_sense(req->sreq, req->sense, sizeof(req->sense));
+ if (n) {
+ req->senselen = n;
+ vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
+ vscsi_put_req(s, req);
+ return;
+ }
+
+ dprintf("VSCSI: Got CHECK_CONDITION, requesting sense...\n");
cdb[0] = 3;
cdb[1] = 0;
cdb[2] = 0;
@@ -522,7 +531,6 @@ static void vscsi_command_complete(SCSIRequest *sreq, int reason, uint32_t arg)
}
vscsi_send_rsp(s, req, 0, res_in, res_out);
} else if (arg == CHECK_CONDITION) {
- dprintf("VSCSI: Got CHECK_CONDITION, requesting sense...\n");
vscsi_send_request_sense(s, req);
return;
} else {
commit 0c34459b6af1b7ed2f000995b9bcb1c722646fbb
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Thu Apr 21 13:21:02 2011 +0200
scsi: introduce scsi_req_get_buf
... and remove some SCSIDevice variables or fields that now become unused.
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/esp.c b/hw/esp.c
index ce2d3b0..d4847db 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -419,7 +419,7 @@ static void esp_command_complete(SCSIRequest *req, int reason, uint32_t arg)
} else {
DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size);
s->async_len = arg;
- s->async_buf = s->current_dev->info->get_buf(req);
+ s->async_buf = scsi_req_get_buf(req);
if (s->dma_left) {
esp_do_dma(s);
} else if (s->dma_counter != 0 && s->ti_size <= 0) {
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index e8409b7..43113a1 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -569,7 +569,7 @@ static void lsi_do_dma(LSIState *s, int out)
s->dnad += count;
s->dbc -= count;
if (s->current->dma_buf == NULL) {
- s->current->dma_buf = dev->info->get_buf(s->current->req);
+ s->current->dma_buf = scsi_req_get_buf(s->current->req);
}
/* ??? Set SFBR to first data byte. */
if (out) {
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index fb96bde..686d59d 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -151,6 +151,11 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun)
return d->info->alloc_req(d, tag, lun);
}
+uint8_t *scsi_req_get_buf(SCSIRequest *req)
+{
+ return req->dev->info->get_buf(req);
+}
+
int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf)
{
int32_t rc;
diff --git a/hw/scsi.h b/hw/scsi.h
index 6fd75dd..edf6828 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -154,6 +154,7 @@ void scsi_req_print(SCSIRequest *req);
void scsi_req_continue(SCSIRequest *req);
void scsi_req_data(SCSIRequest *req, int len);
void scsi_req_complete(SCSIRequest *req);
+uint8_t *scsi_req_get_buf(SCSIRequest *req);
void scsi_req_abort(SCSIRequest *req, int status);
void scsi_req_cancel(SCSIRequest *req);
void scsi_device_purge_requests(SCSIDevice *sdev);
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 1e47fb9..762a22e 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -74,7 +74,6 @@ typedef struct vscsi_req {
union viosrp_iu iu;
/* SCSI request tracking */
- SCSIDevice *sdev;
SCSIRequest *sreq;
uint32_t qtag; /* qemu tag != srp tag */
int lun;
@@ -476,7 +475,6 @@ static void vscsi_command_complete(SCSIRequest *sreq, int reason, uint32_t arg)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
vscsi_req *req = vscsi_find_req(s, sreq);
- SCSIDevice *sdev;
uint8_t *buf;
int32_t res_in = 0, res_out = 0;
int len, rc = 0;
@@ -487,7 +485,6 @@ static void vscsi_command_complete(SCSIRequest *sreq, int reason, uint32_t arg)
fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag);
return;
}
- sdev = req->sdev;
if (req->sensing) {
if (reason == SCSI_REASON_DONE) {
@@ -495,7 +492,7 @@ static void vscsi_command_complete(SCSIRequest *sreq, int reason, uint32_t arg)
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
vscsi_put_req(s, req);
} else {
- uint8_t *buf = sdev->info->get_buf(sreq);
+ uint8_t *buf = scsi_req_get_buf(sreq);
len = MIN(arg, SCSI_SENSE_BUF_SIZE);
dprintf("VSCSI: Sense data, %d bytes:\n", len);
@@ -539,7 +536,7 @@ static void vscsi_command_complete(SCSIRequest *sreq, int reason, uint32_t arg)
* to write for writes (ie, how much is to be DMA'd)
*/
if (arg) {
- buf = sdev->info->get_buf(sreq);
+ buf = scsi_req_get_buf(sreq);
rc = vscsi_srp_transfer_data(s, req, req->writing, buf, arg);
}
if (rc < 0) {
@@ -646,7 +643,6 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
} return 1;
}
- req->sdev = sdev;
req->lun = lun;
req->sreq = scsi_req_new(sdev, req->qtag, lun);
n = scsi_req_enqueue(req->sreq, srp->cmd.cdb);
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index d4c2234..78b57a6 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -247,7 +247,7 @@ static void usb_msd_command_complete(SCSIRequest *req, int reason, uint32_t arg)
}
assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
s->scsi_len = arg;
- s->scsi_buf = s->scsi_dev->info->get_buf(req);
+ s->scsi_buf = scsi_req_get_buf(req);
if (p) {
usb_msd_copy_data(s);
if (s->usb_len == 0) {
commit ad3376cc558f69606ac25ab6d597db71c969d8b6
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Mon Apr 18 15:28:11 2011 +0200
scsi: introduce scsi_req_continue
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Cc: Christoph Hellwig <hch at lst.de>
diff --git a/hw/esp.c b/hw/esp.c
index 6e21684..ce2d3b0 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -253,11 +253,10 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
s->dma_counter = 0;
if (datalen > 0) {
s->rregs[ESP_RSTAT] |= STAT_DI;
- s->current_dev->info->read_data(s->current_req);
} else {
s->rregs[ESP_RSTAT] |= STAT_DO;
- s->current_dev->info->write_data(s->current_req);
}
+ scsi_req_continue(s->current_req);
}
s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
s->rregs[ESP_RSEQ] = SEQ_CD;
@@ -383,22 +382,17 @@ static void esp_do_dma(ESPState *s)
else
s->ti_size -= len;
if (s->async_len == 0) {
- if (to_device) {
- // ti_size is negative
- s->current_dev->info->write_data(s->current_req);
- } else {
- s->current_dev->info->read_data(s->current_req);
- /* If there is still data to be read from the device then
- complete the DMA operation immediately. Otherwise defer
- until the scsi layer has completed. */
- if (s->dma_left == 0 && s->ti_size > 0) {
- esp_dma_done(s);
- }
+ scsi_req_continue(s->current_req);
+ /* If there is still data to be read from the device then
+ complete the DMA operation immediately. Otherwise defer
+ until the scsi layer has completed. */
+ if (to_device || s->dma_left != 0 || s->ti_size == 0) {
+ return;
}
- } else {
- /* Partially filled a scsi buffer. Complete immediately. */
- esp_dma_done(s);
}
+
+ /* Partially filled a scsi buffer. Complete immediately. */
+ esp_dma_done(s);
}
static void esp_command_complete(SCSIRequest *req, int reason, uint32_t arg)
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 6b78f2a..e8409b7 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -580,13 +580,7 @@ static void lsi_do_dma(LSIState *s, int out)
s->current->dma_len -= count;
if (s->current->dma_len == 0) {
s->current->dma_buf = NULL;
- if (out) {
- /* Write the data. */
- dev->info->write_data(s->current->req);
- } else {
- /* Request any remaining data. */
- dev->info->read_data(s->current->req);
- }
+ scsi_req_continue(s->current->req);
} else {
s->current->dma_buf += count;
lsi_resume_script(s);
@@ -791,14 +785,14 @@ static void lsi_do_command(LSIState *s)
s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun);
n = scsi_req_enqueue(s->current->req, buf);
- if (n > 0) {
- lsi_set_phase(s, PHASE_DI);
- dev->info->read_data(s->current->req);
- } else if (n < 0) {
- lsi_set_phase(s, PHASE_DO);
- dev->info->write_data(s->current->req);
+ if (n) {
+ if (n > 0) {
+ lsi_set_phase(s, PHASE_DI);
+ } else if (n < 0) {
+ lsi_set_phase(s, PHASE_DO);
+ }
+ scsi_req_continue(s->current->req);
}
-
if (!s->command_complete) {
if (n) {
/* Command did not complete immediately so disconnect. */
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 6ac2650..fb96bde 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -602,11 +602,21 @@ void scsi_req_unref(SCSIRequest *req)
}
}
+/* Tell the device that we finished processing this chunk of I/O. It
+ will start the next chunk or complete the command. */
+void scsi_req_continue(SCSIRequest *req)
+{
+ trace_scsi_req_continue(req->dev->id, req->lun, req->tag);
+ if (req->cmd.mode == SCSI_XFER_TO_DEV) {
+ req->dev->info->write_data(req);
+ } else {
+ req->dev->info->read_data(req);
+ }
+}
+
/* Called by the devices when data is ready for the HBA. The HBA should
start a DMA operation to read or fill the device's data buffer.
- Once it completes, calling one of req->dev->info->read_data or
- req->dev->info->write_data (depending on the direction of the
- transfer) will restart I/O. */
+ Once it completes, calling scsi_req_continue will restart I/O. */
void scsi_req_data(SCSIRequest *req, int len)
{
trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
diff --git a/hw/scsi.h b/hw/scsi.h
index 928cbf3..6fd75dd 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -151,6 +151,7 @@ void scsi_req_unref(SCSIRequest *req);
int scsi_req_parse(SCSIRequest *req, uint8_t *buf);
void scsi_req_print(SCSIRequest *req);
+void scsi_req_continue(SCSIRequest *req);
void scsi_req_data(SCSIRequest *req, int len);
void scsi_req_complete(SCSIRequest *req);
void scsi_req_abort(SCSIRequest *req, int status);
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index fcdfad4..1e47fb9 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -448,7 +448,6 @@ static int vscsi_preprocess_desc(vscsi_req *req)
static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
{
- SCSIDevice *sdev = req->sdev;
uint8_t *cdb = req->iu.srp.cmd.cdb;
int n;
@@ -469,7 +468,7 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
} else if (n == 0) {
return;
}
- sdev->info->read_data(req->sreq);
+ scsi_req_continue(req->sreq);
}
/* Callback to indicate that the SCSI layer has completed a transfer. */
@@ -508,7 +507,7 @@ static void vscsi_command_complete(SCSIRequest *sreq, int reason, uint32_t arg)
buf[12], buf[13], buf[14], buf[15]);
memcpy(req->sense, buf, len);
req->senselen = len;
- sdev->info->read_data(sreq);
+ scsi_req_continue(req->sreq);
}
return;
}
@@ -552,11 +551,7 @@ static void vscsi_command_complete(SCSIRequest *sreq, int reason, uint32_t arg)
/* Start next chunk */
req->data_len -= rc;
- if (req->writing) {
- sdev->info->write_data(sreq);
- } else {
- sdev->info->read_data(sreq);
- }
+ scsi_req_continue(sreq);
}
static void vscsi_request_cancelled(SCSIRequest *sreq)
@@ -667,15 +662,14 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
/* Preprocess RDMA descriptors */
vscsi_preprocess_desc(req);
- }
- /* Get transfer direction and initiate transfer */
- if (n > 0) {
- req->data_len = n;
- sdev->info->read_data(req->sreq);
- } else if (n < 0) {
- req->data_len = -n;
- sdev->info->write_data(req->sreq);
+ /* Get transfer direction and initiate transfer */
+ if (n > 0) {
+ req->data_len = n;
+ } else if (n < 0) {
+ req->data_len = -n;
+ }
+ scsi_req_continue(req->sreq);
}
/* Don't touch req here, it may have been recycled already */
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index efb15b0..d4c2234 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -190,11 +190,7 @@ static void usb_msd_copy_data(MSDState *s)
s->scsi_buf += len;
s->data_len -= len;
if (s->scsi_len == 0 || s->data_len == 0) {
- if (s->mode == USB_MSDM_DATAIN) {
- s->scsi_dev->info->read_data(s->req);
- } else if (s->mode == USB_MSDM_DATAOUT) {
- s->scsi_dev->info->write_data(s->req);
- }
+ scsi_req_continue(s->req);
}
}
@@ -249,6 +245,7 @@ static void usb_msd_command_complete(SCSIRequest *req, int reason, uint32_t arg)
s->req = NULL;
return;
}
+ assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
s->scsi_len = arg;
s->scsi_buf = s->scsi_dev->info->get_buf(req);
if (p) {
@@ -381,12 +378,8 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
scsi_req_enqueue(s->req, cbw.cmd);
/* ??? Should check that USB and SCSI data transfer
directions match. */
- if (s->residue == 0) {
- if (s->mode == USB_MSDM_DATAIN) {
- s->scsi_dev->info->read_data(s->req);
- } else if (s->mode == USB_MSDM_DATAOUT) {
- s->scsi_dev->info->write_data(s->req);
- }
+ if (s->mode != USB_MSDM_CSW && s->residue == 0) {
+ scsi_req_continue(s->req);
}
ret = len;
break;
diff --git a/trace-events b/trace-events
index 0340eb2..3137a15 100644
--- a/trace-events
+++ b/trace-events
@@ -209,6 +209,7 @@ disable usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature
disable scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d"
disable scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d"
disable scsi_req_dequeue(int target, int lun, int tag) "target %d lun %d tag %d"
+disable scsi_req_continue(int target, int lun, int tag) "target %d lun %d tag %d"
disable scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer, uint64_t lba) "target %d lun %d tag %d command %d dir %d length %d lba %"PRIu64""
disable scsi_req_parse_bad(int target, int lun, int tag, int cmd) "target %d lun %d tag %d command %d"
commit 43a2b33957697347e4e6d00557221538231bfe4d
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Mon Apr 18 19:09:55 2011 +0200
scsi: introduce scsi_req_new
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/esp.c b/hw/esp.c
index 238422a..6e21684 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -244,7 +244,7 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
DPRINTF("do_busid_cmd: busid 0x%x\n", busid);
lun = busid & 7;
- s->current_req = s->current_dev->info->alloc_req(s->current_dev, 0, lun);
+ s->current_req = scsi_req_new(s->current_dev, 0, lun);
datalen = scsi_req_enqueue(s->current_req, buf);
s->ti_size = datalen;
if (datalen != 0) {
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 185622d..6b78f2a 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -788,8 +788,7 @@ static void lsi_do_command(LSIState *s)
assert(s->current == NULL);
s->current = qemu_mallocz(sizeof(lsi_request));
s->current->tag = s->select_tag;
- s->current->req = dev->info->alloc_req(dev, s->current->tag,
- s->current_lun);
+ s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun);
n = scsi_req_enqueue(s->current->req, buf);
if (n > 0) {
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 2e6e7c8..6ac2650 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -146,6 +146,11 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l
return req;
}
+SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun)
+{
+ return d->info->alloc_req(d, tag, lun);
+}
+
int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf)
{
int32_t rc;
diff --git a/hw/scsi.h b/hw/scsi.h
index 839bc0b..928cbf3 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -143,6 +143,7 @@ int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed);
int scsi_sense_valid(SCSISense sense);
SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun);
+SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun);
int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf);
void scsi_req_free(SCSIRequest *req);
SCSIRequest *scsi_req_ref(SCSIRequest *req);
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 54fd4e8..fcdfad4 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -653,7 +653,7 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
req->sdev = sdev;
req->lun = lun;
- req->sreq = sdev->info->alloc_req(sdev, req->qtag, lun);
+ req->sreq = scsi_req_new(sdev, req->qtag, lun);
n = scsi_req_enqueue(req->sreq, srp->cmd.cdb);
dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n",
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index ccfae61..efb15b0 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -377,7 +377,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
s->tag, cbw.flags, cbw.cmd_len, s->data_len);
s->residue = 0;
s->scsi_len = 0;
- s->req = s->scsi_dev->info->alloc_req(s->scsi_dev, s->tag, 0);
+ s->req = scsi_req_new(s->scsi_dev, s->tag, 0);
scsi_req_enqueue(s->req, cbw.cmd);
/* ??? Should check that USB and SCSI data transfer
directions match. */
commit fc4f0754c775d4b5e0fb90e503f7e505f62fb8ed
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Mon Apr 18 19:07:23 2011 +0200
scsi: do not call send_command directly
Move the common part of scsi-disk.c and scsi-generic.c to the SCSI layer.
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/esp.c b/hw/esp.c
index f2677dc..238422a 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -245,7 +245,7 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
DPRINTF("do_busid_cmd: busid 0x%x\n", busid);
lun = busid & 7;
s->current_req = s->current_dev->info->alloc_req(s->current_dev, 0, lun);
- datalen = s->current_dev->info->send_command(s->current_req, buf);
+ datalen = scsi_req_enqueue(s->current_req, buf);
s->ti_size = datalen;
if (datalen != 0) {
s->rregs[ESP_RSTAT] = STAT_TC;
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index bca889a..185622d 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -791,7 +791,7 @@ static void lsi_do_command(LSIState *s)
s->current->req = dev->info->alloc_req(dev, s->current->tag,
s->current_lun);
- n = dev->info->send_command(s->current->req, buf);
+ n = scsi_req_enqueue(s->current->req, buf);
if (n > 0) {
lsi_set_phase(s, PHASE_DI);
dev->info->read_data(s->current->req);
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index d322f3a..2e6e7c8 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -146,12 +146,19 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l
return req;
}
-void scsi_req_enqueue(SCSIRequest *req)
+int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf)
{
+ int32_t rc;
+
assert(!req->enqueued);
scsi_req_ref(req);
req->enqueued = true;
QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
+
+ scsi_req_ref(req);
+ rc = req->dev->info->send_command(req, buf);
+ scsi_req_unref(req);
+ return rc;
}
static void scsi_req_dequeue(SCSIRequest *req)
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 63aa8f1..adee8fe 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -984,7 +984,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
uint8_t *outbuf;
int rc;
- scsi_req_enqueue(req);
command = buf[0];
outbuf = (uint8_t *)r->iov.iov_base;
is_write = 0;
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 64cbe8b..90f2a4a 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -320,7 +320,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
int ret;
- scsi_req_enqueue(req);
if (cmd[0] != REQUEST_SENSE &&
(req->lun != s->lun || (cmd[1] >> 5) != s->lun)) {
DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : cmd[1] >> 5);
diff --git a/hw/scsi.h b/hw/scsi.h
index 7a7c9ef..839bc0b 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -143,7 +143,7 @@ int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed);
int scsi_sense_valid(SCSISense sense);
SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun);
-void scsi_req_enqueue(SCSIRequest *req);
+int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf);
void scsi_req_free(SCSIRequest *req);
SCSIRequest *scsi_req_ref(SCSIRequest *req);
void scsi_req_unref(SCSIRequest *req);
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 5aaf95b..54fd4e8 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -459,7 +459,7 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
cdb[4] = 96;
cdb[5] = 0;
req->sensing = 1;
- n = sdev->info->send_command(req->sreq, cdb);
+ n = scsi_req_enqueue(req->sreq, cdb);
dprintf("VSCSI: Queued request sense tag 0x%x\n", req->qtag);
if (n < 0) {
fprintf(stderr, "VSCSI: REQUEST_SENSE wants write data !?!?!?\n");
@@ -654,7 +654,7 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
req->sdev = sdev;
req->lun = lun;
req->sreq = sdev->info->alloc_req(sdev, req->qtag, lun);
- n = sdev->info->send_command(req->sreq, srp->cmd.cdb);
+ n = scsi_req_enqueue(req->sreq, srp->cmd.cdb);
dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n",
req->qtag, srp->cmd.cdb[0], id, lun, n);
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index ce92682..ccfae61 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -378,7 +378,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
s->residue = 0;
s->scsi_len = 0;
s->req = s->scsi_dev->info->alloc_req(s->scsi_dev, s->tag, 0);
- s->scsi_dev->info->send_command(s->req, cbw.cmd);
+ scsi_req_enqueue(s->req, cbw.cmd);
/* ??? Should check that USB and SCSI data transfer
directions match. */
if (s->residue == 0) {
commit a1f0cce2ac0243572ff72aa561da67fe3766a395
Author: Hannes Reinecke <hare at suse.de>
Date: Mon Apr 18 12:53:14 2011 +0200
scsi: Update sense code handling
The SCSI spec has a quite detailed list of sense codes available.
It even mandates the use of specific ones for some failure cases.
The current implementation just has one type of generic error
which is actually a violation of the spec in certain cases.
This patch introduces various predefined sense codes to have the
sense code reporting more in line with the spec.
On top of Hannes's patch I fixed the reply to REQUEST SENSE commands
with DESC=0 and a small (<18) length.
Signed-off-by: Hannes Reinecke <hare at suse.de>
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index fd1d60f..d322f3a 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -154,7 +154,7 @@ void scsi_req_enqueue(SCSIRequest *req)
QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
}
-void scsi_req_dequeue(SCSIRequest *req)
+static void scsi_req_dequeue(SCSIRequest *req)
{
trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag);
if (req->enqueued) {
@@ -391,6 +391,95 @@ int scsi_req_parse(SCSIRequest *req, uint8_t *buf)
return 0;
}
+/*
+ * Predefined sense codes
+ */
+
+/* No sense data available */
+const struct SCSISense sense_code_NO_SENSE = {
+ .key = NO_SENSE , .asc = 0x00 , .ascq = 0x00
+};
+
+/* LUN not ready, Manual intervention required */
+const struct SCSISense sense_code_LUN_NOT_READY = {
+ .key = NOT_READY, .asc = 0x04, .ascq = 0x03
+};
+
+/* LUN not ready, Medium not present */
+const struct SCSISense sense_code_NO_MEDIUM = {
+ .key = NOT_READY, .asc = 0x3a, .ascq = 0x00
+};
+
+/* Hardware error, internal target failure */
+const struct SCSISense sense_code_TARGET_FAILURE = {
+ .key = HARDWARE_ERROR, .asc = 0x44, .ascq = 0x00
+};
+
+/* Illegal request, invalid command operation code */
+const struct SCSISense sense_code_INVALID_OPCODE = {
+ .key = ILLEGAL_REQUEST, .asc = 0x20, .ascq = 0x00
+};
+
+/* Illegal request, LBA out of range */
+const struct SCSISense sense_code_LBA_OUT_OF_RANGE = {
+ .key = ILLEGAL_REQUEST, .asc = 0x21, .ascq = 0x00
+};
+
+/* Illegal request, Invalid field in CDB */
+const struct SCSISense sense_code_INVALID_FIELD = {
+ .key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00
+};
+
+/* Illegal request, LUN not supported */
+const struct SCSISense sense_code_LUN_NOT_SUPPORTED = {
+ .key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00
+};
+
+/* Command aborted, I/O process terminated */
+const struct SCSISense sense_code_IO_ERROR = {
+ .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06
+};
+
+/* Command aborted, I_T Nexus loss occurred */
+const struct SCSISense sense_code_I_T_NEXUS_LOSS = {
+ .key = ABORTED_COMMAND, .asc = 0x29, .ascq = 0x07
+};
+
+/* Command aborted, Logical Unit failure */
+const struct SCSISense sense_code_LUN_FAILURE = {
+ .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01
+};
+
+/*
+ * scsi_build_sense
+ *
+ * Build a sense buffer
+ */
+int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed)
+{
+ if (!fixed && len < 8) {
+ return 0;
+ }
+
+ memset(buf, 0, len);
+ if (fixed) {
+ /* Return fixed format sense buffer */
+ buf[0] = 0xf0;
+ buf[2] = sense.key;
+ buf[7] = 7;
+ buf[12] = sense.asc;
+ buf[13] = sense.ascq;
+ return MIN(len, 18);
+ } else {
+ /* Return descriptor format sense buffer */
+ buf[0] = 0x72;
+ buf[1] = sense.key;
+ buf[2] = sense.asc;
+ buf[3] = sense.ascq;
+ return 8;
+ }
+}
+
static const char *scsi_command_name(uint8_t cmd)
{
static const char *names[] = {
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 08633db..63aa8f1 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -49,10 +49,6 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
typedef struct SCSIDiskState SCSIDiskState;
-typedef struct SCSISense {
- uint8_t key;
-} SCSISense;
-
typedef struct SCSIDiskReq {
SCSIRequest req;
/* ??? We should probably keep track of whether the data transfer is
@@ -111,24 +107,19 @@ static void scsi_disk_clear_sense(SCSIDiskState *s)
memset(&s->sense, 0, sizeof(s->sense));
}
-static void scsi_disk_set_sense(SCSIDiskState *s, uint8_t key)
-{
- s->sense.key = key;
-}
-
-static void scsi_req_set_status(SCSIDiskReq *r, int status, int sense_code)
+static void scsi_req_set_status(SCSIDiskReq *r, int status, SCSISense sense)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
r->req.status = status;
- scsi_disk_set_sense(s, sense_code);
+ s->sense = sense;
}
/* Helper function for command completion. */
-static void scsi_command_complete(SCSIDiskReq *r, int status, int sense)
+static void scsi_command_complete(SCSIDiskReq *r, int status, SCSISense sense)
{
- DPRINTF("Command complete tag=0x%x status=%d sense=%d\n",
- r->req.tag, status, sense);
+ DPRINTF("Command complete tag=0x%x status=%d sense=%d/%d/%d\n",
+ r->req.tag, status, sense.key, sense.asc, sense.ascq);
scsi_req_set_status(r, status, sense);
scsi_req_complete(&r->req);
}
@@ -182,7 +173,7 @@ static void scsi_read_data(SCSIRequest *req)
}
DPRINTF("Read sector_count=%d\n", r->sector_count);
if (r->sector_count == 0) {
- scsi_command_complete(r, GOOD, NO_SENSE);
+ scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
return;
}
@@ -225,8 +216,13 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type)
if (type == SCSI_REQ_STATUS_RETRY_READ) {
scsi_req_data(&r->req, 0);
}
- scsi_command_complete(r, CHECK_CONDITION,
- HARDWARE_ERROR);
+ if (error == ENOMEM) {
+ scsi_command_complete(r, CHECK_CONDITION,
+ SENSE_CODE(TARGET_FAILURE));
+ } else {
+ scsi_command_complete(r, CHECK_CONDITION,
+ SENSE_CODE(IO_ERROR));
+ }
bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
}
@@ -251,7 +247,7 @@ static void scsi_write_complete(void * opaque, int ret)
r->sector += n;
r->sector_count -= n;
if (r->sector_count == 0) {
- scsi_command_complete(r, GOOD, NO_SENSE);
+ scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
} else {
len = r->sector_count * 512;
if (len > SCSI_DMA_BUF_SIZE) {
@@ -278,7 +274,7 @@ static int scsi_write_data(SCSIRequest *req)
r->req.aiocb = bdrv_aio_writev(s->bs, r->sector, &r->qiov, n,
scsi_write_complete, r);
if (r->req.aiocb == NULL) {
- scsi_write_complete(r, -EIO);
+ scsi_write_complete(r, -ENOMEM);
}
} else {
/* Invoke completion routine to fetch data from host. */
@@ -316,7 +312,7 @@ static void scsi_dma_restart_bh(void *opaque)
case SCSI_REQ_STATUS_RETRY_FLUSH:
ret = scsi_disk_emulate_command(r, r->iov.iov_base);
if (ret == 0) {
- scsi_command_complete(r, GOOD, NO_SENSE);
+ scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
}
}
}
@@ -815,19 +811,8 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
case REQUEST_SENSE:
if (req->cmd.xfer < 4)
goto illegal_request;
- memset(outbuf, 0, 4);
- buflen = 4;
- if (s->sense.key == NOT_READY && req->cmd.xfer >= 18) {
- memset(outbuf, 0, 18);
- buflen = 18;
- outbuf[7] = 10;
- /* asc 0x3a, ascq 0: Medium not present */
- outbuf[12] = 0x3a;
- outbuf[13] = 0;
- }
- outbuf[0] = 0xf0;
- outbuf[1] = 0;
- outbuf[2] = s->sense.key;
+ buflen = scsi_build_sense(s->sense, outbuf, req->cmd.xfer,
+ req->cmd.xfer > 13);
scsi_disk_clear_sense(s);
break;
case INQUIRY:
@@ -965,17 +950,22 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
}
break;
default:
- goto illegal_request;
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
+ return -1;
}
- scsi_req_set_status(r, GOOD, NO_SENSE);
+ scsi_req_set_status(r, GOOD, SENSE_CODE(NO_SENSE));
return buflen;
not_ready:
- scsi_command_complete(r, CHECK_CONDITION, NOT_READY);
+ if (!bdrv_is_inserted(s->bs)) {
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(NO_MEDIUM));
+ } else {
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LUN_NOT_READY));
+ }
return -1;
illegal_request:
- scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST);
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD));
return -1;
}
@@ -1002,7 +992,8 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
if (scsi_req_parse(&r->req, buf) != 0) {
BADF("Unsupported command length, command %x\n", command);
- goto fail;
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
+ return 0;
}
#ifdef DEBUG_SCSI
{
@@ -1017,8 +1008,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
if (req->lun || buf[1] >> 5) {
/* Only LUN 0 supported. */
DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : buf[1] >> 5);
- if (command != REQUEST_SENSE && command != INQUIRY)
- goto fail;
+ if (command != REQUEST_SENSE && command != INQUIRY) {
+ scsi_command_complete(r, CHECK_CONDITION,
+ SENSE_CODE(LUN_NOT_SUPPORTED));
+ return 0;
+ }
}
switch (command) {
case TEST_UNIT_READY:
@@ -1126,15 +1120,17 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
break;
default:
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
+ return 0;
fail:
- scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST);
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD));
return 0;
illegal_lba:
- scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
+ scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LBA_OUT_OF_RANGE));
return 0;
}
if (r->sector_count == 0 && r->iov.iov_len == 0) {
- scsi_command_complete(r, GOOD, NO_SENSE);
+ scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
}
len = r->sector_count * 512 + r->iov.iov_len;
if (is_write) {
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 229d24f..64cbe8b 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -66,6 +66,19 @@ struct SCSIGenericState
uint8_t senselen;
};
+static void scsi_set_sense(SCSIGenericState *s, SCSISense sense)
+{
+ s->senselen = scsi_build_sense(sense, s->sensebuf, SCSI_SENSE_BUF_SIZE, 0);
+ s->driver_status = SG_ERR_DRIVER_SENSE;
+}
+
+static void scsi_clear_sense(SCSIGenericState *s)
+{
+ memset(s->sensebuf, 0, SCSI_SENSE_BUF_SIZE);
+ s->senselen = 0;
+ s->driver_status = 0;
+}
+
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun)
{
SCSIRequest *req;
@@ -92,9 +105,22 @@ static void scsi_command_complete(void *opaque, int ret)
if (s->driver_status & SG_ERR_DRIVER_SENSE)
s->senselen = r->io_header.sb_len_wr;
- if (ret != 0)
- r->req.status = BUSY;
- else {
+ if (ret != 0) {
+ switch (ret) {
+ case -EINVAL:
+ r->req.status = CHECK_CONDITION;
+ scsi_set_sense(s, SENSE_CODE(INVALID_FIELD));
+ break;
+ case -ENOMEM:
+ r->req.status = CHECK_CONDITION;
+ scsi_set_sense(s, SENSE_CODE(TARGET_FAILURE));
+ break;
+ default:
+ r->req.status = CHECK_CONDITION;
+ scsi_set_sense(s, SENSE_CODE(IO_ERROR));
+ break;
+ }
+ } else {
if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) {
r->req.status = BUSY;
BADF("Driver Timeout\n");
@@ -144,7 +170,7 @@ static int execute_command(BlockDriverState *bdrv,
r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r);
if (r->req.aiocb == NULL) {
BADF("execute_command: read failed !\n");
- return -1;
+ return -ENOMEM;
}
return 0;
@@ -198,12 +224,14 @@ static void scsi_read_data(SCSIRequest *req)
r->buf[0], r->buf[1], r->buf[2], r->buf[3],
r->buf[4], r->buf[5], r->buf[6], r->buf[7]);
scsi_req_data(&r->req, s->senselen);
+ /* Clear sensebuf after REQUEST_SENSE */
+ scsi_clear_sense(s);
return;
}
ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
- if (ret == -1) {
- scsi_command_complete(r, -EINVAL);
+ if (ret < 0) {
+ scsi_command_complete(r, ret);
return;
}
}
@@ -246,8 +274,8 @@ static int scsi_write_data(SCSIRequest *req)
}
ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
- if (ret == -1) {
- scsi_command_complete(r, -EINVAL);
+ if (ret < 0) {
+ scsi_command_complete(r, ret);
return 1;
}
@@ -296,16 +324,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
if (cmd[0] != REQUEST_SENSE &&
(req->lun != s->lun || (cmd[1] >> 5) != s->lun)) {
DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : cmd[1] >> 5);
-
- s->sensebuf[0] = 0x70;
- s->sensebuf[1] = 0x00;
- s->sensebuf[2] = ILLEGAL_REQUEST;
- s->sensebuf[3] = 0x00;
- s->sensebuf[4] = 0x00;
- s->sensebuf[5] = 0x00;
- s->sensebuf[6] = 0x00;
- s->senselen = 7;
- s->driver_status = SG_ERR_DRIVER_SENSE;
+ scsi_set_sense(s, SENSE_CODE(LUN_NOT_SUPPORTED));
r->req.status = CHECK_CONDITION;
scsi_req_complete(&r->req);
return 0;
@@ -313,8 +332,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
if (-1 == scsi_req_parse(&r->req, cmd)) {
BADF("Unsupported command length, command %x\n", cmd[0]);
- scsi_req_dequeue(&r->req);
- scsi_req_unref(&r->req);
+ scsi_command_complete(r, -EINVAL);
return 0;
}
scsi_req_fixup(&r->req);
@@ -338,8 +356,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
r->buflen = 0;
r->buf = NULL;
ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete);
- if (ret == -1) {
- scsi_command_complete(r, -EINVAL);
+ if (ret < 0) {
+ scsi_command_complete(r, ret);
+ return 0;
}
return 0;
}
diff --git a/hw/scsi.h b/hw/scsi.h
index e2dc7cb..7a7c9ef 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -27,6 +27,12 @@ enum SCSIXferMode {
SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */
};
+typedef struct SCSISense {
+ uint8_t key;
+ uint8_t asc;
+ uint8_t ascq;
+} SCSISense;
+
struct SCSIRequest {
SCSIBus *bus;
SCSIDevice *dev;
@@ -104,10 +110,41 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
int unit, bool removable);
int scsi_bus_legacy_handle_cmdline(SCSIBus *bus);
+/*
+ * Predefined sense codes
+ */
+
+/* No sense data available */
+extern const struct SCSISense sense_code_NO_SENSE;
+/* LUN not ready, Manual intervention required */
+extern const struct SCSISense sense_code_LUN_NOT_READY;
+/* LUN not ready, Medium not present */
+extern const struct SCSISense sense_code_NO_MEDIUM;
+/* Hardware error, internal target failure */
+extern const struct SCSISense sense_code_TARGET_FAILURE;
+/* Illegal request, invalid command operation code */
+extern const struct SCSISense sense_code_INVALID_OPCODE;
+/* Illegal request, LBA out of range */
+extern const struct SCSISense sense_code_LBA_OUT_OF_RANGE;
+/* Illegal request, Invalid field in CDB */
+extern const struct SCSISense sense_code_INVALID_FIELD;
+/* Illegal request, LUN not supported */
+extern const struct SCSISense sense_code_LUN_NOT_SUPPORTED;
+/* Command aborted, I/O process terminated */
+extern const struct SCSISense sense_code_IO_ERROR;
+/* Command aborted, I_T Nexus loss occurred */
+extern const struct SCSISense sense_code_I_T_NEXUS_LOSS;
+/* Command aborted, Logical Unit failure */
+extern const struct SCSISense sense_code_LUN_FAILURE;
+
+#define SENSE_CODE(x) sense_code_ ## x
+
+int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed);
+int scsi_sense_valid(SCSISense sense);
+
SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun);
void scsi_req_enqueue(SCSIRequest *req);
void scsi_req_free(SCSIRequest *req);
-void scsi_req_dequeue(SCSIRequest *req);
SCSIRequest *scsi_req_ref(SCSIRequest *req);
void scsi_req_unref(SCSIRequest *req);
commit 2b8b3bb9dd1c67d8296b7610dca909539e25a196
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Mon Apr 18 17:06:17 2011 +0200
scsi: use scsi_req_complete
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index c008e9c..229d24f 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -290,7 +290,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
{
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
- SCSIBus *bus;
int ret;
scsi_req_enqueue(req);
@@ -307,8 +306,8 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
s->sensebuf[6] = 0x00;
s->senselen = 7;
s->driver_status = SG_ERR_DRIVER_SENSE;
- bus = scsi_bus_from_device(&s->qdev);
- bus->ops->complete(req, SCSI_REASON_DONE, CHECK_CONDITION);
+ r->req.status = CHECK_CONDITION;
+ scsi_req_complete(&r->req);
return 0;
}
commit 94d3f98a3f3caddd7875f9a11776daeb84962a7b
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Mon Apr 18 22:53:08 2011 +0200
scsi: introduce scsi_req_cancel
This is for when the request must be dropped in the void,
but still memory should be freed. To this end, the devices
register a second callback in SCSIBusOps.
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/esp.c b/hw/esp.c
index 57061ca..f2677dc 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -188,6 +188,17 @@ static void esp_dma_enable(void *opaque, int irq, int level)
}
}
+static void esp_request_cancelled(SCSIRequest *req)
+{
+ ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
+
+ if (req == s->current_req) {
+ scsi_req_unref(s->current_req);
+ s->current_req = NULL;
+ s->current_dev = NULL;
+ }
+}
+
static uint32_t get_cmd(ESPState *s, uint8_t *buf)
{
uint32_t dmalen;
@@ -210,7 +221,7 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf)
if (s->current_dev) {
/* Started a new command before the old one finished. Cancel it. */
- s->current_dev->info->cancel_io(s->current_req);
+ scsi_req_cancel(s->current_req);
s->async_len = 0;
}
@@ -720,7 +731,8 @@ void esp_init(target_phys_addr_t espaddr, int it_shift,
}
static const struct SCSIBusOps esp_scsi_ops = {
- .complete = esp_command_complete
+ .complete = esp_command_complete,
+ .cancel = esp_request_cancelled
};
static int esp_init1(SysBusDevice *dev)
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 56234f8..bca889a 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -664,6 +664,26 @@ static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag)
return NULL;
}
+static void lsi_request_cancelled(SCSIRequest *req)
+{
+ LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
+ lsi_request *p;
+
+ if (s->current && req == s->current->req) {
+ scsi_req_unref(req);
+ qemu_free(s->current);
+ s->current = NULL;
+ return;
+ }
+
+ p = lsi_find_by_tag(s, req->tag);
+ if (p) {
+ QTAILQ_REMOVE(&s->queue, p, next);
+ scsi_req_unref(req);
+ qemu_free(p);
+ }
+}
+
/* Record that data is available for a queued command. Returns zero if
the device was reselected, nonzero if the IO is deferred. */
static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg)
@@ -931,7 +951,7 @@ static void lsi_do_msgout(LSIState *s)
/* The ABORT TAG message clears the current I/O process only. */
DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag);
if (current_req) {
- current_dev->info->cancel_io(current_req->req);
+ scsi_req_cancel(current_req->req);
}
lsi_disconnect(s);
break;
@@ -956,7 +976,7 @@ static void lsi_do_msgout(LSIState *s)
/* clear the current I/O process */
if (s->current) {
- current_dev->info->cancel_io(s->current->req);
+ scsi_req_cancel(s->current->req);
}
/* As the current implemented devices scsi_disk and scsi_generic
@@ -969,8 +989,7 @@ static void lsi_do_msgout(LSIState *s)
id = current_tag & 0x0000ff00;
QTAILQ_FOREACH_SAFE(p, &s->queue, next, p_next) {
if ((p->tag & 0x0000ff00) == id) {
- current_dev->info->cancel_io(p->req);
- QTAILQ_REMOVE(&s->queue, p, next);
+ scsi_req_cancel(p->req);
}
}
@@ -2227,7 +2246,8 @@ static int lsi_scsi_uninit(PCIDevice *d)
}
static const struct SCSIBusOps lsi_scsi_ops = {
- .complete = lsi_command_complete
+ .complete = lsi_command_complete,
+ .cancel = lsi_request_cancelled
};
static int lsi_scsi_init(PCIDevice *dev)
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index d6a055f..fd1d60f 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -549,6 +549,19 @@ void scsi_req_complete(SCSIRequest *req)
scsi_req_unref(req);
}
+void scsi_req_cancel(SCSIRequest *req)
+{
+ if (req->dev && req->dev->info->cancel_io) {
+ req->dev->info->cancel_io(req);
+ }
+ scsi_req_ref(req);
+ scsi_req_dequeue(req);
+ if (req->bus->ops->cancel) {
+ req->bus->ops->cancel(req);
+ }
+ scsi_req_unref(req);
+}
+
void scsi_req_abort(SCSIRequest *req, int status)
{
req->status = status;
@@ -564,9 +577,7 @@ void scsi_device_purge_requests(SCSIDevice *sdev)
while (!QTAILQ_EMPTY(&sdev->requests)) {
req = QTAILQ_FIRST(&sdev->requests);
- sdev->info->cancel_io(req);
- scsi_req_dequeue(req);
- scsi_req_unref(req);
+ scsi_req_cancel(req);
}
}
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 38fbb05..08633db 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -143,7 +143,6 @@ static void scsi_cancel_io(SCSIRequest *req)
bdrv_aio_cancel(r->req.aiocb);
}
r->req.aiocb = NULL;
- scsi_req_dequeue(&r->req);
}
static void scsi_read_complete(void * opaque, int ret)
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 72c4cc7..c008e9c 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -121,7 +121,6 @@ static void scsi_cancel_io(SCSIRequest *req)
bdrv_aio_cancel(r->req.aiocb);
}
r->req.aiocb = NULL;
- scsi_req_dequeue(&r->req);
}
static int execute_command(BlockDriverState *bdrv,
diff --git a/hw/scsi.h b/hw/scsi.h
index 970e812..e2dc7cb 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -78,6 +78,7 @@ struct SCSIDeviceInfo {
struct SCSIBusOps {
void (*complete)(SCSIRequest *req, int reason, uint32_t arg);
+ void (*cancel)(SCSIRequest *req);
};
struct SCSIBus {
@@ -115,6 +116,7 @@ void scsi_req_print(SCSIRequest *req);
void scsi_req_data(SCSIRequest *req, int len);
void scsi_req_complete(SCSIRequest *req);
void scsi_req_abort(SCSIRequest *req, int status);
+void scsi_req_cancel(SCSIRequest *req);
void scsi_device_purge_requests(SCSIDevice *sdev);
#endif
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 24cebd1..5aaf95b 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -559,6 +559,14 @@ static void vscsi_command_complete(SCSIRequest *sreq, int reason, uint32_t arg)
}
}
+static void vscsi_request_cancelled(SCSIRequest *sreq)
+{
+ VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
+ vscsi_req *req = vscsi_find_req(s, sreq);
+
+ vscsi_put_req(s, req);
+}
+
static void vscsi_process_login(VSCSIState *s, vscsi_req *req)
{
union viosrp_iu *iu = &req->iu;
@@ -910,7 +918,8 @@ static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data)
}
static const struct SCSIBusOps vscsi_scsi_ops = {
- .complete = vscsi_command_complete
+ .complete = vscsi_command_complete,
+ .cancel = vscsi_request_cancelled
};
static int spapr_vscsi_init(VIOsPAPRDevice *dev)
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 8e6d48b..ce92682 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -264,6 +264,18 @@ static void usb_msd_command_complete(SCSIRequest *req, int reason, uint32_t arg)
}
}
+static void usb_msd_request_cancelled(SCSIRequest *req)
+{
+ MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
+
+ if (req == s->req) {
+ scsi_req_unref(s->req);
+ s->req = NULL;
+ s->packet = NULL;
+ s->scsi_len = 0;
+ }
+}
+
static void usb_msd_handle_reset(USBDevice *dev)
{
MSDState *s = (MSDState *)dev;
@@ -318,9 +330,7 @@ static int usb_msd_handle_control(USBDevice *dev, int request, int value,
static void usb_msd_cancel_io(USBPacket *p, void *opaque)
{
MSDState *s = opaque;
- s->scsi_dev->info->cancel_io(s->req);
- s->packet = NULL;
- s->scsi_len = 0;
+ scsi_req_cancel(s->req);
}
static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
@@ -491,7 +501,8 @@ static void usb_msd_password_cb(void *opaque, int err)
}
static const struct SCSIBusOps usb_msd_scsi_ops = {
- .complete = usb_msd_command_complete
+ .complete = usb_msd_command_complete,
+ .cancel = usb_msd_request_cancelled
};
static int usb_msd_initfn(USBDevice *dev)
commit 19d110ab8af3308ce58d0936f085f0124930e7e7
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Mon Apr 18 17:14:51 2011 +0200
scsi: introduce scsi_req_abort
This covers the case of canceling a request's I/O and still
completing it.
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index c1e94fa..d6a055f 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -549,6 +549,15 @@ void scsi_req_complete(SCSIRequest *req)
scsi_req_unref(req);
}
+void scsi_req_abort(SCSIRequest *req, int status)
+{
+ req->status = status;
+ if (req->dev && req->dev->info->cancel_io) {
+ req->dev->info->cancel_io(req);
+ }
+ scsi_req_complete(req);
+}
+
void scsi_device_purge_requests(SCSIDevice *sdev)
{
SCSIRequest *req;
diff --git a/hw/scsi.h b/hw/scsi.h
index f1d8888..970e812 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -114,6 +114,7 @@ int scsi_req_parse(SCSIRequest *req, uint8_t *buf);
void scsi_req_print(SCSIRequest *req);
void scsi_req_data(SCSIRequest *req, int len);
void scsi_req_complete(SCSIRequest *req);
+void scsi_req_abort(SCSIRequest *req, int status);
void scsi_device_purge_requests(SCSIDevice *sdev);
#endif
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 36dd744..24cebd1 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -463,10 +463,8 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
dprintf("VSCSI: Queued request sense tag 0x%x\n", req->qtag);
if (n < 0) {
fprintf(stderr, "VSCSI: REQUEST_SENSE wants write data !?!?!?\n");
- sdev->info->cancel_io(req->sreq);
vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
- vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
- vscsi_put_req(s, req);
+ scsi_req_abort(req->sreq, CHECK_CONDITION);
return;
} else if (n == 0) {
return;
@@ -547,10 +545,8 @@ static void vscsi_command_complete(SCSIRequest *sreq, int reason, uint32_t arg)
}
if (rc < 0) {
fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc);
- sdev->info->cancel_io(sreq);
vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
- vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
- vscsi_put_req(s, req);
+ scsi_req_abort(req->sreq, CHECK_CONDITION);
return;
}
commit c557e889156c5f5da23b4b047aea804aefce4982
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Mon Apr 18 18:47:12 2011 +0200
scsi: commonize purging requests
The code for canceling requests upon reset is already the same. Clean
it up and move it to scsi-bus.c.
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index c7748d0..c1e94fa 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -549,6 +549,18 @@ void scsi_req_complete(SCSIRequest *req)
scsi_req_unref(req);
}
+void scsi_device_purge_requests(SCSIDevice *sdev)
+{
+ SCSIRequest *req;
+
+ while (!QTAILQ_EMPTY(&sdev->requests)) {
+ req = QTAILQ_FIRST(&sdev->requests);
+ sdev->info->cancel_io(req);
+ scsi_req_dequeue(req);
+ scsi_req_unref(req);
+ }
+}
+
static char *scsibus_get_fw_dev_path(DeviceState *dev)
{
SCSIDevice *d = (SCSIDevice*)dev;
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index f7c09c9..38fbb05 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -1147,26 +1147,12 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
return len;
}
-static void scsi_disk_purge_requests(SCSIDiskState *s)
-{
- SCSIDiskReq *r;
-
- while (!QTAILQ_EMPTY(&s->qdev.requests)) {
- r = DO_UPCAST(SCSIDiskReq, req, QTAILQ_FIRST(&s->qdev.requests));
- if (r->req.aiocb) {
- bdrv_aio_cancel(r->req.aiocb);
- }
- scsi_req_dequeue(&r->req);
- scsi_req_unref(&r->req);
- }
-}
-
static void scsi_disk_reset(DeviceState *dev)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev);
uint64_t nb_sectors;
- scsi_disk_purge_requests(s);
+ scsi_device_purge_requests(&s->qdev);
bdrv_get_geometry(s->bs, &nb_sectors);
nb_sectors /= s->cluster_size;
@@ -1180,7 +1166,7 @@ static void scsi_destroy(SCSIDevice *dev)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
- scsi_disk_purge_requests(s);
+ scsi_device_purge_requests(&s->qdev);
blockdev_mark_auto_del(s->qdev.conf.bs);
}
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 3740432..72c4cc7 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -424,32 +424,18 @@ static int get_stream_blocksize(BlockDriverState *bdrv)
return (buf[9] << 16) | (buf[10] << 8) | buf[11];
}
-static void scsi_generic_purge_requests(SCSIGenericState *s)
-{
- SCSIGenericReq *r;
-
- while (!QTAILQ_EMPTY(&s->qdev.requests)) {
- r = DO_UPCAST(SCSIGenericReq, req, QTAILQ_FIRST(&s->qdev.requests));
- if (r->req.aiocb) {
- bdrv_aio_cancel(r->req.aiocb);
- }
- scsi_req_dequeue(&r->req);
- scsi_req_unref(&r->req);
- }
-}
-
static void scsi_generic_reset(DeviceState *dev)
{
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev.qdev, dev);
- scsi_generic_purge_requests(s);
+ scsi_device_purge_requests(&s->qdev);
}
static void scsi_destroy(SCSIDevice *d)
{
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
- scsi_generic_purge_requests(s);
+ scsi_device_purge_requests(&s->qdev);
blockdev_mark_auto_del(s->qdev.conf.bs);
}
diff --git a/hw/scsi.h b/hw/scsi.h
index 19bd1ae..f1d8888 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -114,5 +114,6 @@ int scsi_req_parse(SCSIRequest *req, uint8_t *buf);
void scsi_req_print(SCSIRequest *req);
void scsi_req_data(SCSIRequest *req, int len);
void scsi_req_complete(SCSIRequest *req);
+void scsi_device_purge_requests(SCSIDevice *sdev);
#endif
commit 5c6c0e513600ba57c3e73b7151d3c0664438f7b5
Author: Hannes Reinecke <hare at suse.de>
Date: Mon Apr 18 12:35:39 2011 +0200
scsi: Use 'SCSIRequest' directly
Currently the SCSIRequest structure is abstracted away and cannot accessed
directly from the driver. This requires the handler to do a lookup on
an abstract 'tag' which identifies the SCSIRequest structure.
With this patch the SCSIRequest structure is exposed to the driver. This
allows use to use it directly as an argument to the SCSIDeviceInfo
callback functions and remove the lookup.
A new callback function 'alloc_req' is introduced matching 'free
req'; unref'ing to free up resources after use is moved into the
scsi_command_complete callbacks.
This temporarily introduces a leak of requests that are cancelled,
when they are removed from the queue and not from the driver. This
is fixed later by introducing scsi_req_cancel. That patch in turn
depends on this one, because the argument to scsi_req_cancel is a
SCSIRequest.
Signed-off-by: Hannes Reinecke <hare at suse.de>
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/esp.c b/hw/esp.c
index ae18401..57061ca 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -65,6 +65,7 @@ struct ESPState {
uint32_t dma;
SCSIBus bus;
SCSIDevice *current_dev;
+ SCSIRequest *current_req;
uint8_t cmdbuf[TI_BUFSZ];
uint32_t cmdlen;
uint32_t do_cmd;
@@ -209,7 +210,7 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf)
if (s->current_dev) {
/* Started a new command before the old one finished. Cancel it. */
- s->current_dev->info->cancel_io(s->current_dev, 0);
+ s->current_dev->info->cancel_io(s->current_req);
s->async_len = 0;
}
@@ -232,7 +233,8 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
DPRINTF("do_busid_cmd: busid 0x%x\n", busid);
lun = busid & 7;
- datalen = s->current_dev->info->send_command(s->current_dev, 0, buf, lun);
+ s->current_req = s->current_dev->info->alloc_req(s->current_dev, 0, lun);
+ datalen = s->current_dev->info->send_command(s->current_req, buf);
s->ti_size = datalen;
if (datalen != 0) {
s->rregs[ESP_RSTAT] = STAT_TC;
@@ -240,10 +242,10 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
s->dma_counter = 0;
if (datalen > 0) {
s->rregs[ESP_RSTAT] |= STAT_DI;
- s->current_dev->info->read_data(s->current_dev, 0);
+ s->current_dev->info->read_data(s->current_req);
} else {
s->rregs[ESP_RSTAT] |= STAT_DO;
- s->current_dev->info->write_data(s->current_dev, 0);
+ s->current_dev->info->write_data(s->current_req);
}
}
s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
@@ -372,9 +374,9 @@ static void esp_do_dma(ESPState *s)
if (s->async_len == 0) {
if (to_device) {
// ti_size is negative
- s->current_dev->info->write_data(s->current_dev, 0);
+ s->current_dev->info->write_data(s->current_req);
} else {
- s->current_dev->info->read_data(s->current_dev, 0);
+ s->current_dev->info->read_data(s->current_req);
/* If there is still data to be read from the device then
complete the DMA operation immediately. Otherwise defer
until the scsi layer has completed. */
@@ -388,10 +390,9 @@ static void esp_do_dma(ESPState *s)
}
}
-static void esp_command_complete(SCSIBus *bus, int reason, uint32_t tag,
- uint32_t arg)
+static void esp_command_complete(SCSIRequest *req, int reason, uint32_t arg)
{
- ESPState *s = DO_UPCAST(ESPState, busdev.qdev, bus->qbus.parent);
+ ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
if (reason == SCSI_REASON_DONE) {
DPRINTF("SCSI Command complete\n");
@@ -405,11 +406,15 @@ static void esp_command_complete(SCSIBus *bus, int reason, uint32_t tag,
s->sense = arg;
s->rregs[ESP_RSTAT] = STAT_ST;
esp_dma_done(s);
- s->current_dev = NULL;
+ if (s->current_req) {
+ scsi_req_unref(s->current_req);
+ s->current_req = NULL;
+ s->current_dev = NULL;
+ }
} else {
DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size);
s->async_len = arg;
- s->async_buf = s->current_dev->info->get_buf(s->current_dev, 0);
+ s->async_buf = s->current_dev->info->get_buf(req);
if (s->dma_left) {
esp_do_dma(s);
} else if (s->dma_counter != 0 && s->ti_size <= 0) {
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 1ebcde7..56234f8 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -174,6 +174,7 @@ do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0)
#define LSI_TAG_VALID (1 << 16)
typedef struct lsi_request {
+ SCSIRequest *req;
uint32_t tag;
uint32_t dma_len;
uint8_t *dma_buf;
@@ -567,11 +568,9 @@ static void lsi_do_dma(LSIState *s, int out)
s->csbc += count;
s->dnad += count;
s->dbc -= count;
-
- if (s->current->dma_buf == NULL) {
- s->current->dma_buf = dev->info->get_buf(dev, s->current->tag);
+ if (s->current->dma_buf == NULL) {
+ s->current->dma_buf = dev->info->get_buf(s->current->req);
}
-
/* ??? Set SFBR to first data byte. */
if (out) {
cpu_physical_memory_read(addr, s->current->dma_buf, count);
@@ -583,10 +582,10 @@ static void lsi_do_dma(LSIState *s, int out)
s->current->dma_buf = NULL;
if (out) {
/* Write the data. */
- dev->info->write_data(dev, s->current->tag);
+ dev->info->write_data(s->current->req);
} else {
/* Request any remaining data. */
- dev->info->read_data(dev, s->current->tag);
+ dev->info->read_data(s->current->req);
}
} else {
s->current->dma_buf += count;
@@ -698,12 +697,10 @@ static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg)
return 1;
}
}
-
-/* Callback to indicate that the SCSI layer has completed a transfer. */
-static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
- uint32_t arg)
+ /* Callback to indicate that the SCSI layer has completed a transfer. */
+static void lsi_command_complete(SCSIRequest *req, int reason, uint32_t arg)
{
- LSIState *s = DO_UPCAST(LSIState, dev.qdev, bus->qbus.parent);
+ LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
int out;
out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
@@ -718,21 +715,24 @@ static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
lsi_set_phase(s, PHASE_ST);
}
- qemu_free(s->current);
- s->current = NULL;
-
+ if (s->current && req == s->current->req) {
+ scsi_req_unref(s->current->req);
+ qemu_free(s->current);
+ s->current = NULL;
+ }
lsi_resume_script(s);
return;
}
- if (s->waiting == 1 || !s->current || tag != s->current->tag ||
+ if (s->waiting == 1 || !s->current || req->tag != s->current->tag ||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
- if (lsi_queue_tag(s, tag, arg))
+ if (lsi_queue_tag(s, req->tag, arg)) {
return;
+ }
}
/* host adapter (re)connected */
- DPRINTF("Data ready tag=0x%x len=%d\n", tag, arg);
+ DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, arg);
s->current->dma_len = arg;
s->command_complete = 1;
if (!s->waiting)
@@ -768,14 +768,16 @@ static void lsi_do_command(LSIState *s)
assert(s->current == NULL);
s->current = qemu_mallocz(sizeof(lsi_request));
s->current->tag = s->select_tag;
+ s->current->req = dev->info->alloc_req(dev, s->current->tag,
+ s->current_lun);
- n = dev->info->send_command(dev, s->current->tag, buf, s->current_lun);
+ n = dev->info->send_command(s->current->req, buf);
if (n > 0) {
lsi_set_phase(s, PHASE_DI);
- dev->info->read_data(dev, s->current->tag);
+ dev->info->read_data(s->current->req);
} else if (n < 0) {
lsi_set_phase(s, PHASE_DO);
- dev->info->write_data(dev, s->current->tag);
+ dev->info->write_data(s->current->req);
}
if (!s->command_complete) {
@@ -868,13 +870,15 @@ static void lsi_do_msgout(LSIState *s)
int len;
uint32_t current_tag;
SCSIDevice *current_dev;
- lsi_request *p, *p_next;
+ lsi_request *current_req, *p, *p_next;
int id;
if (s->current) {
current_tag = s->current->tag;
+ current_req = s->current;
} else {
current_tag = s->select_tag;
+ current_req = lsi_find_by_tag(s, current_tag);
}
id = (current_tag >> 8) & 0xf;
current_dev = s->bus.devs[id];
@@ -926,7 +930,9 @@ static void lsi_do_msgout(LSIState *s)
case 0x0d:
/* The ABORT TAG message clears the current I/O process only. */
DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag);
- current_dev->info->cancel_io(current_dev, current_tag);
+ if (current_req) {
+ current_dev->info->cancel_io(current_req->req);
+ }
lsi_disconnect(s);
break;
case 0x06:
@@ -949,7 +955,9 @@ static void lsi_do_msgout(LSIState *s)
}
/* clear the current I/O process */
- current_dev->info->cancel_io(current_dev, current_tag);
+ if (s->current) {
+ current_dev->info->cancel_io(s->current->req);
+ }
/* As the current implemented devices scsi_disk and scsi_generic
only support one LUN, we don't need to keep track of LUNs.
@@ -961,7 +969,7 @@ static void lsi_do_msgout(LSIState *s)
id = current_tag & 0x0000ff00;
QTAILQ_FOREACH_SAFE(p, &s->queue, next, p_next) {
if ((p->tag & 0x0000ff00) == id) {
- current_dev->info->cancel_io(current_dev, p->tag);
+ current_dev->info->cancel_io(p->req);
QTAILQ_REMOVE(&s->queue, p, next);
}
}
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index e7fd903..c7748d0 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -136,29 +136,22 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l
SCSIRequest *req;
req = qemu_mallocz(size);
- /* Two references: one is passed back to the HBA, one is in d->requests. */
- req->refcount = 2;
+ req->refcount = 1;
req->bus = scsi_bus_from_device(d);
req->dev = d;
req->tag = tag;
req->lun = lun;
req->status = -1;
- req->enqueued = true;
trace_scsi_req_alloc(req->dev->id, req->lun, req->tag);
- QTAILQ_INSERT_TAIL(&d->requests, req, next);
return req;
}
-SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag)
+void scsi_req_enqueue(SCSIRequest *req)
{
- SCSIRequest *req;
-
- QTAILQ_FOREACH(req, &d->requests, next) {
- if (req->tag == tag) {
- return req;
- }
- }
- return NULL;
+ assert(!req->enqueued);
+ scsi_req_ref(req);
+ req->enqueued = true;
+ QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
}
void scsi_req_dequeue(SCSIRequest *req)
@@ -516,7 +509,7 @@ void scsi_req_unref(SCSIRequest *req)
void scsi_req_data(SCSIRequest *req, int len)
{
trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
- req->bus->ops->complete(req->bus, SCSI_REASON_DATA, req->tag, len);
+ req->bus->ops->complete(req, SCSI_REASON_DATA, len);
}
void scsi_req_print(SCSIRequest *req)
@@ -552,9 +545,7 @@ void scsi_req_complete(SCSIRequest *req)
assert(req->status != -1);
scsi_req_ref(req);
scsi_req_dequeue(req);
- req->bus->ops->complete(req->bus, SCSI_REASON_DONE,
- req->tag,
- req->status);
+ req->bus->ops->complete(req, SCSI_REASON_DONE, req->status);
scsi_req_unref(req);
}
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 87d7b93..f7c09c9 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -86,16 +86,17 @@ struct SCSIDiskState
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type);
static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf);
-static SCSIDiskReq *scsi_new_request(SCSIDiskState *s, uint32_t tag,
+static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag,
uint32_t lun)
{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *req;
SCSIDiskReq *r;
req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun);
r = DO_UPCAST(SCSIDiskReq, req, req);
r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE);
- return r;
+ return req;
}
static void scsi_free_request(SCSIRequest *req)
@@ -105,11 +106,6 @@ static void scsi_free_request(SCSIRequest *req)
qemu_vfree(r->iov.iov_base);
}
-static SCSIDiskReq *scsi_find_request(SCSIDiskState *s, uint32_t tag)
-{
- return DO_UPCAST(SCSIDiskReq, req, scsi_req_find(&s->qdev, tag));
-}
-
static void scsi_disk_clear_sense(SCSIDiskState *s)
{
memset(&s->sense, 0, sizeof(s->sense));
@@ -138,18 +134,16 @@ static void scsi_command_complete(SCSIDiskReq *r, int status, int sense)
}
/* Cancel a pending data transfer. */
-static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
+static void scsi_cancel_io(SCSIRequest *req)
{
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
- SCSIDiskReq *r;
- DPRINTF("Cancel tag=0x%x\n", tag);
- r = scsi_find_request(s, tag);
- if (r) {
- if (r->req.aiocb)
- bdrv_aio_cancel(r->req.aiocb);
- r->req.aiocb = NULL;
- scsi_req_dequeue(&r->req);
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+ DPRINTF("Cancel tag=0x%x\n", req->tag);
+ if (r->req.aiocb) {
+ bdrv_aio_cancel(r->req.aiocb);
}
+ r->req.aiocb = NULL;
+ scsi_req_dequeue(&r->req);
}
static void scsi_read_complete(void * opaque, int ret)
@@ -174,8 +168,10 @@ static void scsi_read_complete(void * opaque, int ret)
}
-static void scsi_read_request(SCSIDiskReq *r)
+/* Read more data from scsi device into buffer. */
+static void scsi_read_data(SCSIRequest *req)
{
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint32_t n;
@@ -207,23 +203,6 @@ static void scsi_read_request(SCSIDiskReq *r)
}
}
-/* Read more data from scsi device into buffer. */
-static void scsi_read_data(SCSIDevice *d, uint32_t tag)
-{
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
- SCSIDiskReq *r;
-
- r = scsi_find_request(s, tag);
- if (!r) {
- BADF("Bad read tag 0x%x\n", tag);
- /* ??? This is the wrong error. */
- scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
- return;
- }
-
- scsi_read_request(r);
-}
-
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type)
{
int is_read = (type == SCSI_REQ_STATUS_RETRY_READ);
@@ -285,8 +264,9 @@ static void scsi_write_complete(void * opaque, int ret)
}
}
-static void scsi_write_request(SCSIDiskReq *r)
+static int scsi_write_data(SCSIRequest *req)
{
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint32_t n;
@@ -305,24 +285,6 @@ static void scsi_write_request(SCSIDiskReq *r)
/* Invoke completion routine to fetch data from host. */
scsi_write_complete(r, 0);
}
-}
-
-/* Write data to a scsi device. Returns nonzero on failure.
- The transfer may complete asynchronously. */
-static int scsi_write_data(SCSIDevice *d, uint32_t tag)
-{
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
- SCSIDiskReq *r;
-
- DPRINTF("Write data tag=0x%x\n", tag);
- r = scsi_find_request(s, tag);
- if (!r) {
- BADF("Bad write tag 0x%x\n", tag);
- scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
- return 1;
- }
-
- scsi_write_request(r);
return 0;
}
@@ -347,10 +309,10 @@ static void scsi_dma_restart_bh(void *opaque)
switch (status & SCSI_REQ_STATUS_RETRY_TYPE_MASK) {
case SCSI_REQ_STATUS_RETRY_READ:
- scsi_read_request(r);
+ scsi_read_data(&r->req);
break;
case SCSI_REQ_STATUS_RETRY_WRITE:
- scsi_write_request(r);
+ scsi_write_data(&r->req);
break;
case SCSI_REQ_STATUS_RETRY_FLUSH:
ret = scsi_disk_emulate_command(r, r->iov.iov_base);
@@ -376,16 +338,10 @@ static void scsi_dma_restart_cb(void *opaque, int running, int reason)
}
/* Return a pointer to the data buffer. */
-static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
+static uint8_t *scsi_get_buf(SCSIRequest *req)
{
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
- SCSIDiskReq *r;
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
- r = scsi_find_request(s, tag);
- if (!r) {
- BADF("Bad buffer tag 0x%x\n", tag);
- return NULL;
- }
return (uint8_t *)r->iov.iov_base;
}
@@ -1029,26 +985,18 @@ illegal_request:
(eg. disk reads), negative for transfers to the device (eg. disk writes),
and zero if the command does not transfer any data. */
-static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
- uint8_t *buf, int lun)
+static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
{
- SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
int32_t len;
int is_write;
uint8_t command;
uint8_t *outbuf;
- SCSIDiskReq *r;
int rc;
+ scsi_req_enqueue(req);
command = buf[0];
- r = scsi_find_request(s, tag);
- if (r) {
- BADF("Tag 0x%x already in use\n", tag);
- scsi_cancel_io(d, tag);
- }
- /* ??? Tags are not unique for different luns. We only implement a
- single lun, so this should not matter. */
- r = scsi_new_request(s, tag, lun);
outbuf = (uint8_t *)r->iov.iov_base;
is_write = 0;
DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
@@ -1067,9 +1015,9 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
}
#endif
- if (lun || buf[1] >> 5) {
+ if (req->lun || buf[1] >> 5) {
/* Only LUN 0 supported. */
- DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5);
+ DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : buf[1] >> 5);
if (command != REQUEST_SENSE && command != INQUIRY)
goto fail;
}
@@ -1095,7 +1043,6 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
case REZERO_UNIT:
rc = scsi_disk_emulate_command(r, outbuf);
if (rc < 0) {
- scsi_req_unref(&r->req);
return 0;
}
@@ -1105,7 +1052,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
case READ_10:
case READ_12:
case READ_16:
- len = r->req.cmd.xfer / d->blocksize;
+ len = r->req.cmd.xfer / s->qdev.blocksize;
DPRINTF("Read (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len);
if (r->req.cmd.lba > s->max_lba)
goto illegal_lba;
@@ -1119,7 +1066,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
case WRITE_VERIFY:
case WRITE_VERIFY_12:
case WRITE_VERIFY_16:
- len = r->req.cmd.xfer / d->blocksize;
+ len = r->req.cmd.xfer / s->qdev.blocksize;
DPRINTF("Write %s(sector %" PRId64 ", count %d)\n",
(command & 0xe) == 0xe ? "And Verify " : "",
r->req.cmd.lba, len);
@@ -1154,7 +1101,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
}
break;
case WRITE_SAME_16:
- len = r->req.cmd.xfer / d->blocksize;
+ len = r->req.cmd.xfer / s->qdev.blocksize;
DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n",
r->req.cmd.lba, len);
@@ -1182,11 +1129,9 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
fail:
scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST);
- scsi_req_unref(&r->req);
return 0;
illegal_lba:
scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
- scsi_req_unref(&r->req);
return 0;
}
if (r->sector_count == 0 && r->iov.iov_len == 0) {
@@ -1199,7 +1144,6 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
if (!r->sector_count)
r->sector_count = -1;
}
- scsi_req_unref(&r->req);
return len;
}
@@ -1213,6 +1157,7 @@ static void scsi_disk_purge_requests(SCSIDiskState *s)
bdrv_aio_cancel(r->req.aiocb);
}
scsi_req_dequeue(&r->req);
+ scsi_req_unref(&r->req);
}
}
@@ -1325,6 +1270,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.qdev.reset = scsi_disk_reset,
.init = scsi_hd_initfn,
.destroy = scsi_destroy,
+ .alloc_req = scsi_new_request,
.free_req = scsi_free_request,
.send_command = scsi_send_command,
.read_data = scsi_read_data,
@@ -1344,6 +1290,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.qdev.reset = scsi_disk_reset,
.init = scsi_cd_initfn,
.destroy = scsi_destroy,
+ .alloc_req = scsi_new_request,
.free_req = scsi_free_request,
.send_command = scsi_send_command,
.read_data = scsi_read_data,
@@ -1362,6 +1309,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.qdev.reset = scsi_disk_reset,
.init = scsi_disk_initfn,
.destroy = scsi_destroy,
+ .alloc_req = scsi_new_request,
.free_req = scsi_free_request,
.send_command = scsi_send_command,
.read_data = scsi_read_data,
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 06e9dfe..3740432 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -66,12 +66,12 @@ struct SCSIGenericState
uint8_t senselen;
};
-static SCSIGenericReq *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun)
+static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun)
{
SCSIRequest *req;
req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun);
- return DO_UPCAST(SCSIGenericReq, req, req);
+ return req;
}
static void scsi_free_request(SCSIRequest *req)
@@ -81,11 +81,6 @@ static void scsi_free_request(SCSIRequest *req)
qemu_free(r->buf);
}
-static SCSIGenericReq *scsi_find_request(SCSIGenericState *s, uint32_t tag)
-{
- return DO_UPCAST(SCSIGenericReq, req, scsi_req_find(&s->qdev, tag));
-}
-
/* Helper function for command completion. */
static void scsi_command_complete(void *opaque, int ret)
{
@@ -117,19 +112,16 @@ static void scsi_command_complete(void *opaque, int ret)
}
/* Cancel a pending data transfer. */
-static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
+static void scsi_cancel_io(SCSIRequest *req)
{
- DPRINTF("scsi_cancel_io 0x%x\n", tag);
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
- SCSIGenericReq *r;
- DPRINTF("Cancel tag=0x%x\n", tag);
- r = scsi_find_request(s, tag);
- if (r) {
- if (r->req.aiocb)
- bdrv_aio_cancel(r->req.aiocb);
- r->req.aiocb = NULL;
- scsi_req_dequeue(&r->req);
+ SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+
+ DPRINTF("Cancel tag=0x%x\n", req->tag);
+ if (r->req.aiocb) {
+ bdrv_aio_cancel(r->req.aiocb);
}
+ r->req.aiocb = NULL;
+ scsi_req_dequeue(&r->req);
}
static int execute_command(BlockDriverState *bdrv,
@@ -182,21 +174,13 @@ static void scsi_read_complete(void * opaque, int ret)
}
/* Read more data from scsi device into buffer. */
-static void scsi_read_data(SCSIDevice *d, uint32_t tag)
+static void scsi_read_data(SCSIRequest *req)
{
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
- SCSIGenericReq *r;
+ SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
int ret;
- DPRINTF("scsi_read_data 0x%x\n", tag);
- r = scsi_find_request(s, tag);
- if (!r) {
- BADF("Bad read tag 0x%x\n", tag);
- /* ??? This is the wrong error. */
- scsi_command_complete(r, -EINVAL);
- return;
- }
-
+ DPRINTF("scsi_read_data 0x%x\n", req->tag);
if (r->len == -1) {
scsi_command_complete(r, 0);
return;
@@ -249,21 +233,13 @@ static void scsi_write_complete(void * opaque, int ret)
/* Write data to a scsi device. Returns nonzero on failure.
The transfer may complete asynchronously. */
-static int scsi_write_data(SCSIDevice *d, uint32_t tag)
+static int scsi_write_data(SCSIRequest *req)
{
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
- SCSIGenericReq *r;
+ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
+ SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
int ret;
- DPRINTF("scsi_write_data 0x%x\n", tag);
- r = scsi_find_request(s, tag);
- if (!r) {
- BADF("Bad write tag 0x%x\n", tag);
- /* ??? This is the wrong error. */
- scsi_command_complete(r, -EINVAL);
- return 0;
- }
-
+ DPRINTF("scsi_write_data 0x%x\n", req->tag);
if (r->len == 0) {
r->len = r->buflen;
scsi_req_data(&r->req, r->len);
@@ -280,15 +256,10 @@ static int scsi_write_data(SCSIDevice *d, uint32_t tag)
}
/* Return a pointer to the data buffer. */
-static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
+static uint8_t *scsi_get_buf(SCSIRequest *req)
{
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
- SCSIGenericReq *r;
- r = scsi_find_request(s, tag);
- if (!r) {
- BADF("Bad buffer tag 0x%x\n", tag);
- return NULL;
- }
+ SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+
return r->buf;
}
@@ -316,18 +287,17 @@ static void scsi_req_fixup(SCSIRequest *req)
(eg. disk reads), negative for transfers to the device (eg. disk writes),
and zero if the command does not transfer any data. */
-static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
- uint8_t *cmd, int lun)
+static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
{
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
- SCSIGenericReq *r;
+ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
+ SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
SCSIBus *bus;
int ret;
- int32_t len;
+ scsi_req_enqueue(req);
if (cmd[0] != REQUEST_SENSE &&
- (lun != s->lun || (cmd[1] >> 5) != s->lun)) {
- DPRINTF("Unimplemented LUN %d\n", lun ? lun : cmd[1] >> 5);
+ (req->lun != s->lun || (cmd[1] >> 5) != s->lun)) {
+ DPRINTF("Unimplemented LUN %d\n", req->lun ? req->lun : cmd[1] >> 5);
s->sensebuf[0] = 0x70;
s->sensebuf[1] = 0x00;
@@ -338,18 +308,11 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
s->sensebuf[6] = 0x00;
s->senselen = 7;
s->driver_status = SG_ERR_DRIVER_SENSE;
- bus = scsi_bus_from_device(d);
- bus->ops->complete(bus, SCSI_REASON_DONE, tag, CHECK_CONDITION);
+ bus = scsi_bus_from_device(&s->qdev);
+ bus->ops->complete(req, SCSI_REASON_DONE, CHECK_CONDITION);
return 0;
}
- r = scsi_find_request(s, tag);
- if (r) {
- BADF("Tag 0x%x already in use %p\n", tag, r);
- scsi_cancel_io(d, tag);
- }
- r = scsi_new_request(d, tag, lun);
-
if (-1 == scsi_req_parse(&r->req, cmd)) {
BADF("Unsupported command length, command %x\n", cmd[0]);
scsi_req_dequeue(&r->req);
@@ -379,10 +342,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete);
if (ret == -1) {
scsi_command_complete(r, -EINVAL);
- scsi_req_unref(&r->req);
- return 0;
}
- scsi_req_unref(&r->req);
return 0;
}
@@ -397,13 +357,10 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
r->len = r->req.cmd.xfer;
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
r->len = 0;
- len = -r->req.cmd.xfer;
+ return -r->req.cmd.xfer;
} else {
- len = r->req.cmd.xfer;
+ return r->req.cmd.xfer;
}
-
- scsi_req_unref(&r->req);
- return len;
}
static int get_blocksize(BlockDriverState *bdrv)
@@ -477,6 +434,7 @@ static void scsi_generic_purge_requests(SCSIGenericState *s)
bdrv_aio_cancel(r->req.aiocb);
}
scsi_req_dequeue(&r->req);
+ scsi_req_unref(&r->req);
}
}
@@ -568,6 +526,7 @@ static SCSIDeviceInfo scsi_generic_info = {
.qdev.reset = scsi_generic_reset,
.init = scsi_generic_initfn,
.destroy = scsi_destroy,
+ .alloc_req = scsi_new_request,
.free_req = scsi_free_request,
.send_command = scsi_send_command,
.read_data = scsi_read_data,
diff --git a/hw/scsi.h b/hw/scsi.h
index a1d0e74..19bd1ae 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -19,6 +19,7 @@ typedef struct SCSIBus SCSIBus;
typedef struct SCSIBusOps SCSIBusOps;
typedef struct SCSIDevice SCSIDevice;
typedef struct SCSIDeviceInfo SCSIDeviceInfo;
+typedef struct SCSIRequest SCSIRequest;
enum SCSIXferMode {
SCSI_XFER_NONE, /* TEST_UNIT_READY, ... */
@@ -26,7 +27,7 @@ enum SCSIXferMode {
SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */
};
-typedef struct SCSIRequest {
+struct SCSIRequest {
SCSIBus *bus;
SCSIDevice *dev;
uint32_t refcount;
@@ -43,7 +44,7 @@ typedef struct SCSIRequest {
BlockDriverAIOCB *aiocb;
bool enqueued;
QTAILQ_ENTRY(SCSIRequest) next;
-} SCSIRequest;
+};
struct SCSIDevice
{
@@ -66,17 +67,17 @@ struct SCSIDeviceInfo {
DeviceInfo qdev;
scsi_qdev_initfn init;
void (*destroy)(SCSIDevice *s);
+ SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun);
void (*free_req)(SCSIRequest *req);
- int32_t (*send_command)(SCSIDevice *s, uint32_t tag, uint8_t *buf,
- int lun);
- void (*read_data)(SCSIDevice *s, uint32_t tag);
- int (*write_data)(SCSIDevice *s, uint32_t tag);
- void (*cancel_io)(SCSIDevice *s, uint32_t tag);
- uint8_t *(*get_buf)(SCSIDevice *s, uint32_t tag);
+ int32_t (*send_command)(SCSIRequest *req, uint8_t *buf);
+ void (*read_data)(SCSIRequest *req);
+ int (*write_data)(SCSIRequest *req);
+ void (*cancel_io)(SCSIRequest *req);
+ uint8_t *(*get_buf)(SCSIRequest *req);
};
struct SCSIBusOps {
- void (*complete)(SCSIBus *bus, int reason, uint32_t tag, uint32_t arg);
+ void (*complete)(SCSIRequest *req, int reason, uint32_t arg);
};
struct SCSIBus {
@@ -103,7 +104,7 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
int scsi_bus_legacy_handle_cmdline(SCSIBus *bus);
SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun);
-SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag);
+void scsi_req_enqueue(SCSIRequest *req);
void scsi_req_free(SCSIRequest *req);
void scsi_req_dequeue(SCSIRequest *req);
SCSIRequest *scsi_req_ref(SCSIRequest *req);
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index c183008..36dd744 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -75,6 +75,7 @@ typedef struct vscsi_req {
/* SCSI request tracking */
SCSIDevice *sdev;
+ SCSIRequest *sreq;
uint32_t qtag; /* qemu tag != srp tag */
int lun;
int active;
@@ -123,11 +124,16 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s)
static void vscsi_put_req(VSCSIState *s, vscsi_req *req)
{
+ if (req->sreq != NULL) {
+ scsi_req_unref(req->sreq);
+ }
+ req->sreq = NULL;
req->active = 0;
}
-static vscsi_req *vscsi_find_req(VSCSIState *s, uint32_t tag)
+static vscsi_req *vscsi_find_req(VSCSIState *s, SCSIRequest *req)
{
+ uint32_t tag = req->tag;
if (tag >= VSCSI_REQ_LIMIT || !s->reqs[tag].active) {
return NULL;
}
@@ -453,11 +459,11 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
cdb[4] = 96;
cdb[5] = 0;
req->sensing = 1;
- n = sdev->info->send_command(sdev, req->qtag, cdb, req->lun);
+ n = sdev->info->send_command(req->sreq, cdb);
dprintf("VSCSI: Queued request sense tag 0x%x\n", req->qtag);
if (n < 0) {
fprintf(stderr, "VSCSI: REQUEST_SENSE wants write data !?!?!?\n");
- sdev->info->cancel_io(sdev, req->qtag);
+ sdev->info->cancel_io(req->sreq);
vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
vscsi_put_req(s, req);
@@ -465,24 +471,23 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
} else if (n == 0) {
return;
}
- sdev->info->read_data(sdev, req->qtag);
+ sdev->info->read_data(req->sreq);
}
/* Callback to indicate that the SCSI layer has completed a transfer. */
-static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
- uint32_t arg)
+static void vscsi_command_complete(SCSIRequest *sreq, int reason, uint32_t arg)
{
- VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, bus->qbus.parent);
- vscsi_req *req = vscsi_find_req(s, tag);
+ VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
+ vscsi_req *req = vscsi_find_req(s, sreq);
SCSIDevice *sdev;
uint8_t *buf;
int32_t res_in = 0, res_out = 0;
int len, rc = 0;
dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x arg=0x%x, req=%p\n",
- reason, tag, arg, req);
+ reason, sreq->tag, arg, req);
if (req == NULL) {
- fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", tag);
+ fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag);
return;
}
sdev = req->sdev;
@@ -493,7 +498,7 @@ static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
vscsi_put_req(s, req);
} else {
- uint8_t *buf = sdev->info->get_buf(sdev, tag);
+ uint8_t *buf = sdev->info->get_buf(sreq);
len = MIN(arg, SCSI_SENSE_BUF_SIZE);
dprintf("VSCSI: Sense data, %d bytes:\n", len);
@@ -505,7 +510,7 @@ static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
buf[12], buf[13], buf[14], buf[15]);
memcpy(req->sense, buf, len);
req->senselen = len;
- sdev->info->read_data(sdev, req->qtag);
+ sdev->info->read_data(sreq);
}
return;
}
@@ -537,12 +542,12 @@ static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
* to write for writes (ie, how much is to be DMA'd)
*/
if (arg) {
- buf = sdev->info->get_buf(sdev, tag);
+ buf = sdev->info->get_buf(sreq);
rc = vscsi_srp_transfer_data(s, req, req->writing, buf, arg);
}
if (rc < 0) {
fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc);
- sdev->info->cancel_io(sdev, req->qtag);
+ sdev->info->cancel_io(sreq);
vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
vscsi_put_req(s, req);
@@ -552,9 +557,9 @@ static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
/* Start next chunk */
req->data_len -= rc;
if (req->writing) {
- sdev->info->write_data(sdev, req->qtag);
+ sdev->info->write_data(sreq);
} else {
- sdev->info->read_data(sdev, req->qtag);
+ sdev->info->read_data(sreq);
}
}
@@ -644,7 +649,8 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
req->sdev = sdev;
req->lun = lun;
- n = sdev->info->send_command(sdev, req->qtag, srp->cmd.cdb, lun);
+ req->sreq = sdev->info->alloc_req(sdev, req->qtag, lun);
+ n = sdev->info->send_command(req->sreq, srp->cmd.cdb);
dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n",
req->qtag, srp->cmd.cdb[0], id, lun, n);
@@ -662,10 +668,10 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
/* Get transfer direction and initiate transfer */
if (n > 0) {
req->data_len = n;
- sdev->info->read_data(sdev, req->qtag);
+ sdev->info->read_data(req->sreq);
} else if (n < 0) {
req->data_len = -n;
- sdev->info->write_data(sdev, req->qtag);
+ sdev->info->write_data(req->sreq);
}
/* Don't touch req here, it may have been recycled already */
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index c0a381a..8e6d48b 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -48,6 +48,7 @@ typedef struct {
uint32_t data_len;
uint32_t residue;
uint32_t tag;
+ SCSIRequest *req;
SCSIBus bus;
BlockConf conf;
SCSIDevice *scsi_dev;
@@ -190,9 +191,9 @@ static void usb_msd_copy_data(MSDState *s)
s->data_len -= len;
if (s->scsi_len == 0 || s->data_len == 0) {
if (s->mode == USB_MSDM_DATAIN) {
- s->scsi_dev->info->read_data(s->scsi_dev, s->tag);
+ s->scsi_dev->info->read_data(s->req);
} else if (s->mode == USB_MSDM_DATAOUT) {
- s->scsi_dev->info->write_data(s->scsi_dev, s->tag);
+ s->scsi_dev->info->write_data(s->req);
}
}
}
@@ -211,14 +212,13 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p)
memcpy(p->data, &csw, len);
}
-static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
- uint32_t arg)
+static void usb_msd_command_complete(SCSIRequest *req, int reason, uint32_t arg)
{
- MSDState *s = DO_UPCAST(MSDState, dev.qdev, bus->qbus.parent);
+ MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
USBPacket *p = s->packet;
- if (tag != s->tag) {
- fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", tag);
+ if (req->tag != s->tag) {
+ fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag);
}
if (reason == SCSI_REASON_DONE) {
DPRINTF("Command complete %d\n", arg);
@@ -245,10 +245,12 @@ static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
} else if (s->data_len == 0) {
s->mode = USB_MSDM_CSW;
}
+ scsi_req_unref(req);
+ s->req = NULL;
return;
}
s->scsi_len = arg;
- s->scsi_buf = s->scsi_dev->info->get_buf(s->scsi_dev, tag);
+ s->scsi_buf = s->scsi_dev->info->get_buf(req);
if (p) {
usb_msd_copy_data(s);
if (s->usb_len == 0) {
@@ -316,7 +318,7 @@ static int usb_msd_handle_control(USBDevice *dev, int request, int value,
static void usb_msd_cancel_io(USBPacket *p, void *opaque)
{
MSDState *s = opaque;
- s->scsi_dev->info->cancel_io(s->scsi_dev, s->tag);
+ s->scsi_dev->info->cancel_io(s->req);
s->packet = NULL;
s->scsi_len = 0;
}
@@ -365,14 +367,15 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
s->tag, cbw.flags, cbw.cmd_len, s->data_len);
s->residue = 0;
s->scsi_len = 0;
- s->scsi_dev->info->send_command(s->scsi_dev, s->tag, cbw.cmd, 0);
+ s->req = s->scsi_dev->info->alloc_req(s->scsi_dev, s->tag, 0);
+ s->scsi_dev->info->send_command(s->req, cbw.cmd);
/* ??? Should check that USB and SCSI data transfer
directions match. */
if (s->residue == 0) {
if (s->mode == USB_MSDM_DATAIN) {
- s->scsi_dev->info->read_data(s->scsi_dev, s->tag);
+ s->scsi_dev->info->read_data(s->req);
} else if (s->mode == USB_MSDM_DATAOUT) {
- s->scsi_dev->info->write_data(s->scsi_dev, s->tag);
+ s->scsi_dev->info->write_data(s->req);
}
}
ret = len;
commit 11257187e122f1b33e4983b881a2b6009f5993ca
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Mon Apr 18 12:01:45 2011 +0200
lsi: extract lsi_find_by_tag
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 704e8ad..1ebcde7 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -652,38 +652,51 @@ static void lsi_reselect(LSIState *s, lsi_request *p)
}
}
-/* Record that data is available for a queued command. Returns zero if
- the device was reselected, nonzero if the IO is deferred. */
-static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg)
+static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag)
{
lsi_request *p;
QTAILQ_FOREACH(p, &s->queue, next) {
if (p->tag == tag) {
- if (p->pending) {
- BADF("Multiple IO pending for tag %d\n", tag);
- }
- p->pending = arg;
- /* Reselect if waiting for it, or if reselection triggers an IRQ
- and the bus is free.
- Since no interrupt stacking is implemented in the emulation, it
- is also required that there are no pending interrupts waiting
- for service from the device driver. */
- if (s->waiting == 1 ||
- (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) &&
- !(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) {
- /* Reselect device. */
- lsi_reselect(s, p);
- return 0;
- } else {
- DPRINTF("Queueing IO tag=0x%x\n", tag);
- p->pending = arg;
- return 1;
- }
+ return p;
}
}
- BADF("IO with unknown tag %d\n", tag);
- return 1;
+
+ return NULL;
+}
+
+/* Record that data is available for a queued command. Returns zero if
+ the device was reselected, nonzero if the IO is deferred. */
+static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg)
+{
+ lsi_request *p;
+
+ p = lsi_find_by_tag(s, tag);
+ if (!p) {
+ BADF("IO with unknown tag %d\n", tag);
+ return 1;
+ }
+
+ if (p->pending) {
+ BADF("Multiple IO pending for tag %d\n", tag);
+ }
+ p->pending = arg;
+ /* Reselect if waiting for it, or if reselection triggers an IRQ
+ and the bus is free.
+ Since no interrupt stacking is implemented in the emulation, it
+ is also required that there are no pending interrupts waiting
+ for service from the device driver. */
+ if (s->waiting == 1 ||
+ (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) &&
+ !(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) {
+ /* Reselect device. */
+ lsi_reselect(s, p);
+ return 0;
+ } else {
+ DPRINTF("Queueing IO tag=0x%x\n", tag);
+ p->pending = arg;
+ return 1;
+ }
}
/* Callback to indicate that the SCSI layer has completed a transfer. */
commit ad2d30f79d3b0812f02c741be2189796b788d6d7
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Mon Apr 18 16:01:56 2011 +0200
scsi: reference-count requests
With the next patch, a device may hold SCSIRequest for an indefinite
time. Split a rather big patch, and protect against access errors,
by reference counting them.
There is some ugliness in scsi_send_command implementation due to
the need to unref the request when it fails. This will go away
with the next patches, which move the unref'ing to the devices.
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Cc: Christoph Hellwig <hch at lst.de>
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 1850a87..e7fd903 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -136,6 +136,8 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l
SCSIRequest *req;
req = qemu_mallocz(size);
+ /* Two references: one is passed back to the HBA, one is in d->requests. */
+ req->refcount = 2;
req->bus = scsi_bus_from_device(d);
req->dev = d;
req->tag = tag;
@@ -159,21 +161,16 @@ SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag)
return NULL;
}
-static void scsi_req_dequeue(SCSIRequest *req)
+void scsi_req_dequeue(SCSIRequest *req)
{
trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag);
if (req->enqueued) {
QTAILQ_REMOVE(&req->dev->requests, req, next);
req->enqueued = false;
+ scsi_req_unref(req);
}
}
-void scsi_req_free(SCSIRequest *req)
-{
- scsi_req_dequeue(req);
- qemu_free(req);
-}
-
static int scsi_req_length(SCSIRequest *req, uint8_t *cmd)
{
switch (cmd[0] >> 5) {
@@ -495,6 +492,22 @@ static const char *scsi_command_name(uint8_t cmd)
return names[cmd];
}
+SCSIRequest *scsi_req_ref(SCSIRequest *req)
+{
+ req->refcount++;
+ return req;
+}
+
+void scsi_req_unref(SCSIRequest *req)
+{
+ if (--req->refcount == 0) {
+ if (req->dev->info->free_req) {
+ req->dev->info->free_req(req);
+ }
+ qemu_free(req);
+ }
+}
+
/* Called by the devices when data is ready for the HBA. The HBA should
start a DMA operation to read or fill the device's data buffer.
Once it completes, calling one of req->dev->info->read_data or
@@ -537,10 +550,12 @@ void scsi_req_print(SCSIRequest *req)
void scsi_req_complete(SCSIRequest *req)
{
assert(req->status != -1);
+ scsi_req_ref(req);
scsi_req_dequeue(req);
req->bus->ops->complete(req->bus, SCSI_REASON_DONE,
req->tag,
req->status);
+ scsi_req_unref(req);
}
static char *scsibus_get_fw_dev_path(DeviceState *dev)
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 741cf39..87d7b93 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -98,10 +98,11 @@ static SCSIDiskReq *scsi_new_request(SCSIDiskState *s, uint32_t tag,
return r;
}
-static void scsi_remove_request(SCSIDiskReq *r)
+static void scsi_free_request(SCSIRequest *req)
{
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
qemu_vfree(r->iov.iov_base);
- scsi_req_free(&r->req);
}
static SCSIDiskReq *scsi_find_request(SCSIDiskState *s, uint32_t tag)
@@ -134,7 +135,6 @@ static void scsi_command_complete(SCSIDiskReq *r, int status, int sense)
r->req.tag, status, sense);
scsi_req_set_status(r, status, sense);
scsi_req_complete(&r->req);
- scsi_remove_request(r);
}
/* Cancel a pending data transfer. */
@@ -148,7 +148,7 @@ static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
if (r->req.aiocb)
bdrv_aio_cancel(r->req.aiocb);
r->req.aiocb = NULL;
- scsi_remove_request(r);
+ scsi_req_dequeue(&r->req);
}
}
@@ -1033,7 +1033,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
uint8_t *buf, int lun)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
- uint32_t len;
+ int32_t len;
int is_write;
uint8_t command;
uint8_t *outbuf;
@@ -1095,6 +1095,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
case REZERO_UNIT:
rc = scsi_disk_emulate_command(r, outbuf);
if (rc < 0) {
+ scsi_req_unref(&r->req);
return 0;
}
@@ -1181,9 +1182,11 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
fail:
scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST);
+ scsi_req_unref(&r->req);
return 0;
illegal_lba:
scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
+ scsi_req_unref(&r->req);
return 0;
}
if (r->sector_count == 0 && r->iov.iov_len == 0) {
@@ -1191,12 +1194,13 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
}
len = r->sector_count * 512 + r->iov.iov_len;
if (is_write) {
- return -len;
+ len = -len;
} else {
if (!r->sector_count)
r->sector_count = -1;
- return len;
}
+ scsi_req_unref(&r->req);
+ return len;
}
static void scsi_disk_purge_requests(SCSIDiskState *s)
@@ -1208,7 +1212,7 @@ static void scsi_disk_purge_requests(SCSIDiskState *s)
if (r->req.aiocb) {
bdrv_aio_cancel(r->req.aiocb);
}
- scsi_remove_request(r);
+ scsi_req_dequeue(&r->req);
}
}
@@ -1321,6 +1325,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.qdev.reset = scsi_disk_reset,
.init = scsi_hd_initfn,
.destroy = scsi_destroy,
+ .free_req = scsi_free_request,
.send_command = scsi_send_command,
.read_data = scsi_read_data,
.write_data = scsi_write_data,
@@ -1339,6 +1344,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.qdev.reset = scsi_disk_reset,
.init = scsi_cd_initfn,
.destroy = scsi_destroy,
+ .free_req = scsi_free_request,
.send_command = scsi_send_command,
.read_data = scsi_read_data,
.write_data = scsi_write_data,
@@ -1356,6 +1362,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.qdev.reset = scsi_disk_reset,
.init = scsi_disk_initfn,
.destroy = scsi_destroy,
+ .free_req = scsi_free_request,
.send_command = scsi_send_command,
.read_data = scsi_read_data,
.write_data = scsi_write_data,
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index bd09983..06e9dfe 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -74,10 +74,11 @@ static SCSIGenericReq *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lu
return DO_UPCAST(SCSIGenericReq, req, req);
}
-static void scsi_remove_request(SCSIGenericReq *r)
+static void scsi_free_request(SCSIRequest *req)
{
+ SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+
qemu_free(r->buf);
- scsi_req_free(&r->req);
}
static SCSIGenericReq *scsi_find_request(SCSIGenericState *s, uint32_t tag)
@@ -113,7 +114,6 @@ static void scsi_command_complete(void *opaque, int ret)
r, r->req.tag, r->req.status);
scsi_req_complete(&r->req);
- scsi_remove_request(r);
}
/* Cancel a pending data transfer. */
@@ -128,7 +128,7 @@ static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
if (r->req.aiocb)
bdrv_aio_cancel(r->req.aiocb);
r->req.aiocb = NULL;
- scsi_remove_request(r);
+ scsi_req_dequeue(&r->req);
}
}
@@ -323,6 +323,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
SCSIGenericReq *r;
SCSIBus *bus;
int ret;
+ int32_t len;
if (cmd[0] != REQUEST_SENSE &&
(lun != s->lun || (cmd[1] >> 5) != s->lun)) {
@@ -351,7 +352,8 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
if (-1 == scsi_req_parse(&r->req, cmd)) {
BADF("Unsupported command length, command %x\n", cmd[0]);
- scsi_remove_request(r);
+ scsi_req_dequeue(&r->req);
+ scsi_req_unref(&r->req);
return 0;
}
scsi_req_fixup(&r->req);
@@ -377,8 +379,10 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete);
if (ret == -1) {
scsi_command_complete(r, -EINVAL);
+ scsi_req_unref(&r->req);
return 0;
}
+ scsi_req_unref(&r->req);
return 0;
}
@@ -393,10 +397,13 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
r->len = r->req.cmd.xfer;
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
r->len = 0;
- return -r->req.cmd.xfer;
+ len = -r->req.cmd.xfer;
+ } else {
+ len = r->req.cmd.xfer;
}
- return r->req.cmd.xfer;
+ scsi_req_unref(&r->req);
+ return len;
}
static int get_blocksize(BlockDriverState *bdrv)
@@ -469,7 +476,7 @@ static void scsi_generic_purge_requests(SCSIGenericState *s)
if (r->req.aiocb) {
bdrv_aio_cancel(r->req.aiocb);
}
- scsi_remove_request(r);
+ scsi_req_dequeue(&r->req);
}
}
@@ -561,6 +568,7 @@ static SCSIDeviceInfo scsi_generic_info = {
.qdev.reset = scsi_generic_reset,
.init = scsi_generic_initfn,
.destroy = scsi_destroy,
+ .free_req = scsi_free_request,
.send_command = scsi_send_command,
.read_data = scsi_read_data,
.write_data = scsi_write_data,
diff --git a/hw/scsi.h b/hw/scsi.h
index d4ecc9b..a1d0e74 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -29,6 +29,7 @@ enum SCSIXferMode {
typedef struct SCSIRequest {
SCSIBus *bus;
SCSIDevice *dev;
+ uint32_t refcount;
uint32_t tag;
uint32_t lun;
uint32_t status;
@@ -65,6 +66,7 @@ struct SCSIDeviceInfo {
DeviceInfo qdev;
scsi_qdev_initfn init;
void (*destroy)(SCSIDevice *s);
+ void (*free_req)(SCSIRequest *req);
int32_t (*send_command)(SCSIDevice *s, uint32_t tag, uint8_t *buf,
int lun);
void (*read_data)(SCSIDevice *s, uint32_t tag);
@@ -103,6 +105,9 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus);
SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun);
SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag);
void scsi_req_free(SCSIRequest *req);
+void scsi_req_dequeue(SCSIRequest *req);
+SCSIRequest *scsi_req_ref(SCSIRequest *req);
+void scsi_req_unref(SCSIRequest *req);
int scsi_req_parse(SCSIRequest *req, uint8_t *buf);
void scsi_req_print(SCSIRequest *req);
commit d33e0ce213cec82a059f5e37667231200eb77325
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Wed May 25 16:53:46 2011 +0200
scsi-generic: do not use a stale aiocb
If a request is canceled after it has been completed, scsi_cancel_io
would pass a stale aiocb to bdrv_aio_cancel. Avoid this.
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Cc: Christoph Hellwig <hch at lst.de>
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index f09458b..bd09983 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -91,6 +91,7 @@ static void scsi_command_complete(void *opaque, int ret)
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
+ r->req.aiocb = NULL;
s->driver_status = r->io_header.driver_status;
if (s->driver_status & SG_ERR_DRIVER_SENSE)
s->senselen = r->io_header.sb_len_wr;
@@ -163,6 +164,7 @@ static void scsi_read_complete(void * opaque, int ret)
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
int len;
+ r->req.aiocb = NULL;
if (ret) {
DPRINTF("IO error ret %d\n", ret);
scsi_command_complete(r, ret);
@@ -229,6 +231,7 @@ static void scsi_write_complete(void * opaque, int ret)
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
DPRINTF("scsi_write_complete() ret = %d\n", ret);
+ r->req.aiocb = NULL;
if (ret) {
DPRINTF("IO error\n");
scsi_command_complete(r, ret);
commit cfdc1bb06ee4cd3a7e4aa0ebf14b00c0ce3a5e94
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Mon Apr 18 17:11:14 2011 +0200
scsi: introduce SCSIBusOps
There are more operations than a SCSI bus can handle, besides completing
commands. One example, which this series will introduce, is cleaning up
after a request is cancelled.
More long term, a "SCSI bus" can represent the LUNs attached to a
target; in this case, while all commands will ultimately reach a logical
unit, it is the target who is in charge of answering REPORT LUNs.
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Cc: Christoph Hellwig <hch at lst.de>
diff --git a/hw/esp.c b/hw/esp.c
index fa9d2a2..ae18401 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -714,6 +714,10 @@ void esp_init(target_phys_addr_t espaddr, int it_shift,
*dma_enable = qdev_get_gpio_in(dev, 1);
}
+static const struct SCSIBusOps esp_scsi_ops = {
+ .complete = esp_command_complete
+};
+
static int esp_init1(SysBusDevice *dev)
{
ESPState *s = FROM_SYSBUS(ESPState, dev);
@@ -728,7 +732,7 @@ static int esp_init1(SysBusDevice *dev)
qdev_init_gpio_in(&dev->qdev, esp_gpio_demux, 2);
- scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, esp_command_complete);
+ scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, &esp_scsi_ops);
return scsi_bus_legacy_handle_cmdline(&s->bus);
}
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 2ce38a9..704e8ad 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -2205,6 +2205,10 @@ static int lsi_scsi_uninit(PCIDevice *d)
return 0;
}
+static const struct SCSIBusOps lsi_scsi_ops = {
+ .complete = lsi_command_complete
+};
+
static int lsi_scsi_init(PCIDevice *dev)
{
LSIState *s = DO_UPCAST(LSIState, dev, dev);
@@ -2241,7 +2245,7 @@ static int lsi_scsi_init(PCIDevice *dev)
PCI_BASE_ADDRESS_SPACE_MEMORY, lsi_ram_mapfunc);
QTAILQ_INIT(&s->queue);
- scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, lsi_command_complete);
+ scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, &lsi_scsi_ops);
if (!dev->qdev.hotplugged) {
return scsi_bus_legacy_handle_cmdline(&s->bus);
}
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 191cbab..1850a87 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -21,13 +21,13 @@ static int next_scsi_bus;
/* Create a scsi bus, and attach devices to it. */
void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
- scsi_completionfn complete)
+ const SCSIBusOps *ops)
{
qbus_create_inplace(&bus->qbus, &scsi_bus_info, host, NULL);
bus->busnr = next_scsi_bus++;
bus->tcq = tcq;
bus->ndev = ndev;
- bus->complete = complete;
+ bus->ops = ops;
bus->qbus.allow_hotplug = 1;
}
@@ -503,7 +503,7 @@ static const char *scsi_command_name(uint8_t cmd)
void scsi_req_data(SCSIRequest *req, int len)
{
trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
- req->bus->complete(req->bus, SCSI_REASON_DATA, req->tag, len);
+ req->bus->ops->complete(req->bus, SCSI_REASON_DATA, req->tag, len);
}
void scsi_req_print(SCSIRequest *req)
@@ -538,9 +538,9 @@ void scsi_req_complete(SCSIRequest *req)
{
assert(req->status != -1);
scsi_req_dequeue(req);
- req->bus->complete(req->bus, SCSI_REASON_DONE,
- req->tag,
- req->status);
+ req->bus->ops->complete(req->bus, SCSI_REASON_DONE,
+ req->tag,
+ req->status);
}
static char *scsibus_get_fw_dev_path(DeviceState *dev)
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index e4f1f30..f09458b 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -335,7 +335,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
s->senselen = 7;
s->driver_status = SG_ERR_DRIVER_SENSE;
bus = scsi_bus_from_device(d);
- bus->complete(bus, SCSI_REASON_DONE, tag, CHECK_CONDITION);
+ bus->ops->complete(bus, SCSI_REASON_DONE, tag, CHECK_CONDITION);
return 0;
}
diff --git a/hw/scsi.h b/hw/scsi.h
index 7c09f32..d4ecc9b 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -16,10 +16,9 @@ enum scsi_reason {
};
typedef struct SCSIBus SCSIBus;
+typedef struct SCSIBusOps SCSIBusOps;
typedef struct SCSIDevice SCSIDevice;
typedef struct SCSIDeviceInfo SCSIDeviceInfo;
-typedef void (*scsi_completionfn)(SCSIBus *bus, int reason, uint32_t tag,
- uint32_t arg);
enum SCSIXferMode {
SCSI_XFER_NONE, /* TEST_UNIT_READY, ... */
@@ -74,20 +73,22 @@ struct SCSIDeviceInfo {
uint8_t *(*get_buf)(SCSIDevice *s, uint32_t tag);
};
-typedef void (*SCSIAttachFn)(DeviceState *host, BlockDriverState *bdrv,
- int unit);
+struct SCSIBusOps {
+ void (*complete)(SCSIBus *bus, int reason, uint32_t tag, uint32_t arg);
+};
+
struct SCSIBus {
BusState qbus;
int busnr;
int tcq, ndev;
- scsi_completionfn complete;
+ const SCSIBusOps *ops;
SCSIDevice *devs[MAX_SCSI_DEVS];
};
void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
- scsi_completionfn complete);
+ const SCSIBusOps *ops);
void scsi_qdev_register(SCSIDeviceInfo *info);
static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d)
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index 9928334..c183008 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -907,6 +907,10 @@ static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data)
return 0;
}
+static const struct SCSIBusOps vscsi_scsi_ops = {
+ .complete = vscsi_command_complete
+};
+
static int spapr_vscsi_init(VIOsPAPRDevice *dev)
{
VSCSIState *s = DO_UPCAST(VSCSIState, vdev, dev);
@@ -923,7 +927,7 @@ static int spapr_vscsi_init(VIOsPAPRDevice *dev)
dev->crq.SendFunc = vscsi_do_crq;
scsi_bus_new(&s->bus, &dev->qdev, 1, VSCSI_REQ_LIMIT,
- vscsi_command_complete);
+ &vscsi_scsi_ops);
if (!dev->qdev.hotplugged) {
scsi_bus_legacy_handle_cmdline(&s->bus);
}
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index bd1c3a4..c0a381a 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -487,6 +487,10 @@ static void usb_msd_password_cb(void *opaque, int err)
qdev_unplug(&s->dev.qdev);
}
+static const struct SCSIBusOps usb_msd_scsi_ops = {
+ .complete = usb_msd_command_complete
+};
+
static int usb_msd_initfn(USBDevice *dev)
{
MSDState *s = DO_UPCAST(MSDState, dev, dev);
@@ -516,7 +520,7 @@ static int usb_msd_initfn(USBDevice *dev)
}
usb_desc_init(dev);
- scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete);
+ scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, &usb_msd_scsi_ops);
s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable);
if (!s->scsi_dev) {
return -1;
commit ab9adc88c80186cfef29bda076363e20aa675241
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Mon Apr 18 14:59:13 2011 +0200
scsi: introduce scsi_req_data
This abstracts calling the command_complete callback, reducing churn
in the following patches.
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 0fd85fc..191cbab 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -495,6 +495,17 @@ static const char *scsi_command_name(uint8_t cmd)
return names[cmd];
}
+/* Called by the devices when data is ready for the HBA. The HBA should
+ start a DMA operation to read or fill the device's data buffer.
+ Once it completes, calling one of req->dev->info->read_data or
+ req->dev->info->write_data (depending on the direction of the
+ transfer) will restart I/O. */
+void scsi_req_data(SCSIRequest *req, int len)
+{
+ trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
+ req->bus->complete(req->bus, SCSI_REASON_DATA, req->tag, len);
+}
+
void scsi_req_print(SCSIRequest *req)
{
FILE *fp = stderr;
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 397b9d6..741cf39 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -170,7 +170,7 @@ static void scsi_read_complete(void * opaque, int ret)
n = r->iov.iov_len / 512;
r->sector += n;
r->sector_count -= n;
- r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len);
+ scsi_req_data(&r->req, r->iov.iov_len);
}
@@ -182,7 +182,7 @@ static void scsi_read_request(SCSIDiskReq *r)
if (r->sector_count == (uint32_t)-1) {
DPRINTF("Read buf_len=%zd\n", r->iov.iov_len);
r->sector_count = 0;
- r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len);
+ scsi_req_data(&r->req, r->iov.iov_len);
return;
}
DPRINTF("Read sector_count=%d\n", r->sector_count);
@@ -245,7 +245,7 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type)
vm_stop(VMSTOP_DISKFULL);
} else {
if (type == SCSI_REQ_STATUS_RETRY_READ) {
- r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, 0);
+ scsi_req_data(&r->req, 0);
}
scsi_command_complete(r, CHECK_CONDITION,
HARDWARE_ERROR);
@@ -281,7 +281,7 @@ static void scsi_write_complete(void * opaque, int ret)
}
r->iov.iov_len = len;
DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, len);
- r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len);
+ scsi_req_data(&r->req, len);
}
}
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 102f1da..e4f1f30 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -175,7 +175,7 @@ static void scsi_read_complete(void * opaque, int ret)
if (len == 0) {
scsi_command_complete(r, 0);
} else {
- r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len);
+ scsi_req_data(&r->req, len);
}
}
@@ -212,7 +212,7 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag)
DPRINTF("Sense: %d %d %d %d %d %d %d %d\n",
r->buf[0], r->buf[1], r->buf[2], r->buf[3],
r->buf[4], r->buf[5], r->buf[6], r->buf[7]);
- r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, s->senselen);
+ scsi_req_data(&r->req, s->senselen);
return;
}
@@ -263,7 +263,7 @@ static int scsi_write_data(SCSIDevice *d, uint32_t tag)
if (r->len == 0) {
r->len = r->buflen;
- r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->len);
+ scsi_req_data(&r->req, r->len);
return 0;
}
diff --git a/hw/scsi.h b/hw/scsi.h
index d3b5d56..7c09f32 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -105,6 +105,7 @@ void scsi_req_free(SCSIRequest *req);
int scsi_req_parse(SCSIRequest *req, uint8_t *buf);
void scsi_req_print(SCSIRequest *req);
+void scsi_req_data(SCSIRequest *req, int len);
void scsi_req_complete(SCSIRequest *req);
#endif
diff --git a/trace-events b/trace-events
index b11b71d..0340eb2 100644
--- a/trace-events
+++ b/trace-events
@@ -207,6 +207,7 @@ disable usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature
# hw/scsi-bus.c
disable scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d"
+disable scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d"
disable scsi_req_dequeue(int target, int lun, int tag) "target %d lun %d tag %d"
disable scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer, uint64_t lba) "target %d lun %d tag %d command %d dir %d length %d lba %"PRIu64""
disable scsi_req_parse_bad(int target, int lun, int tag, int cmd) "target %d lun %d tag %d command %d"
commit 40f16dd1279e7f26357b3c4b3838a89ffc6153da
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Tue May 3 14:15:59 2011 +0200
scsi-generic: Remove bogus double complete
scsi-generic scsi_read_complete() should not -both- call the client
complete callback with SCSI_REASON_DATA -and- call
scsi_command_complete(). The former will cause the client to queue a
new read or write request, while the later will free the request data
structure, thus causing the new read or write request to use a
freed/stale structure when it completes.
This patch fixes the bug, fixing a crash with scsi-generic & RHEL5.5
installer.
Cc: Benjamin Herrenschmidt <benh at kernel.crashing.org>
Cc: David Gibson <david at gibson.dropbear.id.au>
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 9be1cca..102f1da 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -172,9 +172,11 @@ static void scsi_read_complete(void * opaque, int ret)
DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
r->len = -1;
- r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len);
- if (len == 0)
+ if (len == 0) {
scsi_command_complete(r, 0);
+ } else {
+ r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len);
+ }
}
/* Read more data from scsi device into buffer. */
commit 5138efecf23471abcf7dedce1956918f4ba312e3
Author: Paolo Bonzini <pbonzini at redhat.com>
Date: Fri Apr 15 11:51:13 2011 +0200
scsi: add tracing of scsi requests
Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
Reviewed-by: Christoph Hellwig <hch at lst.de>
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index ceeb4ec..0fd85fc 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -4,6 +4,7 @@
#include "scsi-defs.h"
#include "qdev.h"
#include "blockdev.h"
+#include "trace.h"
static char *scsibus_get_fw_dev_path(DeviceState *dev);
@@ -141,6 +142,7 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l
req->lun = lun;
req->status = -1;
req->enqueued = true;
+ trace_scsi_req_alloc(req->dev->id, req->lun, req->tag);
QTAILQ_INSERT_TAIL(&d->requests, req, next);
return req;
}
@@ -159,6 +161,7 @@ SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag)
static void scsi_req_dequeue(SCSIRequest *req)
{
+ trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag);
if (req->enqueued) {
QTAILQ_REMOVE(&req->dev->requests, req, next);
req->enqueued = false;
@@ -195,6 +198,7 @@ static int scsi_req_length(SCSIRequest *req, uint8_t *cmd)
req->cmd.len = 12;
break;
default:
+ trace_scsi_req_parse_bad(req->dev->id, req->lun, req->tag, cmd[0]);
return -1;
}
@@ -392,6 +396,8 @@ int scsi_req_parse(SCSIRequest *req, uint8_t *buf)
memcpy(req->cmd.buf, buf, req->cmd.len);
scsi_req_xfer_mode(req);
req->cmd.lba = scsi_req_lba(req);
+ trace_scsi_req_parsed(req->dev->id, req->lun, req->tag, buf[0],
+ req->cmd.mode, req->cmd.xfer, req->cmd.lba);
return 0;
}
diff --git a/trace-events b/trace-events
index 385cb00..b11b71d 100644
--- a/trace-events
+++ b/trace-events
@@ -205,6 +205,12 @@ disable usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d
disable usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
disable usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
+# hw/scsi-bus.c
+disable scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d"
+disable scsi_req_dequeue(int target, int lun, int tag) "target %d lun %d tag %d"
+disable scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer, uint64_t lba) "target %d lun %d tag %d command %d dir %d length %d lba %"PRIu64""
+disable scsi_req_parse_bad(int target, int lun, int tag, int cmd) "target %d lun %d tag %d command %d"
+
# vl.c
disable vm_state_notify(int running, int reason) "running %d reason %d"
commit 94527ead7e9f4c6bc193754000a61b15939e6c1e
Author: Gerd Hoffmann <kraxel at redhat.com>
Date: Fri Dec 3 16:17:28 2010 +0100
usb: add ehci adapter
This patch finally merges the EHCI host adapter aka USB 2.0 support.
Based on the ehci bits collected @ git://git.kiszka.org/qemu.git ehci
EHCI has a long out-of-tree history. Project was started by Mark
Burkley, with contributions by Niels de Vos. David S. Ahern continued
working on it. Kevin Wolf, Jan Kiszka and Vincent Palatin contributed
bugfixes.
/me (Gerd Hoffmann) picked it up where it left off, prepared the code
for merge, fixed a few bugs and added basic user docs.
Cc: David S. Ahern <daahern at cisco.com>
Cc: Jan Kiszka <jan.kiszka at web.de>
Cc: Kevin Wolf <mail at kevin-wolf.de>
Cc: Vincent Palatin <vincent.palatin_qemu at m4x.org>
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/Makefile.objs b/Makefile.objs
index 4478c61..90838f6 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -193,6 +193,7 @@ hw-obj-$(CONFIG_PCSPK) += pcspk.o
hw-obj-$(CONFIG_PCKBD) += pckbd.o
hw-obj-$(CONFIG_USB_UHCI) += usb-uhci.o
hw-obj-$(CONFIG_USB_OHCI) += usb-ohci.o
+hw-obj-$(CONFIG_USB_EHCI) += usb-ehci.o
hw-obj-$(CONFIG_FDC) += fdc.o
hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
diff --git a/default-configs/pci.mak b/default-configs/pci.mak
index 0471efb..22bd350 100644
--- a/default-configs/pci.mak
+++ b/default-configs/pci.mak
@@ -3,6 +3,7 @@ CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO=y
CONFIG_USB_UHCI=y
CONFIG_USB_OHCI=y
+CONFIG_USB_EHCI=y
CONFIG_NE2000_PCI=y
CONFIG_EEPRO100_PCI=y
CONFIG_PCNET_PCI=y
diff --git a/docs/usb2.txt b/docs/usb2.txt
new file mode 100644
index 0000000..b283c13
--- /dev/null
+++ b/docs/usb2.txt
@@ -0,0 +1,38 @@
+
+USB 2.0 Quick Start
+===================
+
+The QEMU EHCI Adapter does *not* support companion controllers. That
+implies there are two completely separate USB busses: One USB 1.1 bus
+driven by the UHCI controller and one USB 2.0 bus driven by the EHCI
+controller. Devices must be attached to the correct controller
+manually.
+
+The '-usb' switch will make qemu create the UHCI controller as part of
+the PIIX3 chipset. The USB 1.1 bus will carry the name "usb.0".
+
+You can use the standard -device switch to add a EHCI controller to
+your virtual machine. It is strongly recommended to specify an ID for
+the controller so the USB 2.0 bus gets a individual name, for example
+'-device usb-ehci,id=ehci". This will give you a USB 2.0 bus named
+"ehci.0".
+
+I strongly recomment to also use -device to attach usb devices because
+you can specify the bus they should be attached to this way. Here is
+a complete example:
+
+ qemu -M pc ${otheroptions} \
+ -drive if=none,id=usbstick,file=/path/to/image \
+ -usb \
+ -device usb-ehci,id=ehci \
+ -device usb-tablet,bus=usb.0 \
+ -device usb-storage,bus=ehci.0,drive=usbstick
+
+This attaches a usb tablet to the UHCI adapter and a usb mass storage
+device to the EHCI adapter.
+
+enjoy,
+ Gerd
+
+--
+Gerd Hoffmann <kraxel at redhat.com>
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index ea3418c..d9457ed 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -100,6 +100,7 @@
#define PCI_VENDOR_ID_INTEL 0x8086
#define PCI_DEVICE_ID_INTEL_82441 0x1237
#define PCI_DEVICE_ID_INTEL_82801AA_5 0x2415
+#define PCI_DEVICE_ID_INTEL_82801D 0x24CD
#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab
#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000
#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010
diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
new file mode 100644
index 0000000..f63519e
--- /dev/null
+++ b/hw/usb-ehci.c
@@ -0,0 +1,2037 @@
+/*
+ * QEMU USB EHCI Emulation
+ *
+ * Copyright(c) 2008 Emutex Ltd. (address at hidden)
+ *
+ * EHCI project was started by Mark Burkley, with contributions by
+ * Niels de Vos. David S. Ahern continued working on it. Kevin Wolf,
+ * Jan Kiszka and Vincent Palatin contributed bugfixes.
+ *
+ *
+ * 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 General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * TODO:
+ * o Downstream port handoff
+ */
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "usb.h"
+#include "pci.h"
+#include "monitor.h"
+
+#define EHCI_DEBUG 0
+#define STATE_DEBUG 0 /* state transitions */
+
+#if EHCI_DEBUG || STATE_DEBUG
+#define DPRINTF printf
+#else
+#define DPRINTF(...)
+#endif
+
+#if STATE_DEBUG
+#define DPRINTF_ST DPRINTF
+#else
+#define DPRINTF_ST(...)
+#endif
+
+/* internal processing - reset HC to try and recover */
+#define USB_RET_PROCERR (-99)
+
+#define MMIO_SIZE 0x1000
+
+/* Capability Registers Base Address - section 2.2 */
+#define CAPREGBASE 0x0000
+#define CAPLENGTH CAPREGBASE + 0x0000 // 1-byte, 0x0001 reserved
+#define HCIVERSION CAPREGBASE + 0x0002 // 2-bytes, i/f version #
+#define HCSPARAMS CAPREGBASE + 0x0004 // 4-bytes, structural params
+#define HCCPARAMS CAPREGBASE + 0x0008 // 4-bytes, capability params
+#define EECP HCCPARAMS + 1
+#define HCSPPORTROUTE1 CAPREGBASE + 0x000c
+#define HCSPPORTROUTE2 CAPREGBASE + 0x0010
+
+#define OPREGBASE 0x0020 // Operational Registers Base Address
+
+#define USBCMD OPREGBASE + 0x0000
+#define USBCMD_RUNSTOP (1 << 0) // run / Stop
+#define USBCMD_HCRESET (1 << 1) // HC Reset
+#define USBCMD_FLS (3 << 2) // Frame List Size
+#define USBCMD_FLS_SH 2 // Frame List Size Shift
+#define USBCMD_PSE (1 << 4) // Periodic Schedule Enable
+#define USBCMD_ASE (1 << 5) // Asynch Schedule Enable
+#define USBCMD_IAAD (1 << 6) // Int Asynch Advance Doorbell
+#define USBCMD_LHCR (1 << 7) // Light Host Controller Reset
+#define USBCMD_ASPMC (3 << 8) // Async Sched Park Mode Count
+#define USBCMD_ASPME (1 << 11) // Async Sched Park Mode Enable
+#define USBCMD_ITC (0x7f << 16) // Int Threshold Control
+#define USBCMD_ITC_SH 16 // Int Threshold Control Shift
+
+#define USBSTS OPREGBASE + 0x0004
+#define USBSTS_RO_MASK 0x0000003f
+#define USBSTS_INT (1 << 0) // USB Interrupt
+#define USBSTS_ERRINT (1 << 1) // Error Interrupt
+#define USBSTS_PCD (1 << 2) // Port Change Detect
+#define USBSTS_FLR (1 << 3) // Frame List Rollover
+#define USBSTS_HSE (1 << 4) // Host System Error
+#define USBSTS_IAA (1 << 5) // Interrupt on Async Advance
+#define USBSTS_HALT (1 << 12) // HC Halted
+#define USBSTS_REC (1 << 13) // Reclamation
+#define USBSTS_PSS (1 << 14) // Periodic Schedule Status
+#define USBSTS_ASS (1 << 15) // Asynchronous Schedule Status
+
+/*
+ * Interrupt enable bits correspond to the interrupt active bits in USBSTS
+ * so no need to redefine here.
+ */
+#define USBINTR OPREGBASE + 0x0008
+#define USBINTR_MASK 0x0000003f
+
+#define FRINDEX OPREGBASE + 0x000c
+#define CTRLDSSEGMENT OPREGBASE + 0x0010
+#define PERIODICLISTBASE OPREGBASE + 0x0014
+#define ASYNCLISTADDR OPREGBASE + 0x0018
+#define ASYNCLISTADDR_MASK 0xffffffe0
+
+#define CONFIGFLAG OPREGBASE + 0x0040
+
+#define PORTSC (OPREGBASE + 0x0044)
+#define PORTSC_BEGIN PORTSC
+#define PORTSC_END (PORTSC + 4 * NB_PORTS)
+/*
+ * Bits that are reserverd or are read-only are masked out of values
+ * written to us by software
+ */
+#define PORTSC_RO_MASK 0x007021c5
+#define PORTSC_RWC_MASK 0x0000002a
+#define PORTSC_WKOC_E (1 << 22) // Wake on Over Current Enable
+#define PORTSC_WKDS_E (1 << 21) // Wake on Disconnect Enable
+#define PORTSC_WKCN_E (1 << 20) // Wake on Connect Enable
+#define PORTSC_PTC (15 << 16) // Port Test Control
+#define PORTSC_PTC_SH 16 // Port Test Control shift
+#define PORTSC_PIC (3 << 14) // Port Indicator Control
+#define PORTSC_PIC_SH 14 // Port Indicator Control Shift
+#define PORTSC_POWNER (1 << 13) // Port Owner
+#define PORTSC_PPOWER (1 << 12) // Port Power
+#define PORTSC_LINESTAT (3 << 10) // Port Line Status
+#define PORTSC_LINESTAT_SH 10 // Port Line Status Shift
+#define PORTSC_PRESET (1 << 8) // Port Reset
+#define PORTSC_SUSPEND (1 << 7) // Port Suspend
+#define PORTSC_FPRES (1 << 6) // Force Port Resume
+#define PORTSC_OCC (1 << 5) // Over Current Change
+#define PORTSC_OCA (1 << 4) // Over Current Active
+#define PORTSC_PEDC (1 << 3) // Port Enable/Disable Change
+#define PORTSC_PED (1 << 2) // Port Enable/Disable
+#define PORTSC_CSC (1 << 1) // Connect Status Change
+#define PORTSC_CONNECT (1 << 0) // Current Connect Status
+
+#define FRAME_TIMER_FREQ 1000
+#define FRAME_TIMER_USEC (1000000 / FRAME_TIMER_FREQ)
+
+#define NB_MAXINTRATE 8 // Max rate at which controller issues ints
+#define NB_PORTS 4 // Number of downstream ports
+#define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction
+#define MAX_ITERATIONS 20 // Max number of QH before we break the loop
+#define MAX_QH 100 // Max allowable queue heads in a chain
+
+/* Internal periodic / asynchronous schedule state machine states
+ */
+typedef enum {
+ EST_INACTIVE = 1000,
+ EST_ACTIVE,
+ EST_EXECUTING,
+ EST_SLEEPING,
+ /* The following states are internal to the state machine function
+ */
+ EST_WAITLISTHEAD,
+ EST_FETCHENTRY,
+ EST_FETCHQH,
+ EST_FETCHITD,
+ EST_ADVANCEQUEUE,
+ EST_FETCHQTD,
+ EST_EXECUTE,
+ EST_WRITEBACK,
+ EST_HORIZONTALQH
+} EHCI_STATES;
+
+/* macros for accessing fields within next link pointer entry */
+#define NLPTR_GET(x) ((x) & 0xffffffe0)
+#define NLPTR_TYPE_GET(x) (((x) >> 1) & 3)
+#define NLPTR_TBIT(x) ((x) & 1) // 1=invalid, 0=valid
+
+/* link pointer types */
+#define NLPTR_TYPE_ITD 0 // isoc xfer descriptor
+#define NLPTR_TYPE_QH 1 // queue head
+#define NLPTR_TYPE_STITD 2 // split xaction, isoc xfer descriptor
+#define NLPTR_TYPE_FSTN 3 // frame span traversal node
+
+
+/* EHCI spec version 1.0 Section 3.3
+ */
+typedef struct EHCIitd {
+ uint32_t next;
+
+ uint32_t transact[8];
+#define ITD_XACT_ACTIVE (1 << 31)
+#define ITD_XACT_DBERROR (1 << 30)
+#define ITD_XACT_BABBLE (1 << 29)
+#define ITD_XACT_XACTERR (1 << 28)
+#define ITD_XACT_LENGTH_MASK 0x0fff0000
+#define ITD_XACT_LENGTH_SH 16
+#define ITD_XACT_IOC (1 << 15)
+#define ITD_XACT_PGSEL_MASK 0x00007000
+#define ITD_XACT_PGSEL_SH 12
+#define ITD_XACT_OFFSET_MASK 0x00000fff
+
+ uint32_t bufptr[7];
+#define ITD_BUFPTR_MASK 0xfffff000
+#define ITD_BUFPTR_SH 12
+#define ITD_BUFPTR_EP_MASK 0x00000f00
+#define ITD_BUFPTR_EP_SH 8
+#define ITD_BUFPTR_DEVADDR_MASK 0x0000007f
+#define ITD_BUFPTR_DEVADDR_SH 0
+#define ITD_BUFPTR_DIRECTION (1 << 11)
+#define ITD_BUFPTR_MAXPKT_MASK 0x000007ff
+#define ITD_BUFPTR_MAXPKT_SH 0
+#define ITD_BUFPTR_MULT_MASK 0x00000003
+} EHCIitd;
+
+/* EHCI spec version 1.0 Section 3.4
+ */
+typedef struct EHCIsitd {
+ uint32_t next; // Standard next link pointer
+ uint32_t epchar;
+#define SITD_EPCHAR_IO (1 << 31)
+#define SITD_EPCHAR_PORTNUM_MASK 0x7f000000
+#define SITD_EPCHAR_PORTNUM_SH 24
+#define SITD_EPCHAR_HUBADD_MASK 0x007f0000
+#define SITD_EPCHAR_HUBADDR_SH 16
+#define SITD_EPCHAR_EPNUM_MASK 0x00000f00
+#define SITD_EPCHAR_EPNUM_SH 8
+#define SITD_EPCHAR_DEVADDR_MASK 0x0000007f
+
+ uint32_t uframe;
+#define SITD_UFRAME_CMASK_MASK 0x0000ff00
+#define SITD_UFRAME_CMASK_SH 8
+#define SITD_UFRAME_SMASK_MASK 0x000000ff
+
+ uint32_t results;
+#define SITD_RESULTS_IOC (1 << 31)
+#define SITD_RESULTS_PGSEL (1 << 30)
+#define SITD_RESULTS_TBYTES_MASK 0x03ff0000
+#define SITD_RESULTS_TYBYTES_SH 16
+#define SITD_RESULTS_CPROGMASK_MASK 0x0000ff00
+#define SITD_RESULTS_CPROGMASK_SH 8
+#define SITD_RESULTS_ACTIVE (1 << 7)
+#define SITD_RESULTS_ERR (1 << 6)
+#define SITD_RESULTS_DBERR (1 << 5)
+#define SITD_RESULTS_BABBLE (1 << 4)
+#define SITD_RESULTS_XACTERR (1 << 3)
+#define SITD_RESULTS_MISSEDUF (1 << 2)
+#define SITD_RESULTS_SPLITXSTATE (1 << 1)
+
+ uint32_t bufptr[2];
+#define SITD_BUFPTR_MASK 0xfffff000
+#define SITD_BUFPTR_CURROFF_MASK 0x00000fff
+#define SITD_BUFPTR_TPOS_MASK 0x00000018
+#define SITD_BUFPTR_TPOS_SH 3
+#define SITD_BUFPTR_TCNT_MASK 0x00000007
+
+ uint32_t backptr; // Standard next link pointer
+} EHCIsitd;
+
+/* EHCI spec version 1.0 Section 3.5
+ */
+typedef struct EHCIqtd {
+ uint32_t next; // Standard next link pointer
+ uint32_t altnext; // Standard next link pointer
+ uint32_t token;
+#define QTD_TOKEN_DTOGGLE (1 << 31)
+#define QTD_TOKEN_TBYTES_MASK 0x7fff0000
+#define QTD_TOKEN_TBYTES_SH 16
+#define QTD_TOKEN_IOC (1 << 15)
+#define QTD_TOKEN_CPAGE_MASK 0x00007000
+#define QTD_TOKEN_CPAGE_SH 12
+#define QTD_TOKEN_CERR_MASK 0x00000c00
+#define QTD_TOKEN_CERR_SH 10
+#define QTD_TOKEN_PID_MASK 0x00000300
+#define QTD_TOKEN_PID_SH 8
+#define QTD_TOKEN_ACTIVE (1 << 7)
+#define QTD_TOKEN_HALT (1 << 6)
+#define QTD_TOKEN_DBERR (1 << 5)
+#define QTD_TOKEN_BABBLE (1 << 4)
+#define QTD_TOKEN_XACTERR (1 << 3)
+#define QTD_TOKEN_MISSEDUF (1 << 2)
+#define QTD_TOKEN_SPLITXSTATE (1 << 1)
+#define QTD_TOKEN_PING (1 << 0)
+
+ uint32_t bufptr[5]; // Standard buffer pointer
+#define QTD_BUFPTR_MASK 0xfffff000
+} EHCIqtd;
+
+/* EHCI spec version 1.0 Section 3.6
+ */
+typedef struct EHCIqh {
+ uint32_t next; // Standard next link pointer
+
+ /* endpoint characteristics */
+ uint32_t epchar;
+#define QH_EPCHAR_RL_MASK 0xf0000000
+#define QH_EPCHAR_RL_SH 28
+#define QH_EPCHAR_C (1 << 27)
+#define QH_EPCHAR_MPLEN_MASK 0x07FF0000
+#define QH_EPCHAR_MPLEN_SH 16
+#define QH_EPCHAR_H (1 << 15)
+#define QH_EPCHAR_DTC (1 << 14)
+#define QH_EPCHAR_EPS_MASK 0x00003000
+#define QH_EPCHAR_EPS_SH 12
+#define EHCI_QH_EPS_FULL 0
+#define EHCI_QH_EPS_LOW 1
+#define EHCI_QH_EPS_HIGH 2
+#define EHCI_QH_EPS_RESERVED 3
+
+#define QH_EPCHAR_EP_MASK 0x00000f00
+#define QH_EPCHAR_EP_SH 8
+#define QH_EPCHAR_I (1 << 7)
+#define QH_EPCHAR_DEVADDR_MASK 0x0000007f
+#define QH_EPCHAR_DEVADDR_SH 0
+
+ /* endpoint capabilities */
+ uint32_t epcap;
+#define QH_EPCAP_MULT_MASK 0xc0000000
+#define QH_EPCAP_MULT_SH 30
+#define QH_EPCAP_PORTNUM_MASK 0x3f800000
+#define QH_EPCAP_PORTNUM_SH 23
+#define QH_EPCAP_HUBADDR_MASK 0x007f0000
+#define QH_EPCAP_HUBADDR_SH 16
+#define QH_EPCAP_CMASK_MASK 0x0000ff00
+#define QH_EPCAP_CMASK_SH 8
+#define QH_EPCAP_SMASK_MASK 0x000000ff
+#define QH_EPCAP_SMASK_SH 0
+
+ uint32_t current_qtd; // Standard next link pointer
+ uint32_t next_qtd; // Standard next link pointer
+ uint32_t altnext_qtd;
+#define QH_ALTNEXT_NAKCNT_MASK 0x0000001e
+#define QH_ALTNEXT_NAKCNT_SH 1
+
+ uint32_t token; // Same as QTD token
+ uint32_t bufptr[5]; // Standard buffer pointer
+#define BUFPTR_CPROGMASK_MASK 0x000000ff
+#define BUFPTR_FRAMETAG_MASK 0x0000001f
+#define BUFPTR_SBYTES_MASK 0x00000fe0
+#define BUFPTR_SBYTES_SH 5
+} EHCIqh;
+
+/* EHCI spec version 1.0 Section 3.7
+ */
+typedef struct EHCIfstn {
+ uint32_t next; // Standard next link pointer
+ uint32_t backptr; // Standard next link pointer
+} EHCIfstn;
+
+typedef struct {
+ PCIDevice dev;
+ qemu_irq irq;
+ target_phys_addr_t mem_base;
+ int mem;
+ int num_ports;
+ /*
+ * EHCI spec version 1.0 Section 2.3
+ * Host Controller Operational Registers
+ */
+ union {
+ uint8_t mmio[MMIO_SIZE];
+ struct {
+ uint8_t cap[OPREGBASE];
+ uint32_t usbcmd;
+ uint32_t usbsts;
+ uint32_t usbintr;
+ uint32_t frindex;
+ uint32_t ctrldssegment;
+ uint32_t periodiclistbase;
+ uint32_t asynclistaddr;
+ uint32_t notused[9];
+ uint32_t configflag;
+ uint32_t portsc[NB_PORTS];
+ };
+ };
+ /*
+ * Internal states, shadow registers, etc
+ */
+ uint32_t sofv;
+ QEMUTimer *frame_timer;
+ int attach_poll_counter;
+ int astate; // Current state in asynchronous schedule
+ int pstate; // Current state in periodic schedule
+ USBPort ports[NB_PORTS];
+ uint8_t buffer[BUFF_SIZE];
+ uint32_t usbsts_pending;
+
+ /* cached data from guest - needs to be flushed
+ * when guest removes an entry (doorbell, handshake sequence)
+ */
+ EHCIqh qh; // copy of current QH (being worked on)
+ uint32_t qhaddr; // address QH read from
+
+ EHCIqtd qtd; // copy of current QTD (being worked on)
+ uint32_t qtdaddr; // address QTD read from
+
+ uint32_t itdaddr; // current ITD
+
+ uint32_t fetch_addr; // which address to look at next
+
+ USBBus bus;
+ USBPacket usb_packet;
+ int async_complete;
+ uint32_t tbytes;
+ int pid;
+ int exec_status;
+ int isoch_pause;
+ uint32_t last_run_usec;
+ uint32_t frame_end_usec;
+} EHCIState;
+
+#define SET_LAST_RUN_CLOCK(s) \
+ (s)->last_run_usec = qemu_get_clock_ns(vm_clock) / 1000;
+
+/* nifty macros from Arnon's EHCI version */
+#define get_field(data, field) \
+ (((data) & field##_MASK) >> field##_SH)
+
+#define set_field(data, newval, field) do { \
+ uint32_t val = *data; \
+ val &= ~ field##_MASK; \
+ val |= ((newval) << field##_SH) & field##_MASK; \
+ *data = val; \
+ } while(0)
+
+
+#if EHCI_DEBUG
+static const char *addr2str(unsigned addr)
+{
+ const char *r = " unknown";
+ const char *n[] = {
+ [ CAPLENGTH ] = " CAPLENGTH",
+ [ HCIVERSION ] = "HCIVERSION",
+ [ HCSPARAMS ] = " HCSPARAMS",
+ [ HCCPARAMS ] = " HCCPARAMS",
+ [ USBCMD ] = " COMMAND",
+ [ USBSTS ] = " STATUS",
+ [ USBINTR ] = " INTERRUPT",
+ [ FRINDEX ] = " FRAME IDX",
+ [ PERIODICLISTBASE ] = "P-LIST BASE",
+ [ ASYNCLISTADDR ] = "A-LIST ADDR",
+ [ PORTSC_BEGIN ...
+ PORTSC_END ] = "PORT STATUS",
+ [ CONFIGFLAG ] = "CONFIG FLAG",
+ };
+
+ if (addr < ARRAY_SIZE(n) && n[addr] != NULL) {
+ return n[addr];
+ } else {
+ return r;
+ }
+}
+#endif
+
+
+static inline void ehci_set_interrupt(EHCIState *s, int intr)
+{
+ int level = 0;
+
+ // TODO honour interrupt threshold requests
+
+ s->usbsts |= intr;
+
+ if ((s->usbsts & USBINTR_MASK) & s->usbintr) {
+ level = 1;
+ }
+
+ qemu_set_irq(s->irq, level);
+}
+
+static inline void ehci_record_interrupt(EHCIState *s, int intr)
+{
+ s->usbsts_pending |= intr;
+}
+
+static inline void ehci_commit_interrupt(EHCIState *s)
+{
+ if (!s->usbsts_pending) {
+ return;
+ }
+ ehci_set_interrupt(s, s->usbsts_pending);
+ s->usbsts_pending = 0;
+}
+
+/* Attach or detach a device on root hub */
+
+static void ehci_attach(USBPort *port)
+{
+ EHCIState *s = port->opaque;
+ uint32_t *portsc = &s->portsc[port->index];
+
+ DPRINTF("ehci_attach invoked for index %d, portsc 0x%x, desc %s\n",
+ port->index, *portsc, port->dev->product_desc);
+
+ *portsc |= PORTSC_CONNECT;
+ *portsc |= PORTSC_CSC;
+
+ /*
+ * If a high speed device is attached then we own this port(indicated
+ * by zero in the PORTSC_POWNER bit field) so set the status bit
+ * and set an interrupt if enabled.
+ */
+ if ( !(*portsc & PORTSC_POWNER)) {
+ ehci_set_interrupt(s, USBSTS_PCD);
+ }
+}
+
+static void ehci_detach(USBPort *port)
+{
+ EHCIState *s = port->opaque;
+ uint32_t *portsc = &s->portsc[port->index];
+
+ DPRINTF("ehci_attach invoked for index %d, portsc 0x%x\n",
+ port->index, *portsc);
+
+ *portsc &= ~PORTSC_CONNECT;
+ *portsc |= PORTSC_CSC;
+
+ /*
+ * If a high speed device is attached then we own this port(indicated
+ * by zero in the PORTSC_POWNER bit field) so set the status bit
+ * and set an interrupt if enabled.
+ */
+ if ( !(*portsc & PORTSC_POWNER)) {
+ ehci_set_interrupt(s, USBSTS_PCD);
+ }
+}
+
+/* 4.1 host controller initialization */
+static void ehci_reset(void *opaque)
+{
+ EHCIState *s = opaque;
+ uint8_t *pci_conf;
+ int i;
+
+ pci_conf = s->dev.config;
+
+ memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE);
+
+ s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
+ s->usbsts = USBSTS_HALT;
+
+ s->astate = EST_INACTIVE;
+ s->pstate = EST_INACTIVE;
+ s->async_complete = 0;
+ s->isoch_pause = -1;
+ s->attach_poll_counter = 0;
+
+ for(i = 0; i < NB_PORTS; i++) {
+ s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER;
+
+ if (s->ports[i].dev) {
+ usb_attach(&s->ports[i], s->ports[i].dev);
+ }
+ }
+}
+
+static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
+{
+ EHCIState *s = ptr;
+ uint32_t val;
+
+ val = s->mmio[addr];
+
+ return val;
+}
+
+static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr)
+{
+ EHCIState *s = ptr;
+ uint32_t val;
+
+ val = s->mmio[addr] | (s->mmio[addr+1] << 8);
+
+ return val;
+}
+
+static uint32_t ehci_mem_readl(void *ptr, target_phys_addr_t addr)
+{
+ EHCIState *s = ptr;
+ uint32_t val;
+
+ val = s->mmio[addr] | (s->mmio[addr+1] << 8) |
+ (s->mmio[addr+2] << 16) | (s->mmio[addr+3] << 24);
+
+ return val;
+}
+
+static void ehci_mem_writeb(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+ fprintf(stderr, "EHCI doesn't handle byte writes to MMIO\n");
+ exit(1);
+}
+
+static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+ fprintf(stderr, "EHCI doesn't handle 16-bit writes to MMIO\n");
+ exit(1);
+}
+
+static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
+{
+ uint32_t *portsc = &s->portsc[port];
+ int rwc;
+ USBDevice *dev = s->ports[port].dev;
+
+ DPRINTF("port_status_write: "
+ "PORTSC (port %d) curr %08X new %08X rw-clear %08X rw %08X\n",
+ port, *portsc, val, (val & PORTSC_RWC_MASK), val & PORTSC_RO_MASK);
+
+ rwc = val & PORTSC_RWC_MASK;
+ val &= PORTSC_RO_MASK;
+
+ // handle_read_write_clear(&val, portsc, PORTSC_PEDC | PORTSC_CSC);
+
+ *portsc &= ~rwc;
+
+ if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) {
+ DPRINTF("port_status_write: USBTRAN Port %d reset begin\n", port);
+ }
+
+ if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) {
+ DPRINTF("port_status_write: USBTRAN Port %d reset done\n", port);
+ usb_attach(&s->ports[port], dev);
+
+ // TODO how to handle reset of ports with no device
+ if (dev) {
+ usb_send_msg(dev, USB_MSG_RESET);
+ }
+
+ if (s->ports[port].dev) {
+ DPRINTF("port_status_write: "
+ "Device was connected before reset, clearing CSC bit\n");
+ *portsc &= ~PORTSC_CSC;
+ }
+
+ /* Table 2.16 Set the enable bit(and enable bit change) to indicate
+ * to SW that this port has a high speed device attached
+ *
+ * TODO - when to disable?
+ */
+ val |= PORTSC_PED;
+ val |= PORTSC_PEDC;
+ }
+
+ *portsc &= ~PORTSC_RO_MASK;
+ *portsc |= val;
+ DPRINTF("port_status_write: Port %d status set to 0x%08x\n", port, *portsc);
+}
+
+static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+ EHCIState *s = ptr;
+ int i;
+#if EHCI_DEBUG
+ const char *str;
+#endif
+
+ /* Only aligned reads are allowed on OHCI */
+ if (addr & 3) {
+ fprintf(stderr, "usb-ehci: Mis-aligned write to addr 0x"
+ TARGET_FMT_plx "\n", addr);
+ return;
+ }
+
+ if (addr >= PORTSC && addr < PORTSC + 4 * NB_PORTS) {
+ handle_port_status_write(s, (addr-PORTSC)/4, val);
+ return;
+ }
+
+ if (addr < OPREGBASE) {
+ fprintf(stderr, "usb-ehci: write attempt to read-only register"
+ TARGET_FMT_plx "\n", addr);
+ return;
+ }
+
+
+ /* Do any register specific pre-write processing here. */
+#if EHCI_DEBUG
+ str = addr2str((unsigned) addr);
+#endif
+ switch(addr) {
+ case USBCMD:
+ DPRINTF("ehci_mem_writel: USBCMD val=0x%08X, current cmd=0x%08X\n",
+ val, s->usbcmd);
+
+ if ((val & USBCMD_RUNSTOP) && !(s->usbcmd & USBCMD_RUNSTOP)) {
+ DPRINTF("ehci_mem_writel: %s run, clear halt\n", str);
+ qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock));
+ SET_LAST_RUN_CLOCK(s);
+ s->usbsts &= ~USBSTS_HALT;
+ }
+
+ if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) {
+ DPRINTF(" ** STOP **\n");
+ qemu_del_timer(s->frame_timer);
+ // TODO - should finish out some stuff before setting halt
+ s->usbsts |= USBSTS_HALT;
+ }
+
+ if (val & USBCMD_HCRESET) {
+ DPRINTF("ehci_mem_writel: %s run, resetting\n", str);
+ ehci_reset(s);
+ val &= ~USBCMD_HCRESET;
+ }
+
+ /* not supporting dynamic frame list size at the moment */
+ if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) {
+ fprintf(stderr, "attempt to set frame list size -- value %d\n",
+ val & USBCMD_FLS);
+ val &= ~USBCMD_FLS;
+ }
+#if EHCI_DEBUG
+ if ((val & USBCMD_PSE) && !(s->usbcmd & USBCMD_PSE)) {
+ DPRINTF("periodic scheduling enabled\n");
+ }
+ if (!(val & USBCMD_PSE) && (s->usbcmd & USBCMD_PSE)) {
+ DPRINTF("periodic scheduling disabled\n");
+ }
+ if ((val & USBCMD_ASE) && !(s->usbcmd & USBCMD_ASE)) {
+ DPRINTF("asynchronous scheduling enabled\n");
+ }
+ if (!(val & USBCMD_ASE) && (s->usbcmd & USBCMD_ASE)) {
+ DPRINTF("asynchronous scheduling disabled\n");
+ }
+ if ((val & USBCMD_IAAD) && !(s->usbcmd & USBCMD_IAAD)) {
+ DPRINTF("doorbell request received\n");
+ }
+ if ((val & USBCMD_LHCR) && !(s->usbcmd & USBCMD_LHCR)) {
+ DPRINTF("light host controller reset received\n");
+ }
+ if ((val & USBCMD_ITC) != (s->usbcmd & USBCMD_ITC)) {
+ DPRINTF("interrupt threshold control set to %x\n",
+ (val & USBCMD_ITC)>>USBCMD_ITC_SH);
+ }
+#endif
+ break;
+
+
+ case USBSTS:
+ val &= USBSTS_RO_MASK; // bits 6 thru 31 are RO
+ DPRINTF("ehci_mem_writel: %s RWC set to 0x%08X\n", str, val);
+
+ val = (s->usbsts &= ~val); // bits 0 thru 5 are R/WC
+
+ DPRINTF("ehci_mem_writel: %s updating interrupt condition\n", str);
+ ehci_set_interrupt(s, 0);
+ break;
+
+
+ case USBINTR:
+ val &= USBINTR_MASK;
+ DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
+ break;
+
+ case FRINDEX:
+ s->sofv = val >> 3;
+ DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
+ break;
+
+ case CONFIGFLAG:
+ DPRINTF("ehci_mem_writel: %s set to 0x%08X\n", str, val);
+ val &= 0x1;
+ if (val) {
+ for(i = 0; i < NB_PORTS; i++)
+ s->portsc[i] &= ~PORTSC_POWNER;
+ }
+ break;
+
+ case PERIODICLISTBASE:
+ if ((s->usbcmd & USBCMD_PSE) && (s->usbcmd & USBCMD_RUNSTOP)) {
+ fprintf(stderr,
+ "ehci: PERIODIC list base register set while periodic schedule\n"
+ " is enabled and HC is enabled\n");
+ }
+ DPRINTF("ehci_mem_writel: P-LIST BASE set to 0x%08X\n", val);
+ break;
+
+ case ASYNCLISTADDR:
+ if ((s->usbcmd & USBCMD_ASE) && (s->usbcmd & USBCMD_RUNSTOP)) {
+ fprintf(stderr,
+ "ehci: ASYNC list address register set while async schedule\n"
+ " is enabled and HC is enabled\n");
+ }
+ DPRINTF("ehci_mem_writel: A-LIST ADDR set to 0x%08X\n", val);
+ break;
+ }
+
+ *(uint32_t *)(&s->mmio[addr]) = val;
+}
+
+
+// TODO : Put in common header file, duplication from usb-ohci.c
+
+/* Get an array of dwords from main memory */
+static inline int get_dwords(uint32_t addr, uint32_t *buf, int num)
+{
+ int i;
+
+ for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+ cpu_physical_memory_rw(addr,(uint8_t *)buf, sizeof(*buf), 0);
+ *buf = le32_to_cpu(*buf);
+ }
+
+ return 1;
+}
+
+/* Put an array of dwords in to main memory */
+static inline int put_dwords(uint32_t addr, uint32_t *buf, int num)
+{
+ int i;
+
+ for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+ uint32_t tmp = cpu_to_le32(*buf);
+ cpu_physical_memory_rw(addr,(uint8_t *)&tmp, sizeof(tmp), 1);
+ }
+
+ return 1;
+}
+
+// 4.10.2
+
+static int ehci_qh_do_overlay(EHCIState *ehci, EHCIqh *qh, EHCIqtd *qtd)
+{
+ int i;
+ int dtoggle;
+ int ping;
+ int eps;
+ int reload;
+
+ // remember values in fields to preserve in qh after overlay
+
+ dtoggle = qh->token & QTD_TOKEN_DTOGGLE;
+ ping = qh->token & QTD_TOKEN_PING;
+
+ DPRINTF("setting qh.current from %08X to 0x%08X\n", qh->current_qtd,
+ ehci->qtdaddr);
+ qh->current_qtd = ehci->qtdaddr;
+ qh->next_qtd = qtd->next;
+ qh->altnext_qtd = qtd->altnext;
+ qh->token = qtd->token;
+
+
+ eps = get_field(qh->epchar, QH_EPCHAR_EPS);
+ if (eps == EHCI_QH_EPS_HIGH) {
+ qh->token &= ~QTD_TOKEN_PING;
+ qh->token |= ping;
+ }
+
+ reload = get_field(qh->epchar, QH_EPCHAR_RL);
+ set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
+
+ for (i = 0; i < 5; i++) {
+ qh->bufptr[i] = qtd->bufptr[i];
+ }
+
+ if (!(qh->epchar & QH_EPCHAR_DTC)) {
+ // preserve QH DT bit
+ qh->token &= ~QTD_TOKEN_DTOGGLE;
+ qh->token |= dtoggle;
+ }
+
+ qh->bufptr[1] &= ~BUFPTR_CPROGMASK_MASK;
+ qh->bufptr[2] &= ~BUFPTR_FRAMETAG_MASK;
+
+ put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
+
+ return 0;
+}
+
+static int ehci_buffer_rw(uint8_t *buffer, EHCIqh *qh, int bytes, int rw)
+{
+ int bufpos = 0;
+ int cpage, offset;
+ uint32_t head;
+ uint32_t tail;
+
+
+ if (!bytes) {
+ return 0;
+ }
+
+ cpage = get_field(qh->token, QTD_TOKEN_CPAGE);
+ if (cpage > 4) {
+ fprintf(stderr, "cpage out of range (%d)\n", cpage);
+ return USB_RET_PROCERR;
+ }
+
+ offset = qh->bufptr[0] & ~QTD_BUFPTR_MASK;
+ DPRINTF("ehci_buffer_rw: %sing %d bytes %08x cpage %d offset %d\n",
+ rw ? "writ" : "read", bytes, qh->bufptr[0], cpage, offset);
+
+ do {
+ /* start and end of this page */
+ head = qh->bufptr[cpage] & QTD_BUFPTR_MASK;
+ tail = head + ~QTD_BUFPTR_MASK + 1;
+ /* add offset into page */
+ head |= offset;
+
+ if (bytes <= (tail - head)) {
+ tail = head + bytes;
+ }
+
+ DPRINTF("DATA %s cpage:%d head:%08X tail:%08X target:%08X\n",
+ rw ? "WRITE" : "READ ", cpage, head, tail, bufpos);
+
+ cpu_physical_memory_rw(head, &buffer[bufpos], tail - head, rw);
+
+ bufpos += (tail - head);
+ bytes -= (tail - head);
+
+ if (bytes > 0) {
+ cpage++;
+ offset = 0;
+ }
+ } while (bytes > 0);
+
+ /* save cpage */
+ set_field(&qh->token, cpage, QTD_TOKEN_CPAGE);
+
+ /* save offset into cpage */
+ offset = tail - head;
+ qh->bufptr[0] &= ~QTD_BUFPTR_MASK;
+ qh->bufptr[0] |= offset;
+
+ return 0;
+}
+
+static void ehci_async_complete_packet(USBDevice *dev, USBPacket *packet)
+{
+ EHCIState *ehci = container_of(packet, EHCIState, usb_packet);
+
+ DPRINTF("Async packet complete\n");
+ ehci->async_complete = 1;
+ ehci->exec_status = packet->len;
+}
+
+static int ehci_execute_complete(EHCIState *ehci, EHCIqh *qh, int ret)
+{
+ int c_err, reload;
+
+ if (ret == USB_RET_ASYNC && !ehci->async_complete) {
+ DPRINTF("not done yet\n");
+ return ret;
+ }
+
+ ehci->async_complete = 0;
+
+ DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n",
+ ehci->qhaddr, qh->next, ehci->qtdaddr, ret);
+
+ if (ret < 0) {
+err:
+ /* TO-DO: put this is in a function that can be invoked below as well */
+ c_err = get_field(qh->token, QTD_TOKEN_CERR);
+ c_err--;
+ set_field(&qh->token, c_err, QTD_TOKEN_CERR);
+
+ switch(ret) {
+ case USB_RET_NODEV:
+ fprintf(stderr, "USB no device\n");
+ break;
+ case USB_RET_STALL:
+ fprintf(stderr, "USB stall\n");
+ qh->token |= QTD_TOKEN_HALT;
+ ehci_record_interrupt(ehci, USBSTS_ERRINT);
+ break;
+ case USB_RET_NAK:
+ /* 4.10.3 */
+ reload = get_field(qh->epchar, QH_EPCHAR_RL);
+ if ((ehci->pid == USB_TOKEN_IN) && reload) {
+ int nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
+ nakcnt--;
+ set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
+ } else if (!reload) {
+ return USB_RET_NAK;
+ }
+ break;
+ case USB_RET_BABBLE:
+ fprintf(stderr, "USB babble TODO\n");
+ qh->token |= QTD_TOKEN_BABBLE;
+ ehci_record_interrupt(ehci, USBSTS_ERRINT);
+ break;
+ default:
+ fprintf(stderr, "USB invalid response %d to handle\n", ret);
+ /* TO-DO: transaction error */
+ ret = USB_RET_PROCERR;
+ break;
+ }
+ } else {
+ // DPRINTF("Short packet condition\n");
+ // TODO check 4.12 for splits
+
+ if ((ret > ehci->tbytes) && (ehci->pid == USB_TOKEN_IN)) {
+ ret = USB_RET_BABBLE;
+ goto err;
+ }
+
+ if (ehci->tbytes && ehci->pid == USB_TOKEN_IN) {
+ if (ehci_buffer_rw(ehci->buffer, qh, ret, 1) != 0) {
+ return USB_RET_PROCERR;
+ }
+ ehci->tbytes -= ret;
+ } else {
+ ehci->tbytes = 0;
+ }
+
+ DPRINTF("updating tbytes to %d\n", ehci->tbytes);
+ set_field(&qh->token, ehci->tbytes, QTD_TOKEN_TBYTES);
+ }
+
+ qh->token ^= QTD_TOKEN_DTOGGLE;
+ qh->token &= ~QTD_TOKEN_ACTIVE;
+
+ if ((ret >= 0) && (qh->token & QTD_TOKEN_IOC)) {
+ ehci_record_interrupt(ehci, USBSTS_INT);
+ }
+
+ return ret;
+}
+
+// 4.10.3
+
+static int ehci_execute(EHCIState *ehci, EHCIqh *qh)
+{
+ USBPort *port;
+ USBDevice *dev;
+ int ret;
+ int i;
+ int endp;
+ int devadr;
+
+ if ( !(qh->token & QTD_TOKEN_ACTIVE)) {
+ fprintf(stderr, "Attempting to execute inactive QH\n");
+ return USB_RET_PROCERR;
+ }
+
+ ehci->tbytes = (qh->token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
+ if (ehci->tbytes > BUFF_SIZE) {
+ fprintf(stderr, "Request for more bytes than allowed\n");
+ return USB_RET_PROCERR;
+ }
+
+ ehci->pid = (qh->token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
+ switch(ehci->pid) {
+ case 0: ehci->pid = USB_TOKEN_OUT; break;
+ case 1: ehci->pid = USB_TOKEN_IN; break;
+ case 2: ehci->pid = USB_TOKEN_SETUP; break;
+ default: fprintf(stderr, "bad token\n"); break;
+ }
+
+ if ((ehci->tbytes && ehci->pid != USB_TOKEN_IN) &&
+ (ehci_buffer_rw(ehci->buffer, qh, ehci->tbytes, 0) != 0)) {
+ return USB_RET_PROCERR;
+ }
+
+ endp = get_field(qh->epchar, QH_EPCHAR_EP);
+ devadr = get_field(qh->epchar, QH_EPCHAR_DEVADDR);
+
+ ret = USB_RET_NODEV;
+
+ // TO-DO: associating device with ehci port
+ for(i = 0; i < NB_PORTS; i++) {
+ port = &ehci->ports[i];
+ dev = port->dev;
+
+ // TODO sometime we will also need to check if we are the port owner
+
+ if (!(ehci->portsc[i] &(PORTSC_CONNECT))) {
+ DPRINTF("Port %d, no exec, not connected(%08X)\n",
+ i, ehci->portsc[i]);
+ continue;
+ }
+
+ ehci->usb_packet.pid = ehci->pid;
+ ehci->usb_packet.devaddr = devadr;
+ ehci->usb_packet.devep = endp;
+ ehci->usb_packet.data = ehci->buffer;
+ ehci->usb_packet.len = ehci->tbytes;
+
+ ret = usb_handle_packet(dev, &ehci->usb_packet);
+
+ DPRINTF("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n",
+ ehci->qhaddr, qh->next, ehci->qtdaddr, ehci->pid,
+ ehci->usb_packet.len, ehci->tbytes, endp, ret);
+
+ if (ret != USB_RET_NODEV) {
+ break;
+ }
+ }
+
+ if (ret > BUFF_SIZE) {
+ fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n");
+ return USB_RET_PROCERR;
+ }
+
+ if (ret == USB_RET_ASYNC) {
+ ehci->async_complete = 0;
+ }
+
+ return ret;
+}
+
+/* 4.7.2
+ */
+
+static int ehci_process_itd(EHCIState *ehci,
+ EHCIitd *itd)
+{
+ USBPort *port;
+ USBDevice *dev;
+ int ret;
+ int i, j;
+ int ptr;
+ int pid;
+ int pg;
+ int len;
+ int dir;
+ int devadr;
+ int endp;
+ int maxpkt;
+
+ dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION);
+ devadr = get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR);
+ endp = get_field(itd->bufptr[0], ITD_BUFPTR_EP);
+ maxpkt = get_field(itd->bufptr[1], ITD_BUFPTR_MAXPKT);
+
+ for(i = 0; i < 8; i++) {
+ if (itd->transact[i] & ITD_XACT_ACTIVE) {
+ DPRINTF("ISOCHRONOUS active for frame %d, interval %d\n",
+ ehci->frindex >> 3, i);
+
+ pg = get_field(itd->transact[i], ITD_XACT_PGSEL);
+ ptr = (itd->bufptr[pg] & ITD_BUFPTR_MASK) |
+ (itd->transact[i] & ITD_XACT_OFFSET_MASK);
+ len = get_field(itd->transact[i], ITD_XACT_LENGTH);
+
+ if (len > BUFF_SIZE) {
+ return USB_RET_PROCERR;
+ }
+
+ DPRINTF("ISOCH: buffer %08X len %d\n", ptr, len);
+
+ if (!dir) {
+ cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 0);
+ pid = USB_TOKEN_OUT;
+ } else
+ pid = USB_TOKEN_IN;
+
+ ret = USB_RET_NODEV;
+
+ for (j = 0; j < NB_PORTS; j++) {
+ port = &ehci->ports[j];
+ dev = port->dev;
+
+ // TODO sometime we will also need to check if we are the port owner
+
+ if (!(ehci->portsc[j] &(PORTSC_CONNECT))) {
+ DPRINTF("Port %d, no exec, not connected(%08X)\n",
+ j, ehci->portsc[j]);
+ continue;
+ }
+
+ ehci->usb_packet.pid = ehci->pid;
+ ehci->usb_packet.devaddr = devadr;
+ ehci->usb_packet.devep = endp;
+ ehci->usb_packet.data = ehci->buffer;
+ ehci->usb_packet.len = len;
+
+ DPRINTF("calling usb_handle_packet\n");
+ ret = usb_handle_packet(dev, &ehci->usb_packet);
+
+ if (ret != USB_RET_NODEV) {
+ break;
+ }
+ }
+
+ /* In isoch, there is no facility to indicate a NAK so let's
+ * instead just complete a zero-byte transaction. Setting
+ * DBERR seems too draconian.
+ */
+
+ if (ret == USB_RET_NAK) {
+ if (ehci->isoch_pause > 0) {
+ DPRINTF("ISOCH: received a NAK but paused so returning\n");
+ ehci->isoch_pause--;
+ return 0;
+ } else if (ehci->isoch_pause == -1) {
+ DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n");
+ // Pause frindex for up to 50 msec waiting for data from
+ // remote
+ ehci->isoch_pause = 50;
+ return 0;
+ } else {
+ DPRINTF("ISOCH: isoch pause timeout! return 0\n");
+ ret = 0;
+ }
+ } else {
+ DPRINTF("ISOCH: received ACK, clearing pause\n");
+ ehci->isoch_pause = -1;
+ }
+
+ if (ret >= 0) {
+ itd->transact[i] &= ~ITD_XACT_ACTIVE;
+
+ if (itd->transact[i] & ITD_XACT_IOC) {
+ ehci_record_interrupt(ehci, USBSTS_INT);
+ }
+ }
+
+ if (ret >= 0 && dir) {
+ cpu_physical_memory_rw(ptr, &ehci->buffer[0], len, 1);
+
+ if (ret != len) {
+ DPRINTF("ISOCH IN expected %d, got %d\n",
+ len, ret);
+ set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* This state is the entry point for asynchronous schedule
+ * processing. Entry here consitutes a EHCI start event state (4.8.5)
+ */
+static int ehci_state_waitlisthead(EHCIState *ehci, int async, int *state)
+{
+ EHCIqh *qh = &ehci->qh;
+ int i = 0;
+ int again = 0;
+ uint32_t entry = ehci->asynclistaddr;
+
+ /* set reclamation flag at start event (4.8.6) */
+ if (async) {
+ ehci->usbsts |= USBSTS_REC;
+ }
+
+ /* Find the head of the list (4.9.1.1) */
+ for(i = 0; i < MAX_QH; i++) {
+ get_dwords(NLPTR_GET(entry), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
+
+ if (qh->epchar & QH_EPCHAR_H) {
+ DPRINTF_ST("WAITLISTHEAD: QH %08X is the HEAD of the list\n",
+ entry);
+ if (async) {
+ entry |= (NLPTR_TYPE_QH << 1);
+ }
+
+ ehci->fetch_addr = entry;
+ *state = EST_FETCHENTRY;
+ again = 1;
+ goto out;
+ }
+
+ DPRINTF_ST("WAITLISTHEAD: QH %08X is NOT the HEAD of the list\n",
+ entry);
+ entry = qh->next;
+ if (entry == ehci->asynclistaddr) {
+ DPRINTF("WAITLISTHEAD: reached beginning of QH list\n");
+ break;
+ }
+ }
+
+ /* no head found for list. */
+
+ *state = EST_ACTIVE;
+
+out:
+ return again;
+}
+
+
+/* This state is the entry point for periodic schedule processing as
+ * well as being a continuation state for async processing.
+ */
+static int ehci_state_fetchentry(EHCIState *ehci, int async, int *state)
+{
+ int again = 0;
+ uint32_t entry = ehci->fetch_addr;
+
+#if EHCI_DEBUG == 0
+ if (qemu_get_clock_ns(vm_clock) / 1000 >= ehci->frame_end_usec) {
+ if (async) {
+ DPRINTF("FETCHENTRY: FRAME timer elapsed, exit state machine\n");
+ goto out;
+ } else {
+ DPRINTF("FETCHENTRY: WARNING "
+ "- frame timer elapsed during periodic\n");
+ }
+ }
+#endif
+ if (entry < 0x1000) {
+ DPRINTF("fetchentry: entry invalid (0x%08x)\n", entry);
+ *state = EST_ACTIVE;
+ goto out;
+ }
+
+ /* section 4.8, only QH in async schedule */
+ if (async && (NLPTR_TYPE_GET(entry) != NLPTR_TYPE_QH)) {
+ fprintf(stderr, "non queue head request in async schedule\n");
+ return -1;
+ }
+
+ switch (NLPTR_TYPE_GET(entry)) {
+ case NLPTR_TYPE_QH:
+ DPRINTF_ST("FETCHENTRY: entry %X is a Queue Head\n", entry);
+ *state = EST_FETCHQH;
+ ehci->qhaddr = entry;
+ again = 1;
+ break;
+
+ case NLPTR_TYPE_ITD:
+ DPRINTF_ST("FETCHENTRY: entry %X is an ITD\n", entry);
+ *state = EST_FETCHITD;
+ ehci->itdaddr = entry;
+ again = 1;
+ break;
+
+ default:
+ // TODO: handle siTD and FSTN types
+ fprintf(stderr, "FETCHENTRY: entry at %X is of type %d "
+ "which is not supported yet\n", entry, NLPTR_TYPE_GET(entry));
+ return -1;
+ }
+
+out:
+ return again;
+}
+
+static int ehci_state_fetchqh(EHCIState *ehci, int async, int *state)
+{
+ EHCIqh *qh = &ehci->qh;
+ int reload;
+ int again = 0;
+
+ get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
+
+ if (async && (qh->epchar & QH_EPCHAR_H)) {
+
+ /* EHCI spec version 1.0 Section 4.8.3 & 4.10.1 */
+ if (ehci->usbsts & USBSTS_REC) {
+ ehci->usbsts &= ~USBSTS_REC;
+ } else {
+ DPRINTF("FETCHQH: QH 0x%08x. H-bit set, reclamation status reset"
+ " - done processing\n", ehci->qhaddr);
+ *state = EST_ACTIVE;
+ goto out;
+ }
+ }
+
+#if EHCI_DEBUG
+ if (ehci->qhaddr != qh->next) {
+ DPRINTF("FETCHQH: QH 0x%08x (h %x halt %x active %x) next 0x%08x\n",
+ ehci->qhaddr,
+ qh->epchar & QH_EPCHAR_H,
+ qh->token & QTD_TOKEN_HALT,
+ qh->token & QTD_TOKEN_ACTIVE,
+ qh->next);
+ }
+#endif
+
+ reload = get_field(qh->epchar, QH_EPCHAR_RL);
+ if (reload) {
+ DPRINTF_ST("FETCHQH: reloading nakcnt to %d\n", reload);
+ set_field(&qh->altnext_qtd, reload, QH_ALTNEXT_NAKCNT);
+ }
+
+ if (qh->token & QTD_TOKEN_HALT) {
+ DPRINTF_ST("FETCHQH: QH Halted, go horizontal\n");
+ *state = EST_HORIZONTALQH;
+ again = 1;
+
+ } else if ((qh->token & QTD_TOKEN_ACTIVE) && (qh->current_qtd > 0x1000)) {
+ DPRINTF_ST("FETCHQH: Active, !Halt, execute - fetch qTD\n");
+ ehci->qtdaddr = qh->current_qtd;
+ *state = EST_FETCHQTD;
+ again = 1;
+
+ } else {
+ /* EHCI spec version 1.0 Section 4.10.2 */
+ DPRINTF_ST("FETCHQH: !Active, !Halt, advance queue\n");
+ *state = EST_ADVANCEQUEUE;
+ again = 1;
+ }
+
+out:
+ return again;
+}
+
+static int ehci_state_fetchitd(EHCIState *ehci, int async, int *state)
+{
+ EHCIitd itd;
+
+ get_dwords(NLPTR_GET(ehci->itdaddr),(uint32_t *) &itd,
+ sizeof(EHCIitd) >> 2);
+ DPRINTF_ST("FETCHITD: Fetched ITD at address %08X " "(next is %08X)\n",
+ ehci->itdaddr, itd.next);
+
+ if (ehci_process_itd(ehci, &itd) != 0) {
+ return -1;
+ }
+
+ put_dwords(NLPTR_GET(ehci->itdaddr), (uint32_t *) &itd,
+ sizeof(EHCIitd) >> 2);
+ ehci->fetch_addr = itd.next;
+ *state = EST_FETCHENTRY;
+
+ return 1;
+}
+
+/* Section 4.10.2 - paragraph 3 */
+static int ehci_state_advqueue(EHCIState *ehci, int async, int *state)
+{
+#if 0
+ /* TO-DO: 4.10.2 - paragraph 2
+ * if I-bit is set to 1 and QH is not active
+ * go to horizontal QH
+ */
+ if (I-bit set) {
+ *state = EST_HORIZONTALQH;
+ goto out;
+ }
+#endif
+
+ /*
+ * want data and alt-next qTD is valid
+ */
+ if (((ehci->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) &&
+ (ehci->qh.altnext_qtd > 0x1000) &&
+ (NLPTR_TBIT(ehci->qh.altnext_qtd) == 0)) {
+ DPRINTF_ST("ADVQUEUE: goto alt next qTD. "
+ "curr 0x%08x next 0x%08x alt 0x%08x (next qh %x)\n",
+ ehci->qh.current_qtd, ehci->qh.altnext_qtd,
+ ehci->qh.next_qtd, ehci->qh.next);
+ ehci->qtdaddr = ehci->qh.altnext_qtd;
+ *state = EST_FETCHQTD;
+
+ /*
+ * next qTD is valid
+ */
+ } else if ((ehci->qh.next_qtd > 0x1000) &&
+ (NLPTR_TBIT(ehci->qh.next_qtd) == 0)) {
+ DPRINTF_ST("ADVQUEUE: next qTD. "
+ "curr 0x%08x next 0x%08x alt 0x%08x (next qh %x)\n",
+ ehci->qh.current_qtd, ehci->qh.altnext_qtd,
+ ehci->qh.next_qtd, ehci->qh.next);
+ ehci->qtdaddr = ehci->qh.next_qtd;
+ *state = EST_FETCHQTD;
+
+ /*
+ * no valid qTD, try next QH
+ */
+ } else {
+ DPRINTF_ST("ADVQUEUE: go to horizontal QH\n");
+ *state = EST_HORIZONTALQH;
+ }
+
+ return 1;
+}
+
+/* Section 4.10.2 - paragraph 4 */
+static int ehci_state_fetchqtd(EHCIState *ehci, int async, int *state)
+{
+ EHCIqtd *qtd = &ehci->qtd;
+ int again = 0;
+
+ get_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) qtd, sizeof(EHCIqtd) >> 2);
+
+ if (qtd->token & QTD_TOKEN_ACTIVE) {
+ *state = EST_EXECUTE;
+ again = 1;
+ } else {
+ *state = EST_HORIZONTALQH;
+ again = 1;
+ }
+
+ return again;
+}
+
+static int ehci_state_horizqh(EHCIState *ehci, int async, int *state)
+{
+ int again = 0;
+
+ if (ehci->fetch_addr != ehci->qh.next) {
+ ehci->fetch_addr = ehci->qh.next;
+ *state = EST_FETCHENTRY;
+ again = 1;
+ } else {
+ *state = EST_ACTIVE;
+ }
+
+ return again;
+}
+
+static int ehci_state_execute(EHCIState *ehci, int async, int *state)
+{
+ EHCIqh *qh = &ehci->qh;
+ EHCIqtd *qtd = &ehci->qtd;
+ int again = 0;
+ int reload, nakcnt;
+ int smask;
+
+ if (async) {
+ DPRINTF_ST(">>>>> ASYNC STATE MACHINE execute QH 0x%08x, QTD 0x%08x\n",
+ ehci->qhaddr, ehci->qtdaddr);
+ } else {
+ DPRINTF_ST(">>>>> PERIODIC STATE MACHINE execute\n");
+ }
+
+ if (ehci_qh_do_overlay(ehci, qh, qtd) != 0) {
+ return -1;
+ }
+
+ smask = get_field(qh->epcap, QH_EPCAP_SMASK);
+
+ if (!smask) {
+ reload = get_field(qh->epchar, QH_EPCHAR_RL);
+ nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
+ if (reload && !nakcnt) {
+ DPRINTF_ST("EXECUTE: RL != 0 but NakCnt == 0 -- no execute\n");
+ *state = EST_HORIZONTALQH;
+ again = 1;
+ goto out;
+ }
+ }
+
+ // TODO verify enough time remains in the uframe as in 4.4.1.1
+ // TODO write back ptr to async list when done or out of time
+ // TODO Windows does not seem to ever set the MULT field
+
+ if (!async) {
+ int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT);
+ if (!transactCtr) {
+ DPRINTF("ZERO transactctr for int qh, go HORIZ\n");
+ *state = EST_HORIZONTALQH;
+ again = 1;
+ goto out;
+ }
+ }
+
+ if (async) {
+ ehci->usbsts |= USBSTS_REC;
+ }
+
+ ehci->exec_status = ehci_execute(ehci, qh);
+ if (ehci->exec_status == USB_RET_PROCERR) {
+ again = -1;
+ goto out;
+ }
+ *state = EST_EXECUTING;
+
+ if (ehci->exec_status != USB_RET_ASYNC) {
+ again = 1;
+ }
+
+out:
+ return again;
+}
+
+static int ehci_state_executing(EHCIState *ehci, int async, int *state)
+{
+ EHCIqh *qh = &ehci->qh;
+ int again = 0;
+ int reload, nakcnt;
+
+ ehci->exec_status = ehci_execute_complete(ehci, qh, ehci->exec_status);
+ if (ehci->exec_status == USB_RET_ASYNC) {
+ goto out;
+ }
+ if (ehci->exec_status == USB_RET_PROCERR) {
+ again = -1;
+ goto out;
+ }
+
+ // 4.10.3
+ if (!async) {
+ int transactCtr = get_field(qh->epcap, QH_EPCAP_MULT);
+ transactCtr--;
+ set_field(&qh->epcap, transactCtr, QH_EPCAP_MULT);
+ // 4.10.3, bottom of page 82, should exit this state when transaction
+ // counter decrements to 0
+ }
+
+
+ reload = get_field(qh->epchar, QH_EPCHAR_RL);
+ if (reload) {
+ nakcnt = get_field(qh->altnext_qtd, QH_ALTNEXT_NAKCNT);
+ if (ehci->exec_status == USB_RET_NAK) {
+ if (nakcnt) {
+ nakcnt--;
+ }
+ DPRINTF_ST("EXECUTING: Nak occured and RL != 0, dec NakCnt to %d\n",
+ nakcnt);
+ } else {
+ nakcnt = reload;
+ DPRINTF_ST("EXECUTING: Nak didn't occur, reloading to %d\n",
+ nakcnt);
+ }
+ set_field(&qh->altnext_qtd, nakcnt, QH_ALTNEXT_NAKCNT);
+ }
+
+ /*
+ * Write the qh back to guest physical memory. This step isn't
+ * in the EHCI spec but we need to do it since we don't share
+ * physical memory with our guest VM.
+ */
+
+ DPRINTF("EXECUTING: write QH to VM memory: qhaddr 0x%x, next 0x%x\n",
+ ehci->qhaddr, qh->next);
+ put_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) qh, sizeof(EHCIqh) >> 2);
+
+ /* 4.10.5 */
+ if ((ehci->exec_status == USB_RET_NAK) || (qh->token & QTD_TOKEN_ACTIVE)) {
+ *state = EST_HORIZONTALQH;
+ } else {
+ *state = EST_WRITEBACK;
+ }
+
+ again = 1;
+
+out:
+ return again;
+}
+
+
+static int ehci_state_writeback(EHCIState *ehci, int async, int *state)
+{
+ EHCIqh *qh = &ehci->qh;
+ int again = 0;
+
+ /* Write back the QTD from the QH area */
+ DPRINTF_ST("WRITEBACK: write QTD to VM memory\n");
+ put_dwords(NLPTR_GET(ehci->qtdaddr),(uint32_t *) &qh->next_qtd,
+ sizeof(EHCIqtd) >> 2);
+
+ /* TODO confirm next state. For now, keep going if async
+ * but stop after one qtd if periodic
+ */
+ //if (async) {
+ *state = EST_ADVANCEQUEUE;
+ again = 1;
+ //} else {
+ // *state = EST_ACTIVE;
+ //}
+ return again;
+}
+
+/*
+ * This is the state machine that is common to both async and periodic
+ */
+
+static int ehci_advance_state(EHCIState *ehci,
+ int async,
+ int state)
+{
+ int again;
+ int iter = 0;
+
+ do {
+ if (state == EST_FETCHQH) {
+ iter++;
+ /* if we are roaming a lot of QH without executing a qTD
+ * something is wrong with the linked list. TO-DO: why is
+ * this hack needed?
+ */
+ if (iter > MAX_ITERATIONS) {
+ DPRINTF("\n*** advance_state: bailing on MAX ITERATIONS***\n");
+ state = EST_ACTIVE;
+ break;
+ }
+ }
+ switch(state) {
+ case EST_WAITLISTHEAD:
+ again = ehci_state_waitlisthead(ehci, async, &state);
+ break;
+
+ case EST_FETCHENTRY:
+ again = ehci_state_fetchentry(ehci, async, &state);
+ break;
+
+ case EST_FETCHQH:
+ again = ehci_state_fetchqh(ehci, async, &state);
+ break;
+
+ case EST_FETCHITD:
+ again = ehci_state_fetchitd(ehci, async, &state);
+ break;
+
+ case EST_ADVANCEQUEUE:
+ again = ehci_state_advqueue(ehci, async, &state);
+ break;
+
+ case EST_FETCHQTD:
+ again = ehci_state_fetchqtd(ehci, async, &state);
+ break;
+
+ case EST_HORIZONTALQH:
+ again = ehci_state_horizqh(ehci, async, &state);
+ break;
+
+ case EST_EXECUTE:
+ iter = 0;
+ again = ehci_state_execute(ehci, async, &state);
+ break;
+
+ case EST_EXECUTING:
+ again = ehci_state_executing(ehci, async, &state);
+ break;
+
+ case EST_WRITEBACK:
+ again = ehci_state_writeback(ehci, async, &state);
+ break;
+
+ default:
+ fprintf(stderr, "Bad state!\n");
+ again = -1;
+ break;
+ }
+
+ if (again < 0) {
+ fprintf(stderr, "processing error - resetting ehci HC\n");
+ ehci_reset(ehci);
+ again = 0;
+ }
+ }
+ while (again);
+
+ ehci_commit_interrupt(ehci);
+ return state;
+}
+
+static void ehci_advance_async_state(EHCIState *ehci)
+{
+ EHCIqh qh;
+ int state = ehci->astate;
+
+ switch(state) {
+ case EST_INACTIVE:
+ if (!(ehci->usbcmd & USBCMD_ASE)) {
+ break;
+ }
+ ehci->usbsts |= USBSTS_ASS;
+ ehci->astate = EST_ACTIVE;
+ // No break, fall through to ACTIVE
+
+ case EST_ACTIVE:
+ if ( !(ehci->usbcmd & USBCMD_ASE)) {
+ ehci->usbsts &= ~USBSTS_ASS;
+ ehci->astate = EST_INACTIVE;
+ break;
+ }
+
+ /* If the doorbell is set, the guest wants to make a change to the
+ * schedule. The host controller needs to release cached data.
+ * (section 4.8.2)
+ */
+ if (ehci->usbcmd & USBCMD_IAAD) {
+ DPRINTF("ASYNC: doorbell request acknowledged\n");
+ ehci->usbcmd &= ~USBCMD_IAAD;
+ ehci_set_interrupt(ehci, USBSTS_IAA);
+ break;
+ }
+
+ /* make sure guest has acknowledged */
+ /* TO-DO: is this really needed? */
+ if (ehci->usbsts & USBSTS_IAA) {
+ DPRINTF("IAA status bit still set.\n");
+ break;
+ }
+
+ DPRINTF_ST("ASYNC: waiting for listhead, starting at %08x\n",
+ ehci->asynclistaddr);
+ /* check that address register has been set */
+ if (ehci->asynclistaddr == 0) {
+ break;
+ }
+
+ state = EST_WAITLISTHEAD;
+ /* fall through */
+
+ case EST_FETCHENTRY:
+ /* fall through */
+
+ case EST_EXECUTING:
+ get_dwords(NLPTR_GET(ehci->qhaddr), (uint32_t *) &qh,
+ sizeof(EHCIqh) >> 2);
+ ehci->astate = ehci_advance_state(ehci, 1, state);
+ break;
+
+ default:
+ /* this should only be due to a developer mistake */
+ fprintf(stderr, "ehci: Bad asynchronous state %d. "
+ "Resetting to active\n", ehci->astate);
+ ehci->astate = EST_ACTIVE;
+ }
+}
+
+static void ehci_advance_periodic_state(EHCIState *ehci)
+{
+ uint32_t entry;
+ uint32_t list;
+
+ // 4.6
+
+ switch(ehci->pstate) {
+ case EST_INACTIVE:
+ if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) {
+ DPRINTF("PERIODIC going active\n");
+ ehci->usbsts |= USBSTS_PSS;
+ ehci->pstate = EST_ACTIVE;
+ // No break, fall through to ACTIVE
+ } else
+ break;
+
+ case EST_ACTIVE:
+ if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) {
+ DPRINTF("PERIODIC going inactive\n");
+ ehci->usbsts &= ~USBSTS_PSS;
+ ehci->pstate = EST_INACTIVE;
+ break;
+ }
+
+ list = ehci->periodiclistbase & 0xfffff000;
+ /* check that register has been set */
+ if (list == 0) {
+ break;
+ }
+ list |= ((ehci->frindex & 0x1ff8) >> 1);
+
+ cpu_physical_memory_rw(list, (uint8_t *) &entry, sizeof entry, 0);
+ entry = le32_to_cpu(entry);
+
+ DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n",
+ ehci->frindex / 8, list, entry);
+ ehci->fetch_addr = entry;
+ ehci->pstate = ehci_advance_state(ehci, 0, EST_FETCHENTRY);
+ break;
+
+ case EST_EXECUTING:
+ DPRINTF("PERIODIC state adv for executing\n");
+ ehci->pstate = ehci_advance_state(ehci, 0, EST_EXECUTING);
+ break;
+
+ default:
+ /* this should only be due to a developer mistake */
+ fprintf(stderr, "ehci: Bad periodic state %d. "
+ "Resetting to active\n", ehci->pstate);
+ ehci->pstate = EST_ACTIVE;
+ }
+}
+
+static void ehci_frame_timer(void *opaque)
+{
+ EHCIState *ehci = opaque;
+ int64_t expire_time, t_now;
+ int usec_elapsed;
+ int frames;
+ int usec_now;
+ int i;
+ int skipped_frames = 0;
+
+
+ t_now = qemu_get_clock_ns(vm_clock);
+ expire_time = t_now + (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+ if (expire_time == t_now) {
+ expire_time++;
+ }
+
+ usec_now = t_now / 1000;
+ usec_elapsed = usec_now - ehci->last_run_usec;
+ frames = usec_elapsed / FRAME_TIMER_USEC;
+ ehci->frame_end_usec = usec_now + FRAME_TIMER_USEC - 10;
+
+ for (i = 0; i < frames; i++) {
+ if ( !(ehci->usbsts & USBSTS_HALT)) {
+ if (ehci->isoch_pause <= 0) {
+ ehci->frindex += 8;
+ }
+
+ if (ehci->frindex > 0x00001fff) {
+ ehci->frindex = 0;
+ ehci_set_interrupt(ehci, USBSTS_FLR);
+ }
+
+ ehci->sofv = (ehci->frindex - 1) >> 3;
+ ehci->sofv &= 0x000003ff;
+ }
+
+ if (frames - i > 10) {
+ skipped_frames++;
+ } else {
+ // TODO could this cause periodic frames to get skipped if async
+ // active?
+ if (ehci->astate != EST_EXECUTING) {
+ ehci_advance_periodic_state(ehci);
+ }
+ }
+
+ ehci->last_run_usec += FRAME_TIMER_USEC;
+ }
+
+#if 0
+ if (skipped_frames) {
+ DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames);
+ }
+#endif
+
+ /* Async is not inside loop since it executes everything it can once
+ * called
+ */
+ if (ehci->pstate != EST_EXECUTING) {
+ ehci_advance_async_state(ehci);
+ }
+
+ qemu_mod_timer(ehci->frame_timer, expire_time);
+}
+
+static CPUReadMemoryFunc *ehci_readfn[3]={
+ ehci_mem_readb,
+ ehci_mem_readw,
+ ehci_mem_readl
+};
+
+static CPUWriteMemoryFunc *ehci_writefn[3]={
+ ehci_mem_writeb,
+ ehci_mem_writew,
+ ehci_mem_writel
+};
+
+static void ehci_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ EHCIState *s =(EHCIState *)pci_dev;
+
+ DPRINTF("ehci_map: region %d, addr %08" PRIx64 ", size %" PRId64 ", s->mem %08X\n",
+ region_num, addr, size, s->mem);
+ s->mem_base = addr;
+ cpu_register_physical_memory(addr, size, s->mem);
+}
+
+static int usb_ehci_initfn(PCIDevice *dev);
+
+static USBPortOps ehci_port_ops = {
+ .attach = ehci_attach,
+ .detach = ehci_detach,
+ .complete = ehci_async_complete_packet,
+};
+
+static PCIDeviceInfo ehci_info = {
+ .qdev.name = "usb-ehci",
+ .qdev.size = sizeof(EHCIState),
+ .init = usb_ehci_initfn,
+};
+
+static int usb_ehci_initfn(PCIDevice *dev)
+{
+ EHCIState *s = DO_UPCAST(EHCIState, dev, dev);
+ uint8_t *pci_conf = s->dev.config;
+ int i;
+
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82801D);
+ pci_set_byte(&pci_conf[PCI_REVISION_ID], 0x10);
+ pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20);
+ pci_config_set_class(pci_conf, PCI_CLASS_SERIAL_USB);
+ pci_set_byte(&pci_conf[PCI_HEADER_TYPE], PCI_HEADER_TYPE_NORMAL);
+
+ /* capabilities pointer */
+ pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00);
+ //pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50);
+
+ pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); // interrupt pin 3
+ pci_set_byte(&pci_conf[PCI_MIN_GNT], 0);
+ pci_set_byte(&pci_conf[PCI_MAX_LAT], 0);
+
+ // pci_conf[0x50] = 0x01; // power management caps
+
+ pci_set_byte(&pci_conf[0x60], 0x20); // spec release number (2.1.4)
+ pci_set_byte(&pci_conf[0x61], 0x20); // frame length adjustment (2.1.5)
+ pci_set_word(&pci_conf[0x62], 0x00); // port wake up capability (2.1.6)
+
+ pci_conf[0x64] = 0x00;
+ pci_conf[0x65] = 0x00;
+ pci_conf[0x66] = 0x00;
+ pci_conf[0x67] = 0x00;
+ pci_conf[0x68] = 0x01;
+ pci_conf[0x69] = 0x00;
+ pci_conf[0x6a] = 0x00;
+ pci_conf[0x6b] = 0x00; // USBLEGSUP
+ pci_conf[0x6c] = 0x00;
+ pci_conf[0x6d] = 0x00;
+ pci_conf[0x6e] = 0x00;
+ pci_conf[0x6f] = 0xc0; // USBLEFCTLSTS
+
+ // 2.2 host controller interface version
+ s->mmio[0x00] = (uint8_t) OPREGBASE;
+ s->mmio[0x01] = 0x00;
+ s->mmio[0x02] = 0x00;
+ s->mmio[0x03] = 0x01; // HC version
+ s->mmio[0x04] = NB_PORTS; // Number of downstream ports
+ s->mmio[0x05] = 0x00; // No companion ports at present
+ s->mmio[0x06] = 0x00;
+ s->mmio[0x07] = 0x00;
+ s->mmio[0x08] = 0x80; // We can cache whole frame, not 64-bit capable
+ s->mmio[0x09] = 0x68; // EECP
+ s->mmio[0x0a] = 0x00;
+ s->mmio[0x0b] = 0x00;
+
+ s->irq = s->dev.irq[3];
+
+ usb_bus_new(&s->bus, &s->dev.qdev);
+ for(i = 0; i < NB_PORTS; i++) {
+ usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops,
+ USB_SPEED_MASK_HIGH);
+ usb_port_location(&s->ports[i], NULL, i+1);
+ s->ports[i].dev = 0;
+ }
+
+ s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s);
+
+ qemu_register_reset(ehci_reset, s);
+
+ s->mem = cpu_register_io_memory(ehci_readfn, ehci_writefn, s,
+ DEVICE_LITTLE_ENDIAN);
+
+ pci_register_bar(&s->dev, 0, MMIO_SIZE, PCI_BASE_ADDRESS_SPACE_MEMORY,
+ ehci_map);
+
+ fprintf(stderr, "*** EHCI support is under development ***\n");
+
+ return 0;
+}
+
+static void ehci_register(void)
+{
+ pci_qdev_register(&ehci_info);
+}
+device_init(ehci_register);
+
+/*
+ * vim: expandtab ts=4
+ */
commit eb5e680ae5a72b999946e5618c501648367734a8
Author: Gerd Hoffmann <kraxel at redhat.com>
Date: Mon May 16 10:34:53 2011 +0200
usb: move cancel callback to USBDeviceInfo
Remove the cancel callback from the USBPacket struct, move it over
to USBDeviceInfo. Zap usb_defer_packet() which is obsolete now.
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 1064920..141da2c 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -315,9 +315,9 @@ static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
return ret;
}
-static void usb_msd_cancel_io(USBPacket *p, void *opaque)
+static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
{
- MSDState *s = opaque;
+ MSDState *s = DO_UPCAST(MSDState, dev, dev);
s->scsi_dev->info->cancel_io(s->scsi_dev, s->tag);
s->packet = NULL;
s->scsi_len = 0;
@@ -398,7 +398,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
}
if (s->usb_len) {
DPRINTF("Deferring packet %p\n", p);
- usb_defer_packet(p, usb_msd_cancel_io, s);
s->packet = p;
ret = USB_RET_ASYNC;
} else {
@@ -421,7 +420,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
if (s->data_len != 0 || len < 13)
goto fail;
/* Waiting for SCSI write to complete. */
- usb_defer_packet(p, usb_msd_cancel_io, s);
s->packet = p;
ret = USB_RET_ASYNC;
break;
@@ -455,7 +453,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
}
if (s->usb_len) {
DPRINTF("Deferring packet %p\n", p);
- usb_defer_packet(p, usb_msd_cancel_io, s);
s->packet = p;
ret = USB_RET_ASYNC;
} else {
@@ -604,6 +601,7 @@ static struct USBDeviceInfo msd_info = {
.usb_desc = &desc,
.init = usb_msd_initfn,
.handle_packet = usb_generic_handle_packet,
+ .cancel_packet = usb_msd_cancel_io,
.handle_attach = usb_desc_attach,
.handle_reset = usb_msd_handle_reset,
.handle_control = usb_msd_handle_control,
diff --git a/hw/usb.c b/hw/usb.c
index 8a9a7fc..4a39cbc 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -345,6 +345,6 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
void usb_cancel_packet(USBPacket * p)
{
assert(p->owner != NULL);
- p->cancel_cb(p, p->cancel_opaque);
+ p->owner->info->cancel_packet(p->owner, p);
p->owner = NULL;
}
diff --git a/hw/usb.h b/hw/usb.h
index 80e8e90..9882400 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -194,6 +194,11 @@ struct USBDeviceInfo {
int (*handle_packet)(USBDevice *dev, USBPacket *p);
/*
+ * Called when a packet is canceled.
+ */
+ void (*cancel_packet)(USBDevice *dev, USBPacket *p);
+
+ /*
* Called when device is destroyed.
*/
void (*handle_destroy)(USBDevice *dev);
@@ -263,24 +268,12 @@ struct USBPacket {
int len;
/* Internal use by the USB layer. */
USBDevice *owner;
- USBCallback *cancel_cb;
- void *cancel_opaque;
};
int usb_handle_packet(USBDevice *dev, USBPacket *p);
void usb_packet_complete(USBDevice *dev, USBPacket *p);
void usb_cancel_packet(USBPacket * p);
-/* Defer completion of a USB packet. The hadle_packet routine should then
- return USB_RET_ASYNC. Packets that complete immediately (before
- handle_packet returns) should not call this method. */
-static inline void usb_defer_packet(USBPacket *p, USBCallback *cancel,
- void * opaque)
-{
- p->cancel_cb = cancel;
- p->cancel_opaque = opaque;
-}
-
void usb_attach(USBPort *port, USBDevice *dev);
void usb_wakeup(USBDevice *dev);
int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
diff --git a/usb-linux.c b/usb-linux.c
index c7e96c3..baa6574 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -335,9 +335,9 @@ static void async_complete(void *opaque)
}
}
-static void async_cancel(USBPacket *p, void *opaque)
+static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
{
- USBHostDevice *s = opaque;
+ USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
AsyncURB *aurb;
QLIST_FOREACH(aurb, &s->aurbs, next) {
@@ -736,7 +736,6 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
}
}
- usb_defer_packet(p, async_cancel, s);
return USB_RET_ASYNC;
}
@@ -868,7 +867,6 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
}
}
- usb_defer_packet(p, async_cancel, s);
return USB_RET_ASYNC;
}
@@ -1197,6 +1195,7 @@ static struct USBDeviceInfo usb_host_dev_info = {
.qdev.size = sizeof(USBHostDevice),
.init = usb_host_initfn,
.handle_packet = usb_generic_handle_packet,
+ .cancel_packet = usb_host_async_cancel,
.handle_data = usb_host_handle_data,
.handle_control = usb_host_handle_control,
.handle_reset = usb_host_handle_reset,
commit 4ff658fb6c4f1cb7f771b16f808547e4f5767d02
Author: Gerd Hoffmann <kraxel at redhat.com>
Date: Thu May 12 13:48:13 2011 +0200
usb: keep track of packet owner.
Keep track of the device which owns the usb packet for async processing.
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/hw/usb.c b/hw/usb.c
index 966cb0f..8a9a7fc 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -313,6 +313,38 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
{
int ret;
+ assert(p->owner == NULL);
ret = dev->info->handle_packet(dev, p);
+ if (ret == USB_RET_ASYNC) {
+ if (p->owner == NULL) {
+ p->owner = dev;
+ } else {
+ /* We'll end up here when usb_handle_packet is called
+ * recursively due to a hub being in the chain. Nothing
+ * to do. Leave p->owner pointing to the device, not the
+ * hub. */;
+ }
+ }
return ret;
}
+
+/* Notify the controller that an async packet is complete. This should only
+ be called for packets previously deferred by returning USB_RET_ASYNC from
+ handle_packet. */
+void usb_packet_complete(USBDevice *dev, USBPacket *p)
+{
+ /* Note: p->owner != dev is possible in case dev is a hub */
+ assert(p->owner != NULL);
+ dev->port->ops->complete(dev, p);
+ p->owner = NULL;
+}
+
+/* Cancel an active packet. The packed must have been deferred by
+ returning USB_RET_ASYNC from handle_packet, and not yet
+ completed. */
+void usb_cancel_packet(USBPacket * p)
+{
+ assert(p->owner != NULL);
+ p->cancel_cb(p, p->cancel_opaque);
+ p->owner = NULL;
+}
diff --git a/hw/usb.h b/hw/usb.h
index 6889467..80e8e90 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -262,11 +262,14 @@ struct USBPacket {
uint8_t *data;
int len;
/* Internal use by the USB layer. */
+ USBDevice *owner;
USBCallback *cancel_cb;
void *cancel_opaque;
};
int usb_handle_packet(USBDevice *dev, USBPacket *p);
+void usb_packet_complete(USBDevice *dev, USBPacket *p);
+void usb_cancel_packet(USBPacket * p);
/* Defer completion of a USB packet. The hadle_packet routine should then
return USB_RET_ASYNC. Packets that complete immediately (before
@@ -278,21 +281,6 @@ static inline void usb_defer_packet(USBPacket *p, USBCallback *cancel,
p->cancel_opaque = opaque;
}
-/* Notify the controller that an async packet is complete. This should only
- be called for packets previously deferred with usb_defer_packet, and
- should never be called from within handle_packet. */
-static inline void usb_packet_complete(USBDevice *dev, USBPacket *p)
-{
- dev->port->ops->complete(dev, p);
-}
-
-/* Cancel an active packet. The packed must have been deferred with
- usb_defer_packet, and not yet completed. */
-static inline void usb_cancel_packet(USBPacket * p)
-{
- p->cancel_cb(p, p->cancel_opaque);
-}
-
void usb_attach(USBPort *port, USBDevice *dev);
void usb_wakeup(USBDevice *dev);
int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
commit 53aa8c0e2af473050fa765533a8d69f3450788ab
Author: Gerd Hoffmann <kraxel at redhat.com>
Date: Thu May 12 13:20:39 2011 +0200
usb: add usb_handle_packet
Add a usb_handle_packet function, put it into use everywhere.
Right now it just calls dev->info->handle_packet(), that will
change in future patches though.
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
index 477927b..6e2a358 100644
--- a/hw/usb-hub.c
+++ b/hw/usb-hub.c
@@ -495,7 +495,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p)
port = &s->ports[i];
dev = port->port.dev;
if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
- ret = dev->info->handle_packet(dev, p);
+ ret = usb_handle_packet(dev, p);
if (ret != USB_RET_NODEV) {
return ret;
}
diff --git a/hw/usb-musb.c b/hw/usb-musb.c
index 38986d3..6037193 100644
--- a/hw/usb-musb.c
+++ b/hw/usb-musb.c
@@ -601,7 +601,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
ep->packey[dir].dir = dir;
if (s->port.dev)
- ret = s->port.dev->info->handle_packet(s->port.dev, &ep->packey[dir].p);
+ ret = usb_handle_packet(s->port.dev, &ep->packey[dir].p);
else
ret = USB_RET_NODEV;
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c
index 32913eb..8b966f7 100644
--- a/hw/usb-ohci.c
+++ b/hw/usb-ohci.c
@@ -748,7 +748,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
ohci->usb_packet.data = ohci->usb_buf;
ohci->usb_packet.len = len;
- ret = dev->info->handle_packet(dev, &ohci->usb_packet);
+ ret = usb_handle_packet(dev, &ohci->usb_packet);
if (ret != USB_RET_NODEV)
break;
}
@@ -944,7 +944,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
ohci->usb_packet.data = ohci->usb_buf;
ohci->usb_packet.len = len;
- ret = dev->info->handle_packet(dev, &ohci->usb_packet);
+ ret = usb_handle_packet(dev, &ohci->usb_packet);
if (ret != USB_RET_NODEV)
break;
}
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
index 1e9c1e7..c0de05b 100644
--- a/hw/usb-uhci.c
+++ b/hw/usb-uhci.c
@@ -632,7 +632,7 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
USBDevice *dev = port->port.dev;
if (dev && (port->ctrl & UHCI_PORT_EN))
- ret = dev->info->handle_packet(dev, p);
+ ret = usb_handle_packet(dev, p);
}
DPRINTF("uhci: packet exit. ret %d len %d\n", ret, p->len);
diff --git a/hw/usb.c b/hw/usb.c
index 60027c6..966cb0f 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -297,9 +297,22 @@ int set_usb_string(uint8_t *buf, const char *str)
void usb_send_msg(USBDevice *dev, int msg)
{
USBPacket p;
+ int ret;
+
memset(&p, 0, sizeof(p));
p.pid = msg;
- dev->info->handle_packet(dev, &p);
-
+ ret = usb_handle_packet(dev, &p);
/* This _must_ be synchronous */
+ assert(ret != USB_RET_ASYNC);
+}
+
+/* Hand over a packet to a device for processing. Return value
+ USB_RET_ASYNC indicates the processing isn't finished yet, the
+ driver will call usb_packet_complete() when done processing it. */
+int usb_handle_packet(USBDevice *dev, USBPacket *p)
+{
+ int ret;
+
+ ret = dev->info->handle_packet(dev, p);
+ return ret;
}
diff --git a/hw/usb.h b/hw/usb.h
index c1d1014..6889467 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -266,6 +266,8 @@ struct USBPacket {
void *cancel_opaque;
};
+int usb_handle_packet(USBDevice *dev, USBPacket *p);
+
/* Defer completion of a USB packet. The hadle_packet routine should then
return USB_RET_ASYNC. Packets that complete immediately (before
handle_packet returns) should not call this method. */
commit ebd669a19f00d0ff8370e1edfb6232f50e42110d
Author: Gerd Hoffmann <kraxel at redhat.com>
Date: Mon May 16 14:25:59 2011 +0200
usb-storage: don't call usb_packet_complete twice
usb_msd_copy_data() may cause a recursive call to
usb_msd_command_complete() which in turn may complete
the packet, setting s->packet to NULL in case it does.
Recheck s->packet before calling usb_packet_complete()
to fix the double call.
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index c3a197a..1064920 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -253,7 +253,7 @@ static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
s->scsi_buf = s->scsi_dev->info->get_buf(s->scsi_dev, tag);
if (p) {
usb_msd_copy_data(s);
- if (s->usb_len == 0) {
+ if (s->packet && s->usb_len == 0) {
/* Set s->packet to NULL before calling usb_packet_complete
because another request may be issued before
usb_packet_complete returns. */
commit 6dfcdccb09b98dff758b31811d1433b11cc9aaa1
Author: Gerd Hoffmann <kraxel at redhat.com>
Date: Mon May 16 11:30:57 2011 +0200
usb-linux: fix max_packet_size for highspeed.
Calculate the max packet size correctly. Only bits 0..11 specify the size,
bits 11+12 specify the number of (highspeed) microframes the endpoint wants
to use.
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/usb-linux.c b/usb-linux.c
index 4edcdc4..c7e96c3 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -213,6 +213,22 @@ static int get_iso_buffer_used(USBHostDevice *s, int ep)
return s->endp_table[ep - 1].iso_buffer_used;
}
+static void set_max_packet_size(USBHostDevice *s, int ep, uint8_t *descriptor)
+{
+ int raw = descriptor[4] + (descriptor[5] << 8);
+ int size, microframes;
+
+ size = raw & 0x7ff;
+ switch ((raw >> 11) & 3) {
+ case 1: microframes = 2; break;
+ case 2: microframes = 3; break;
+ default: microframes = 1; break;
+ }
+ DPRINTF("husb: max packet size: 0x%x -> %d x %d\n",
+ raw, microframes, size);
+ s->endp_table[ep - 1].max_packet_size = size * microframes;
+}
+
static int get_max_packet_size(USBHostDevice *s, int ep)
{
return s->endp_table[ep - 1].max_packet_size;
@@ -1008,8 +1024,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
break;
case 0x01:
type = USBDEVFS_URB_TYPE_ISO;
- s->endp_table[(devep & 0xf) - 1].max_packet_size =
- descriptors[i + 4] + (descriptors[i + 5] << 8);
+ set_max_packet_size(s, (devep & 0xf), descriptors + i);
break;
case 0x02:
type = USBDEVFS_URB_TYPE_BULK;
commit 71138531d3b19a211d63170d78592513f14ae59b
Author: Gerd Hoffmann <kraxel at redhat.com>
Date: Mon May 16 10:21:51 2011 +0200
usb-linux: split large xfers
Add support for splitting large transfers into multiple smaller ones.
This is needed for the upcoming EHCI emulation which allows guests
to submit requests up to 20k in size. The linux kernel allows 16k
max size though.
Based on a patch from David Ahern, see
http://www.mail-archive.com/qemu-devel@nongnu.org/msg30337.html
Cc: David Ahern <daahern at cisco.com>
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/usb-linux.c b/usb-linux.c
index 5e9c5e4..4edcdc4 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -89,6 +89,9 @@ static int usb_fs_type;
#define ISO_URB_COUNT 3
#define INVALID_EP_TYPE 255
+/* devio.c limits single requests to 16k */
+#define MAX_USBFS_BUFFER_SIZE 16384
+
typedef struct AsyncURB AsyncURB;
struct endp_data {
@@ -229,6 +232,7 @@ struct AsyncURB
/* For regular async urbs */
USBPacket *packet;
+ int more; /* large transfer, more urbs follow */
/* For buffered iso handling */
int iso_frame_idx; /* -1 means in flight */
@@ -291,7 +295,7 @@ static void async_complete(void *opaque)
if (p) {
switch (aurb->urb.status) {
case 0:
- p->len = aurb->urb.actual_length;
+ p->len += aurb->urb.actual_length;
break;
case -EPIPE:
@@ -306,7 +310,7 @@ static void async_complete(void *opaque)
if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
usb_generic_async_ctrl_complete(&s->dev, p);
- } else {
+ } else if (!aurb->more) {
usb_packet_complete(&s->dev, p);
}
}
@@ -646,7 +650,8 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
struct usbdevfs_urb *urb;
AsyncURB *aurb;
- int ret;
+ int ret, rem;
+ uint8_t *pbuf;
uint8_t ep;
if (!is_valid(s, p->devep)) {
@@ -673,32 +678,45 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
}
- aurb = async_alloc(s);
- aurb->packet = p;
+ rem = p->len;
+ pbuf = p->data;
+ p->len = 0;
+ while (rem) {
+ aurb = async_alloc(s);
+ aurb->packet = p;
- urb = &aurb->urb;
+ urb = &aurb->urb;
+ urb->endpoint = ep;
+ urb->type = USBDEVFS_URB_TYPE_BULK;
+ urb->usercontext = s;
+ urb->buffer = pbuf;
- urb->endpoint = ep;
- urb->buffer = p->data;
- urb->buffer_length = p->len;
- urb->type = USBDEVFS_URB_TYPE_BULK;
- urb->usercontext = s;
+ if (rem > MAX_USBFS_BUFFER_SIZE) {
+ urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
+ aurb->more = 1;
+ } else {
+ urb->buffer_length = rem;
+ aurb->more = 0;
+ }
+ pbuf += urb->buffer_length;
+ rem -= urb->buffer_length;
- ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+ ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
- DPRINTF("husb: data submit. ep 0x%x len %u aurb %p\n",
- urb->endpoint, p->len, aurb);
+ DPRINTF("husb: data submit: ep 0x%x, len %u, more %d, packet %p, aurb %p\n",
+ urb->endpoint, urb->buffer_length, aurb->more, p, aurb);
- if (ret < 0) {
- DPRINTF("husb: submit failed. errno %d\n", errno);
- async_free(aurb);
+ if (ret < 0) {
+ DPRINTF("husb: submit failed. errno %d\n", errno);
+ async_free(aurb);
- switch(errno) {
- case ETIMEDOUT:
- return USB_RET_NAK;
- case EPIPE:
- default:
- return USB_RET_STALL;
+ switch(errno) {
+ case ETIMEDOUT:
+ return USB_RET_NAK;
+ case EPIPE:
+ default:
+ return USB_RET_STALL;
+ }
}
}
commit 227ebeb5353681b206a74db44530e60a46c24275
Author: Gerd Hoffmann <kraxel at redhat.com>
Date: Mon May 16 09:20:06 2011 +0200
usb-linux: walk async urb list in cancel
Lookup async urbs which are to be canceled using the linked list
instead of the direct opaque pointer. There are two reasons we
are doing that: First, to avoid the opaque poiner to the callback,
which is needed for upcoming cleanups. Second, because we might
need multiple urbs per request for highspeed support, so a single
opaque pointer doesn't cut it any more anyway.
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/usb-linux.c b/usb-linux.c
index 3213215..5e9c5e4 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -315,19 +315,25 @@ static void async_complete(void *opaque)
}
}
-static void async_cancel(USBPacket *unused, void *opaque)
+static void async_cancel(USBPacket *p, void *opaque)
{
- AsyncURB *aurb = opaque;
- USBHostDevice *s = aurb->hdev;
+ USBHostDevice *s = opaque;
+ AsyncURB *aurb;
- DPRINTF("husb: async cancel. aurb %p\n", aurb);
+ QLIST_FOREACH(aurb, &s->aurbs, next) {
+ if (p != aurb->packet) {
+ continue;
+ }
- /* Mark it as dead (see async_complete above) */
- aurb->packet = NULL;
+ DPRINTF("husb: async cancel: packet %p, aurb %p\n", p, aurb);
- int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
- if (r < 0) {
- DPRINTF("husb: async. discard urb failed errno %d\n", errno);
+ /* Mark it as dead (see async_complete above) */
+ aurb->packet = NULL;
+
+ int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
+ if (r < 0) {
+ DPRINTF("husb: async. discard urb failed errno %d\n", errno);
+ }
}
}
@@ -696,7 +702,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
}
}
- usb_defer_packet(p, async_cancel, aurb);
+ usb_defer_packet(p, async_cancel, s);
return USB_RET_ASYNC;
}
@@ -828,7 +834,7 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
}
}
- usb_defer_packet(p, async_cancel, aurb);
+ usb_defer_packet(p, async_cancel, s);
return USB_RET_ASYNC;
}
commit 7a8fc83f3469188ef03ea0c1ac9d2ff0dcc36637
Author: Gerd Hoffmann <kraxel at redhat.com>
Date: Mon May 16 09:13:05 2011 +0200
usb-linux: track aurbs in list
This patch adds code to track all async urbs in a linked list,
so we can find them without having to pass around a opaque
pointer to them. Prerequisite for the cleanups.
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/usb-linux.c b/usb-linux.c
index 55d914d..3213215 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -121,6 +121,7 @@ typedef struct USBHostDevice {
Notifier exit;
struct endp_data endp_table[MAX_ENDPOINTS];
+ QLIST_HEAD(, AsyncURB) aurbs;
/* Host side address */
int bus_num;
@@ -223,22 +224,27 @@ struct AsyncURB
{
struct usbdevfs_urb urb;
struct usbdevfs_iso_packet_desc isocpd[ISO_FRAME_DESC_PER_URB];
+ USBHostDevice *hdev;
+ QLIST_ENTRY(AsyncURB) next;
/* For regular async urbs */
USBPacket *packet;
- USBHostDevice *hdev;
/* For buffered iso handling */
int iso_frame_idx; /* -1 means in flight */
};
-static AsyncURB *async_alloc(void)
+static AsyncURB *async_alloc(USBHostDevice *s)
{
- return (AsyncURB *) qemu_mallocz(sizeof(AsyncURB));
+ AsyncURB *aurb = qemu_mallocz(sizeof(AsyncURB));
+ aurb->hdev = s;
+ QLIST_INSERT_HEAD(&s->aurbs, aurb, next);
+ return aurb;
}
static void async_free(AsyncURB *aurb)
{
+ QLIST_REMOVE(aurb, next);
qemu_free(aurb);
}
@@ -661,8 +667,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
}
- aurb = async_alloc();
- aurb->hdev = s;
+ aurb = async_alloc(s);
aurb->packet = p;
urb = &aurb->urb;
@@ -787,8 +792,7 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
return USB_RET_STALL;
}
- aurb = async_alloc();
- aurb->hdev = s;
+ aurb = async_alloc(s);
aurb->packet = p;
/*
commit 9056a2972a9e935198e518c37365513a199ae3d0
Author: Gerd Hoffmann <kraxel at redhat.com>
Date: Tue May 10 12:07:42 2011 +0200
usb-linux: add hostport property
This patch adds a hostport property which allows to specify the host usb
devices to pass through by bus number and physical port. This means you
can basically hand over one (or more) of the usb plugs on your host to
the guest and whatever device is plugged in there will show up in the
guest.
Usage:
-device usb-host,hostbus=1,hostport=1
You can figure the port numbers by plugging in some usb device, then
find it in "info usbhost" and pick bus and port specified there.
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/usb-linux.c b/usb-linux.c
index 2c6e249..55d914d 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -104,6 +104,7 @@ struct endp_data {
struct USBAutoFilter {
uint32_t bus_num;
uint32_t addr;
+ char *port;
uint32_t vendor_id;
uint32_t product_id;
};
@@ -1162,6 +1163,7 @@ static struct USBDeviceInfo usb_host_dev_info = {
.qdev.props = (Property[]) {
DEFINE_PROP_UINT32("hostbus", USBHostDevice, match.bus_num, 0),
DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr, 0),
+ DEFINE_PROP_STRING("hostport", USBHostDevice, match.port),
DEFINE_PROP_HEX32("vendorid", USBHostDevice, match.vendor_id, 0),
DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
DEFINE_PROP_END_OF_LIST(),
@@ -1580,6 +1582,9 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, char *port,
if (f->addr > 0 && f->addr != addr) {
continue;
}
+ if (f->port != NULL && (port == NULL || strcmp(f->port, port) != 0)) {
+ continue;
+ }
if (f->vendor_id > 0 && f->vendor_id != vendor_id) {
continue;
@@ -1805,7 +1810,7 @@ void usb_host_info(Monitor *mon)
dec2str(f->addr, addr, sizeof(addr));
hex2str(f->vendor_id, vid, sizeof(vid));
hex2str(f->product_id, pid, sizeof(pid));
- monitor_printf(mon, " Device %s.%s ID %s:%s\n",
- bus, addr, vid, pid);
+ monitor_printf(mon, " Bus %s, Addr %s, Port %s, ID %s:%s\n",
+ bus, addr, f->port ? f->port : "*", vid, pid);
}
}
commit 5557d820817028603d8a1446b0ddb62f7d267510
Author: Gerd Hoffmann <kraxel at redhat.com>
Date: Tue May 10 11:43:57 2011 +0200
usb-linux: fix device path aka physical port handling
The device path isn't just a number. It specifies the physical port
the device is connected to and in case the device is connected via
usb hub you'll have two numbers there, like this: "5.1". The first
specifies the root port where the hub is plugged into, the second
specifies the port number of the hub where the device is plugged in.
With multiple hubs chained the string can become longer.
This patch renames devpath to port and makes it a string. It also
adapts the sysfs parsing code accordingly. The parser code is also more
strict now and skips the root hubs (which can't be assigned anyway).
The "info usbhost" monitor command now prints bus number, (os-assigned)
device address and physical port for each device.
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/usb-linux.c b/usb-linux.c
index 84d3a8b..2c6e249 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -54,7 +54,7 @@ struct usb_ctrltransfer {
void *data;
};
-typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath,
+typedef int USBScanFunc(void *opaque, int bus_num, int addr, char *port,
int class_id, int vendor_id, int product_id,
const char *product_name, int speed);
@@ -71,6 +71,7 @@ typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath,
#define USBPROCBUS_PATH "/proc/bus/usb"
#define PRODUCT_NAME_SZ 32
#define MAX_ENDPOINTS 15
+#define MAX_PORTLEN 16
#define USBDEVBUS_PATH "/dev/bus/usb"
#define USBSYSBUS_PATH "/sys/bus/usb"
@@ -123,7 +124,7 @@ typedef struct USBHostDevice {
/* Host side address */
int bus_num;
int addr;
- int devpath;
+ char port[MAX_PORTLEN];
struct USBAutoFilter match;
QTAILQ_ENTRY(USBHostDevice) next;
@@ -836,7 +837,7 @@ static int usb_linux_get_configuration(USBHostDevice *s)
char device_name[32], line[1024];
int configuration;
- sprintf(device_name, "%d-%d", s->bus_num, s->devpath);
+ sprintf(device_name, "%d-%s", s->bus_num, s->port);
if (!usb_host_read_file(line, sizeof(line), "bConfigurationValue",
device_name)) {
@@ -882,7 +883,7 @@ static uint8_t usb_linux_get_alt_setting(USBHostDevice *s,
char device_name[64], line[1024];
int alt_setting;
- sprintf(device_name, "%d-%d:%d.%d", s->bus_num, s->devpath,
+ sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port,
(int)configuration, (int)interface);
if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting",
@@ -1001,7 +1002,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
}
static int usb_host_open(USBHostDevice *dev, int bus_num,
- int addr, int devpath, const char *prod_name)
+ int addr, char *port, const char *prod_name)
{
int fd = -1, ret;
struct usbdevfs_connectinfo ci;
@@ -1027,7 +1028,7 @@ static int usb_host_open(USBHostDevice *dev, int bus_num,
dev->bus_num = bus_num;
dev->addr = addr;
- dev->devpath = devpath;
+ strcpy(dev->port, port);
dev->fd = fd;
/* read the device description */
@@ -1401,8 +1402,9 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
{
DIR *dir = NULL;
char line[1024];
- int bus_num, addr, devpath, speed, class_id, product_id, vendor_id;
+ int bus_num, addr, speed, class_id, product_id, vendor_id;
int ret = 0;
+ char port[MAX_PORTLEN];
char product_name[512];
struct dirent *de;
@@ -1414,12 +1416,8 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
while ((de = readdir(dir))) {
if (de->d_name[0] != '.' && !strchr(de->d_name, ':')) {
- char *tmpstr = de->d_name;
- if (!strncmp(de->d_name, "usb", 3)) {
- tmpstr += 3;
- }
- if (sscanf(tmpstr, "%d-%d", &bus_num, &devpath) < 1) {
- goto the_end;
+ if (sscanf(de->d_name, "%d-%7[0-9.]", &bus_num, port) < 2) {
+ continue;
}
if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) {
@@ -1471,7 +1469,7 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
speed = USB_SPEED_FULL;
}
- ret = func(opaque, bus_num, addr, devpath, class_id, vendor_id,
+ ret = func(opaque, bus_num, addr, port, class_id, vendor_id,
product_id, product_name, speed);
if (ret) {
goto the_end;
@@ -1562,7 +1560,7 @@ static int usb_host_scan(void *opaque, USBScanFunc *func)
static QEMUTimer *usb_auto_timer;
-static int usb_host_auto_scan(void *opaque, int bus_num, int addr, int devpath,
+static int usb_host_auto_scan(void *opaque, int bus_num, int addr, char *port,
int class_id, int vendor_id, int product_id,
const char *product_name, int speed)
{
@@ -1598,7 +1596,7 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, int devpath,
}
DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr);
- usb_host_open(s, bus_num, addr, devpath, product_name);
+ usb_host_open(s, bus_num, addr, port, product_name);
}
return 0;
@@ -1720,8 +1718,8 @@ static const char *usb_class_str(uint8_t class)
return p->class_name;
}
-static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id,
- int vendor_id, int product_id,
+static void usb_info_device(Monitor *mon, int bus_num, int addr, char *port,
+ int class_id, int vendor_id, int product_id,
const char *product_name,
int speed)
{
@@ -1742,8 +1740,8 @@ static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id,
break;
}
- monitor_printf(mon, " Device %d.%d, speed %s Mb/s\n",
- bus_num, addr, speed_str);
+ monitor_printf(mon, " Bus %d, Addr %d, Port %s, Speed %s Mb/s\n",
+ bus_num, addr, port, speed_str);
class_str = usb_class_str(class_id);
if (class_str) {
monitor_printf(mon, " %s:", class_str);
@@ -1758,14 +1756,14 @@ static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id,
}
static int usb_host_info_device(void *opaque, int bus_num, int addr,
- int devpath, int class_id,
+ char *path, int class_id,
int vendor_id, int product_id,
const char *product_name,
int speed)
{
Monitor *mon = opaque;
- usb_info_device(mon, bus_num, addr, class_id, vendor_id, product_id,
+ usb_info_device(mon, bus_num, addr, path, class_id, vendor_id, product_id,
product_name, speed);
return 0;
}
commit 50b7963e72da6c31c2bebd435aeefd2966cd94ee
Author: Hans de Goede <hdegoede at redhat.com>
Date: Wed Feb 2 17:36:29 2011 +0100
usb-linux: use usb_generic_handle_packet()
Make the linux usb host passthrough code use the usb_generic_handle_packet()
function, rather then the curent DYI code. This removes 200 lines of almost
identical code.
Signed-off-by: Hans de Goede <hdegoede at redhat.com>
diff --git a/hw/usb.c b/hw/usb.c
index f503b7a..60027c6 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -63,9 +63,10 @@ void usb_wakeup(USBDevice *dev)
protocol)
*/
-#define SETUP_STATE_IDLE 0
-#define SETUP_STATE_DATA 1
-#define SETUP_STATE_ACK 2
+#define SETUP_STATE_IDLE 0
+#define SETUP_STATE_SETUP 1
+#define SETUP_STATE_DATA 2
+#define SETUP_STATE_ACK 3
static int do_token_setup(USBDevice *s, USBPacket *p)
{
@@ -86,6 +87,10 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
if (s->setup_buf[0] & USB_DIR_IN) {
ret = s->info->handle_control(s, p, request, value, index,
s->setup_len, s->data_buf);
+ if (ret == USB_RET_ASYNC) {
+ s->setup_state = SETUP_STATE_SETUP;
+ return USB_RET_ASYNC;
+ }
if (ret < 0)
return ret;
@@ -241,6 +246,36 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
}
}
+/* ctrl complete function for devices which use usb_generic_handle_packet and
+ may return USB_RET_ASYNC from their handle_control callback. Device code
+ which does this *must* call this function instead of the normal
+ usb_packet_complete to complete their async control packets. */
+void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
+{
+ if (p->len < 0) {
+ s->setup_state = SETUP_STATE_IDLE;
+ }
+
+ switch (s->setup_state) {
+ case SETUP_STATE_SETUP:
+ if (p->len < s->setup_len) {
+ s->setup_len = p->len;
+ }
+ s->setup_state = SETUP_STATE_DATA;
+ p->len = 8;
+ break;
+
+ case SETUP_STATE_ACK:
+ s->setup_state = SETUP_STATE_IDLE;
+ p->len = 0;
+ break;
+
+ default:
+ break;
+ }
+ usb_packet_complete(s, p);
+}
+
/* XXX: fix overflow */
int set_usb_string(uint8_t *buf, const char *str)
{
diff --git a/hw/usb.h b/hw/usb.h
index b52fa34..c1d1014 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -294,6 +294,7 @@ static inline void usb_cancel_packet(USBPacket * p)
void usb_attach(USBPort *port, USBDevice *dev);
void usb_wakeup(USBDevice *dev);
int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
+void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
int set_usb_string(uint8_t *buf, const char *str);
void usb_send_msg(USBDevice *dev, int msg);
diff --git a/usb-linux.c b/usb-linux.c
index 0ef1d26..84d3a8b 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -54,14 +54,6 @@ struct usb_ctrltransfer {
void *data;
};
-struct usb_ctrlrequest {
- uint8_t bRequestType;
- uint8_t bRequest;
- uint16_t wValue;
- uint16_t wIndex;
- uint16_t wLength;
-};
-
typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath,
int class_id, int vendor_id, int product_id,
const char *product_name, int speed);
@@ -108,26 +100,6 @@ struct endp_data {
int max_packet_size;
};
-enum {
- CTRL_STATE_IDLE = 0,
- CTRL_STATE_SETUP,
- CTRL_STATE_DATA,
- CTRL_STATE_ACK
-};
-
-/*
- * Control transfer state.
- * Note that 'buffer' _must_ follow 'req' field because
- * we need contiguous buffer when we submit control URB.
- */
-struct ctrl_struct {
- uint16_t len;
- uint16_t offset;
- uint8_t state;
- struct usb_ctrlrequest req;
- uint8_t buffer[8192];
-};
-
struct USBAutoFilter {
uint32_t bus_num;
uint32_t addr;
@@ -146,7 +118,6 @@ typedef struct USBHostDevice {
int closing;
Notifier exit;
- struct ctrl_struct ctrl;
struct endp_data endp_table[MAX_ENDPOINTS];
/* Host side address */
@@ -269,26 +240,6 @@ static void async_free(AsyncURB *aurb)
qemu_free(aurb);
}
-static void async_complete_ctrl(USBHostDevice *s, USBPacket *p)
-{
- switch(s->ctrl.state) {
- case CTRL_STATE_SETUP:
- if (p->len < s->ctrl.len)
- s->ctrl.len = p->len;
- s->ctrl.state = CTRL_STATE_DATA;
- p->len = 8;
- break;
-
- case CTRL_STATE_ACK:
- s->ctrl.state = CTRL_STATE_IDLE;
- p->len = 0;
- break;
-
- default:
- break;
- }
-}
-
static void async_complete(void *opaque)
{
USBHostDevice *s = opaque;
@@ -333,9 +284,6 @@ static void async_complete(void *opaque)
switch (aurb->urb.status) {
case 0:
p->len = aurb->urb.actual_length;
- if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
- async_complete_ctrl(s, p);
- }
break;
case -EPIPE:
@@ -348,7 +296,11 @@ static void async_complete(void *opaque)
break;
}
- usb_packet_complete(&s->dev, p);
+ if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
+ usb_generic_async_ctrl_complete(&s->dev, p);
+ } else {
+ usb_packet_complete(&s->dev, p);
+ }
}
async_free(aurb);
@@ -675,8 +627,9 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
return len;
}
-static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
+static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
{
+ USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
struct usbdevfs_urb *urb;
AsyncURB *aurb;
int ret;
@@ -796,45 +749,39 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
return 0;
}
-static int usb_host_handle_control(USBHostDevice *s, USBPacket *p)
+static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data)
{
+ USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
struct usbdevfs_urb *urb;
AsyncURB *aurb;
- int ret, value, index;
- int buffer_len;
+ int ret;
/*
* Process certain standard device requests.
* These are infrequent and are processed synchronously.
*/
- value = le16_to_cpu(s->ctrl.req.wValue);
- index = le16_to_cpu(s->ctrl.req.wIndex);
+ /* Note request is (bRequestType << 8) | bRequest */
DPRINTF("husb: ctrl type 0x%x req 0x%x val 0x%x index %u len %u\n",
- s->ctrl.req.bRequestType, s->ctrl.req.bRequest, value, index,
- s->ctrl.len);
+ request >> 8, request & 0xff, value, index, length);
- if (s->ctrl.req.bRequestType == 0) {
- switch (s->ctrl.req.bRequest) {
- case USB_REQ_SET_ADDRESS:
- return usb_host_set_address(s, value);
+ switch (request) {
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ return usb_host_set_address(s, value);
- case USB_REQ_SET_CONFIGURATION:
- return usb_host_set_config(s, value & 0xff);
- }
- }
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ return usb_host_set_config(s, value & 0xff);
- if (s->ctrl.req.bRequestType == 1 &&
- s->ctrl.req.bRequest == USB_REQ_SET_INTERFACE) {
+ case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
return usb_host_set_interface(s, index, value);
}
/* The rest are asynchronous */
- buffer_len = 8 + s->ctrl.len;
- if (buffer_len > sizeof(s->ctrl.buffer)) {
- fprintf(stderr, "husb: ctrl buffer too small (%u > %zu)\n",
- buffer_len, sizeof(s->ctrl.buffer));
+ if (length > sizeof(dev->data_buf)) {
+ fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n",
+ length, sizeof(dev->data_buf));
return USB_RET_STALL;
}
@@ -853,8 +800,8 @@ static int usb_host_handle_control(USBHostDevice *s, USBPacket *p)
urb->type = USBDEVFS_URB_TYPE_CONTROL;
urb->endpoint = p->devep;
- urb->buffer = &s->ctrl.req;
- urb->buffer_length = buffer_len;
+ urb->buffer = &dev->setup_buf;
+ urb->buffer_length = length + 8;
urb->usercontext = s;
@@ -879,170 +826,6 @@ static int usb_host_handle_control(USBHostDevice *s, USBPacket *p)
return USB_RET_ASYNC;
}
-static int do_token_setup(USBDevice *dev, USBPacket *p)
-{
- USBHostDevice *s = (USBHostDevice *) dev;
- int ret = 0;
-
- if (p->len != 8) {
- return USB_RET_STALL;
- }
-
- memcpy(&s->ctrl.req, p->data, 8);
- s->ctrl.len = le16_to_cpu(s->ctrl.req.wLength);
- s->ctrl.offset = 0;
- s->ctrl.state = CTRL_STATE_SETUP;
-
- if (s->ctrl.req.bRequestType & USB_DIR_IN) {
- ret = usb_host_handle_control(s, p);
- if (ret < 0) {
- return ret;
- }
-
- if (ret < s->ctrl.len) {
- s->ctrl.len = ret;
- }
- s->ctrl.state = CTRL_STATE_DATA;
- } else {
- if (s->ctrl.len == 0) {
- s->ctrl.state = CTRL_STATE_ACK;
- } else {
- s->ctrl.state = CTRL_STATE_DATA;
- }
- }
-
- return ret;
-}
-
-static int do_token_in(USBDevice *dev, USBPacket *p)
-{
- USBHostDevice *s = (USBHostDevice *) dev;
- int ret = 0;
-
- if (p->devep != 0) {
- return usb_host_handle_data(s, p);
- }
-
- switch(s->ctrl.state) {
- case CTRL_STATE_ACK:
- if (!(s->ctrl.req.bRequestType & USB_DIR_IN)) {
- ret = usb_host_handle_control(s, p);
- if (ret == USB_RET_ASYNC) {
- return USB_RET_ASYNC;
- }
- s->ctrl.state = CTRL_STATE_IDLE;
- return ret > 0 ? 0 : ret;
- }
-
- return 0;
-
- case CTRL_STATE_DATA:
- if (s->ctrl.req.bRequestType & USB_DIR_IN) {
- int len = s->ctrl.len - s->ctrl.offset;
- if (len > p->len) {
- len = p->len;
- }
- memcpy(p->data, s->ctrl.buffer + s->ctrl.offset, len);
- s->ctrl.offset += len;
- if (s->ctrl.offset >= s->ctrl.len) {
- s->ctrl.state = CTRL_STATE_ACK;
- }
- return len;
- }
-
- s->ctrl.state = CTRL_STATE_IDLE;
- return USB_RET_STALL;
-
- default:
- return USB_RET_STALL;
- }
-}
-
-static int do_token_out(USBDevice *dev, USBPacket *p)
-{
- USBHostDevice *s = (USBHostDevice *) dev;
-
- if (p->devep != 0) {
- return usb_host_handle_data(s, p);
- }
-
- switch(s->ctrl.state) {
- case CTRL_STATE_ACK:
- if (s->ctrl.req.bRequestType & USB_DIR_IN) {
- s->ctrl.state = CTRL_STATE_IDLE;
- /* transfer OK */
- } else {
- /* ignore additional output */
- }
- return 0;
-
- case CTRL_STATE_DATA:
- if (!(s->ctrl.req.bRequestType & USB_DIR_IN)) {
- int len = s->ctrl.len - s->ctrl.offset;
- if (len > p->len) {
- len = p->len;
- }
- memcpy(s->ctrl.buffer + s->ctrl.offset, p->data, len);
- s->ctrl.offset += len;
- if (s->ctrl.offset >= s->ctrl.len) {
- s->ctrl.state = CTRL_STATE_ACK;
- }
- return len;
- }
-
- s->ctrl.state = CTRL_STATE_IDLE;
- return USB_RET_STALL;
-
- default:
- return USB_RET_STALL;
- }
-}
-
-/*
- * Packet handler.
- * Called by the HC (host controller).
- *
- * Returns length of the transaction or one of the USB_RET_XXX codes.
- */
-static int usb_host_handle_packet(USBDevice *s, USBPacket *p)
-{
- switch(p->pid) {
- case USB_MSG_ATTACH:
- s->state = USB_STATE_ATTACHED;
- return 0;
-
- case USB_MSG_DETACH:
- s->state = USB_STATE_NOTATTACHED;
- return 0;
-
- case USB_MSG_RESET:
- s->remote_wakeup = 0;
- s->addr = 0;
- s->state = USB_STATE_DEFAULT;
- s->info->handle_reset(s);
- return 0;
- }
-
- /* Rest of the PIDs must match our address */
- if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr) {
- return USB_RET_NODEV;
- }
-
- switch (p->pid) {
- case USB_TOKEN_SETUP:
- return do_token_setup(s, p);
-
- case USB_TOKEN_IN:
- return do_token_in(s, p);
-
- case USB_TOKEN_OUT:
- return do_token_out(s, p);
-
- default:
- return USB_RET_STALL;
- }
-}
-
static int usb_linux_get_configuration(USBHostDevice *s)
{
uint8_t configuration;
@@ -1368,7 +1151,9 @@ static struct USBDeviceInfo usb_host_dev_info = {
.qdev.name = "usb-host",
.qdev.size = sizeof(USBHostDevice),
.init = usb_host_initfn,
- .handle_packet = usb_host_handle_packet,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_data = usb_host_handle_data,
+ .handle_control = usb_host_handle_control,
.handle_reset = usb_host_handle_reset,
.handle_destroy = usb_host_handle_destroy,
.usbdevice_name = "host",
commit 007fd62f4d3959f2a61abe61a34a54c9f99560b0
Author: Hans de Goede <hdegoede at redhat.com>
Date: Wed Feb 2 16:33:13 2011 +0100
usb: Pass the packet to the device's handle_control callback
This allows using the generic usb_generic_handle_packet function from
device code which does ASYNC control requests (such as the linux host
pass through code).
Signed-off-by: Hans de Goede <hdegoede at redhat.com>
diff --git a/hw/bt-hid.c b/hw/bt-hid.c
index abdfd35..09120af 100644
--- a/hw/bt-hid.c
+++ b/hw/bt-hid.c
@@ -323,7 +323,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s,
break;
}
s->proto = parameter;
- s->usbdev->info->handle_control(s->usbdev, SET_PROTOCOL, s->proto, 0, 0,
+ s->usbdev->info->handle_control(s->usbdev, NULL, SET_PROTOCOL, s->proto, 0, 0,
NULL);
ret = BT_HS_SUCCESSFUL;
break;
@@ -333,7 +333,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s,
ret = BT_HS_ERR_INVALID_PARAMETER;
break;
}
- s->usbdev->info->handle_control(s->usbdev, GET_IDLE, 0, 0, 1,
+ s->usbdev->info->handle_control(s->usbdev, NULL, GET_IDLE, 0, 0, 1,
s->control->sdu_out(s->control, 1));
s->control->sdu_submit(s->control);
break;
@@ -346,7 +346,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s,
/* We don't need to know about the Idle Rate here really,
* so just pass it on to the device. */
- ret = s->usbdev->info->handle_control(s->usbdev,
+ ret = s->usbdev->info->handle_control(s->usbdev, NULL,
SET_IDLE, data[1], 0, 0, NULL) ?
BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER;
/* XXX: Does this generate a handshake? */
diff --git a/hw/usb-bt.c b/hw/usb-bt.c
index 22e6845..baae487 100644
--- a/hw/usb-bt.c
+++ b/hw/usb-bt.c
@@ -372,13 +372,13 @@ static void usb_bt_handle_reset(USBDevice *dev)
s->altsetting = 0;
}
-static int usb_bt_handle_control(USBDevice *dev, int request, int value,
- int index, int length, uint8_t *data)
+static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data)
{
struct USBBtState *s = (struct USBBtState *) dev->opaque;
int ret;
- ret = usb_desc_handle_control(dev, request, value, index, length, data);
+ ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) {
switch (request) {
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c
index 079b4a2..5b6878b 100644
--- a/hw/usb-ccid.c
+++ b/hw/usb-ccid.c
@@ -602,8 +602,8 @@ static void ccid_handle_reset(USBDevice *dev)
ccid_reset(s);
}
-static int ccid_handle_control(USBDevice *dev, int request, int value,
- int index, int length, uint8_t *data)
+static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
+ int value, int index, int length, uint8_t *data)
{
USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
int ret = 0;
diff --git a/hw/usb-desc.c b/hw/usb-desc.c
index 8367c45..e4a4680 100644
--- a/hw/usb-desc.c
+++ b/hw/usb-desc.c
@@ -390,8 +390,8 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
return ret;
}
-int usb_desc_handle_control(USBDevice *dev, int request, int value,
- int index, int length, uint8_t *data)
+int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data)
{
const USBDesc *desc = dev->info->usb_desc;
int i, ret = -1;
diff --git a/hw/usb-desc.h b/hw/usb-desc.h
index a612515..9d7ed59 100644
--- a/hw/usb-desc.h
+++ b/hw/usb-desc.h
@@ -106,7 +106,7 @@ void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str);
const char *usb_desc_get_string(USBDevice *dev, uint8_t index);
int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len);
int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len);
-int usb_desc_handle_control(USBDevice *dev, int request, int value,
- int index, int length, uint8_t *data);
+int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data);
#endif /* QEMU_HW_USB_DESC_H */
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
index bf59a7d..53b261c 100644
--- a/hw/usb-hid.c
+++ b/hw/usb-hid.c
@@ -727,13 +727,13 @@ static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime)
s->next_idle_clock = curtime + (get_ticks_per_sec() * s->idle * 4) / 1000;
}
-static int usb_hid_handle_control(USBDevice *dev, int request, int value,
- int index, int length, uint8_t *data)
+static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data)
{
USBHIDState *s = (USBHIDState *)dev;
int ret;
- ret = usb_desc_handle_control(dev, request, value, index, length, data);
+ ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) {
return ret;
}
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
index 7c1f159..477927b 100644
--- a/hw/usb-hub.c
+++ b/hw/usb-hub.c
@@ -285,13 +285,13 @@ static void usb_hub_handle_reset(USBDevice *dev)
/* XXX: do it */
}
-static int usb_hub_handle_control(USBDevice *dev, int request, int value,
- int index, int length, uint8_t *data)
+static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data)
{
USBHubState *s = (USBHubState *)dev;
int ret;
- ret = usb_desc_handle_control(dev, request, value, index, length, data);
+ ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) {
return ret;
}
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 040ea7a..c3a197a 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -272,13 +272,13 @@ static void usb_msd_handle_reset(USBDevice *dev)
s->mode = USB_MSDM_CBW;
}
-static int usb_msd_handle_control(USBDevice *dev, int request, int value,
- int index, int length, uint8_t *data)
+static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data)
{
MSDState *s = (MSDState *)dev;
int ret;
- ret = usb_desc_handle_control(dev, request, value, index, length, data);
+ ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) {
return ret;
}
diff --git a/hw/usb-net.c b/hw/usb-net.c
index bf51bb3..9be709f 100644
--- a/hw/usb-net.c
+++ b/hw/usb-net.c
@@ -1042,13 +1042,13 @@ static void usb_net_handle_reset(USBDevice *dev)
{
}
-static int usb_net_handle_control(USBDevice *dev, int request, int value,
- int index, int length, uint8_t *data)
+static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data)
{
USBNetState *s = (USBNetState *) dev;
int ret;
- ret = usb_desc_handle_control(dev, request, value, index, length, data);
+ ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) {
return ret;
}
diff --git a/hw/usb-serial.c b/hw/usb-serial.c
index 48ea0d8..59cb0fb 100644
--- a/hw/usb-serial.c
+++ b/hw/usb-serial.c
@@ -219,14 +219,14 @@ static uint8_t usb_get_modem_lines(USBSerialState *s)
return ret;
}
-static int usb_serial_handle_control(USBDevice *dev, int request, int value,
- int index, int length, uint8_t *data)
+static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data)
{
USBSerialState *s = (USBSerialState *)dev;
int ret;
DPRINTF("got control %x, value %x\n",request, value);
- ret = usb_desc_handle_control(dev, request, value, index, length, data);
+ ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) {
return ret;
}
diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c
index 57041a1..9d348e1 100644
--- a/hw/usb-wacom.c
+++ b/hw/usb-wacom.c
@@ -250,13 +250,13 @@ static void usb_wacom_handle_reset(USBDevice *dev)
s->mode = WACOM_MODE_HID;
}
-static int usb_wacom_handle_control(USBDevice *dev, int request, int value,
- int index, int length, uint8_t *data)
+static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
+ int request, int value, int index, int length, uint8_t *data)
{
USBWacomState *s = (USBWacomState *) dev;
int ret;
- ret = usb_desc_handle_control(dev, request, value, index, length, data);
+ ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) {
return ret;
}
diff --git a/hw/usb.c b/hw/usb.c
index d8c0a75..f503b7a 100644
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -82,9 +82,9 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
request = (s->setup_buf[0] << 8) | s->setup_buf[1];
value = (s->setup_buf[3] << 8) | s->setup_buf[2];
index = (s->setup_buf[5] << 8) | s->setup_buf[4];
-
+
if (s->setup_buf[0] & USB_DIR_IN) {
- ret = s->info->handle_control(s, request, value, index,
+ ret = s->info->handle_control(s, p, request, value, index,
s->setup_len, s->data_buf);
if (ret < 0)
return ret;
@@ -123,9 +123,12 @@ static int do_token_in(USBDevice *s, USBPacket *p)
switch(s->setup_state) {
case SETUP_STATE_ACK:
if (!(s->setup_buf[0] & USB_DIR_IN)) {
- s->setup_state = SETUP_STATE_IDLE;
- ret = s->info->handle_control(s, request, value, index,
+ ret = s->info->handle_control(s, p, request, value, index,
s->setup_len, s->data_buf);
+ if (ret == USB_RET_ASYNC) {
+ return USB_RET_ASYNC;
+ }
+ s->setup_state = SETUP_STATE_IDLE;
if (ret > 0)
return 0;
return ret;
diff --git a/hw/usb.h b/hw/usb.h
index e0961ac..b52fa34 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -214,7 +214,7 @@ struct USBDeviceInfo {
*
* Returns length or one of the USB_RET_ codes.
*/
- int (*handle_control)(USBDevice *dev, int request, int value,
+ int (*handle_control)(USBDevice *dev, USBPacket *p, int request, int value,
int index, int length, uint8_t *data);
/*
diff --git a/usb-bsd.c b/usb-bsd.c
index 50ccd48..9bab6e3 100644
--- a/usb-bsd.c
+++ b/usb-bsd.c
@@ -126,6 +126,7 @@ static void usb_host_handle_reset(USBDevice *dev)
* and return appropriate response
*/
static int usb_host_handle_control(USBDevice *dev,
+ USBPacket *p,
int request,
int value,
int index,
commit 8656954aedbd9995e68e998df734a849f8e63ade
Author: Jan Vesely <jano.vesely at gmail.com>
Date: Mon May 9 12:16:50 2011 +0200
Bug #757654: UHCI fails to signal stall response patch
UHCI host controller status register indicates error and
an interrupt is triggered on BABBLE and STALL errors.
Signed-off-by: Jan Vesely <jano.vesely at gmail.com>
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
index a65e0b3..1e9c1e7 100644
--- a/hw/usb-uhci.c
+++ b/hw/usb-uhci.c
@@ -702,11 +702,15 @@ out:
case USB_RET_STALL:
td->ctrl |= TD_CTRL_STALL;
td->ctrl &= ~TD_CTRL_ACTIVE;
+ s->status |= UHCI_STS_USBERR;
+ uhci_update_irq(s);
return 1;
case USB_RET_BABBLE:
td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
td->ctrl &= ~TD_CTRL_ACTIVE;
+ s->status |= UHCI_STS_USBERR;
+ uhci_update_irq(s);
/* frame interrupted */
return -1;
commit 6e625fc70410d76f2fc0d31185a96cf667076f8b
Author: Brad Hards <bradh at frogmouth.net>
Date: Sun Apr 3 15:33:21 2011 +1000
usb: add support for "grouped" interfaces and the Interface Association Descriptor
This is used for some devices that have multiple interfaces that form a logic
device. An example is Video Class, which has a Control interface and a
Streaming interface. There can be additional interfaces on the same (physical)
devices (e.g. a microphone), and Interface Association Descriptor handles this
case.
Signed-off-by: Brad Hards <bradh at frogmouth.net>
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/hw/usb-desc.c b/hw/usb-desc.c
index a784155..8367c45 100644
--- a/hw/usb-desc.c
+++ b/hw/usb-desc.c
@@ -91,6 +91,18 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
dest[0x08] = conf->bMaxPower;
wTotalLength += bLength;
+ /* handle grouped interfaces if any*/
+ for (i = 0; i < conf->nif_groups; i++) {
+ rc = usb_desc_iface_group(&(conf->if_groups[i]),
+ dest + wTotalLength,
+ len - wTotalLength);
+ if (rc < 0) {
+ return rc;
+ }
+ wTotalLength += rc;
+ }
+
+ /* handle normal (ungrouped / no IAD) interfaces if any */
for (i = 0; i < conf->nif; i++) {
rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
if (rc < 0) {
@@ -104,6 +116,41 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
return wTotalLength;
}
+int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
+ size_t len)
+{
+ int pos = 0;
+ int i = 0;
+
+ /* handle interface association descriptor */
+ uint8_t bLength = 0x08;
+
+ if (len < bLength) {
+ return -1;
+ }
+
+ dest[0x00] = bLength;
+ dest[0x01] = USB_DT_INTERFACE_ASSOC;
+ dest[0x02] = iad->bFirstInterface;
+ dest[0x03] = iad->bInterfaceCount;
+ dest[0x04] = iad->bFunctionClass;
+ dest[0x05] = iad->bFunctionSubClass;
+ dest[0x06] = iad->bFunctionProtocol;
+ dest[0x07] = iad->iFunction;
+ pos += bLength;
+
+ /* handle associated interfaces in this group */
+ for (i = 0; i < iad->nif; i++) {
+ int rc = usb_desc_iface(&(iad->ifs[i]), dest + pos, len - pos);
+ if (rc < 0) {
+ return rc;
+ }
+ pos += rc;
+ }
+
+ return pos;
+}
+
int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
{
uint8_t bLength = 0x09;
diff --git a/hw/usb-desc.h b/hw/usb-desc.h
index ac734ab..a612515 100644
--- a/hw/usb-desc.h
+++ b/hw/usb-desc.h
@@ -30,6 +30,24 @@ struct USBDescConfig {
uint8_t bmAttributes;
uint8_t bMaxPower;
+ /* grouped interfaces */
+ uint8_t nif_groups;
+ const USBDescIfaceAssoc *if_groups;
+
+ /* "normal" interfaces */
+ uint8_t nif;
+ const USBDescIface *ifs;
+};
+
+/* conceptually an Interface Association Descriptor, and releated interfaces */
+struct USBDescIfaceAssoc {
+ uint8_t bFirstInterface;
+ uint8_t bInterfaceCount;
+ uint8_t bFunctionClass;
+ uint8_t bFunctionSubClass;
+ uint8_t bFunctionProtocol;
+ uint8_t iFunction;
+
uint8_t nif;
const USBDescIface *ifs;
};
@@ -75,6 +93,8 @@ int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
int usb_desc_device_qualifier(const USBDescDevice *dev,
uint8_t *dest, size_t len);
int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len);
+int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
+ size_t len);
int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len);
int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len);
int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len);
diff --git a/hw/usb.h b/hw/usb.h
index ca06bf8..e0961ac 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -141,6 +141,7 @@ typedef struct USBDesc USBDesc;
typedef struct USBDescID USBDescID;
typedef struct USBDescDevice USBDescDevice;
typedef struct USBDescConfig USBDescConfig;
+typedef struct USBDescIfaceAssoc USBDescIfaceAssoc;
typedef struct USBDescIface USBDescIface;
typedef struct USBDescEndpoint USBDescEndpoint;
typedef struct USBDescOther USBDescOther;
commit fef13fa8e4de9255cad32192ff76e007568cf1b3
Author: Brad Hards <bradh at frogmouth.net>
Date: Sun Apr 3 15:33:20 2011 +1000
usb: remove fallback to bNumInterfaces if no .nif
All callers have been updated.
Signed-off-by: Brad Hards <bradh at frogmouth.net>
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/hw/usb-desc.c b/hw/usb-desc.c
index 62591f2..a784155 100644
--- a/hw/usb-desc.c
+++ b/hw/usb-desc.c
@@ -76,7 +76,7 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
{
uint8_t bLength = 0x09;
uint16_t wTotalLength = 0;
- int i, rc, count;
+ int i, rc;
if (len < bLength) {
return -1;
@@ -91,8 +91,7 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
dest[0x08] = conf->bMaxPower;
wTotalLength += bLength;
- count = conf->nif ? conf->nif : conf->bNumInterfaces;
- for (i = 0; i < count; i++) {
+ for (i = 0; i < conf->nif; i++) {
rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
if (rc < 0) {
return rc;
commit add750882f327db813af0795727efe5e4579ca5c
Author: Brad Hards <bradh at frogmouth.net>
Date: Sun Apr 3 15:33:19 2011 +1000
usb: update config descriptors to identify number of interfaces
Previously we relied on the .bNumInterfaces, but that won't always be
accurate after the introduction of grouped interfaces.
Signed-off-by: Brad Hards <bradh at frogmouth.net>
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
index 89c293c..bf59a7d 100644
--- a/hw/usb-hid.c
+++ b/hw/usb-hid.c
@@ -211,6 +211,7 @@ static const USBDescDevice desc_device_mouse = {
.iConfiguration = STR_CONFIG_MOUSE,
.bmAttributes = 0xa0,
.bMaxPower = 50,
+ .nif = 1,
.ifs = &desc_iface_mouse,
},
},
@@ -227,6 +228,7 @@ static const USBDescDevice desc_device_tablet = {
.iConfiguration = STR_CONFIG_TABLET,
.bmAttributes = 0xa0,
.bMaxPower = 50,
+ .nif = 1,
.ifs = &desc_iface_tablet,
},
},
@@ -243,6 +245,7 @@ static const USBDescDevice desc_device_keyboard = {
.iConfiguration = STR_CONFIG_KEYBOARD,
.bmAttributes = 0xa0,
.bMaxPower = 50,
+ .nif = 1,
.ifs = &desc_iface_keyboard,
},
},
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
index e0588f8..7c1f159 100644
--- a/hw/usb-hub.c
+++ b/hw/usb-hub.c
@@ -119,6 +119,7 @@ static const USBDescDevice desc_device_hub = {
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.bmAttributes = 0xe0,
+ .nif = 1,
.ifs = &desc_iface_hub,
},
},
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index bd1c3a4..040ea7a 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -119,6 +119,7 @@ static const USBDescDevice desc_device_full = {
.bConfigurationValue = 1,
.iConfiguration = STR_CONFIG_FULL,
.bmAttributes = 0xc0,
+ .nif = 1,
.ifs = &desc_iface_full,
},
},
@@ -153,6 +154,7 @@ static const USBDescDevice desc_device_high = {
.bConfigurationValue = 1,
.iConfiguration = STR_CONFIG_HIGH,
.bmAttributes = 0xc0,
+ .nif = 1,
.ifs = &desc_iface_high,
},
},
diff --git a/hw/usb-serial.c b/hw/usb-serial.c
index 6763d52..48ea0d8 100644
--- a/hw/usb-serial.c
+++ b/hw/usb-serial.c
@@ -146,6 +146,7 @@ static const USBDescDevice desc_device = {
.bConfigurationValue = 1,
.bmAttributes = 0x80,
.bMaxPower = 50,
+ .nif = 1,
.ifs = &desc_iface0,
},
},
diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c
index 16be7a2..57041a1 100644
--- a/hw/usb-wacom.c
+++ b/hw/usb-wacom.c
@@ -108,6 +108,7 @@ static const USBDescDevice desc_device_wacom = {
.bConfigurationValue = 1,
.bmAttributes = 0x80,
.bMaxPower = 40,
+ .nif = 1,
.ifs = &desc_iface_wacom,
},
},
commit c6d3ad0fad34b33557976e579ab0e4159989506f
Author: Brad Hards <bradh at frogmouth.net>
Date: Sun Apr 3 15:33:18 2011 +1000
usb: Add Interface Association Descriptor descriptor type
Signed-off-by: Brad Hards <bradh at frogmouth.net>
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/hw/usb.h b/hw/usb.h
index 7e46141..ca06bf8 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -124,6 +124,7 @@
#define USB_DT_ENDPOINT 0x05
#define USB_DT_DEVICE_QUALIFIER 0x06
#define USB_DT_OTHER_SPEED_CONFIG 0x07
+#define USB_DT_INTERFACE_ASSOC 0x0B
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
commit aa29141d84d58171c2d219f0a4b599bd76fb2e37
Merge: 6093d3d... 505597e...
Author: Anthony Liguori <aliguori at us.ibm.com>
Date: Wed May 25 07:04:13 2011 -0500
Merge remote-tracking branch 'kraxel/CVE-2011-1751' into staging
commit 6093d3d4ec873c93092d05d5ecc703aba8029efd
Author: Peter Maydell <peter.maydell at linaro.org>
Date: Mon May 23 10:13:47 2011 +0100
configure: Document --disable-slirp option in --help
The --disable-slirp option was undocumented; add it to configure's
--help output.
Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/configure b/configure
index bc54293..a318d37 100755
--- a/configure
+++ b/configure
@@ -966,6 +966,7 @@ echo " --disable-check-utests disable check unit-tests"
echo " --enable-check-utests enable check unit-tests"
echo " --disable-bluez disable bluez stack connectivity"
echo " --enable-bluez enable bluez stack connectivity"
+echo " --disable-slirp disable SLIRP userspace network connectivity"
echo " --disable-kvm disable KVM acceleration support"
echo " --enable-kvm enable KVM acceleration support"
echo " --disable-nptl disable usermode NPTL support"
commit 89e402688e0fb7463ab0b59db88f7f6de799a34f
Author: Peter Maydell <peter.maydell at linaro.org>
Date: Mon May 23 16:04:42 2011 +0100
target-arm/exec.h: Remove unused #define of M0
Remove a preprocessor #define which is never used.
Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/target-arm/exec.h b/target-arm/exec.h
index 44e1b55..db6608e 100644
--- a/target-arm/exec.h
+++ b/target-arm/exec.h
@@ -21,8 +21,6 @@
register struct CPUARMState *env asm(AREG0);
-#define M0 env->iwmmxt.val
-
#include "cpu.h"
#include "exec-all.h"
commit cab565c412a5634ce26f4d24ae65907c3108bdb0
Author: Peter Maydell <peter.maydell at linaro.org>
Date: Thu May 19 14:46:16 2011 +0100
target-arm: Signal InvalidOp for Neon GE and GT compares of QNaN
If the input to a Neon float comparison is a quiet NaN, the ARM ARM
specifies that we should raise InvalidOp if the comparison is GE or GT
but not for EQ. (Signaling NaNs raise InvalidOp regardless). This means
only EQ should use the _quiet version of the comparison function.
We implement this by cleaning up the comparison helpers to call the
appopriate versions of the softfloat simple comparison functions
(float32_le and friends) rather than the generic float32_compare functions.
This makes them simple enough that they are clearer opencoded rather
than macroised.
Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/target-arm/neon_helper.c b/target-arm/neon_helper.c
index f5b173a..9165519 100644
--- a/target-arm/neon_helper.c
+++ b/target-arm/neon_helper.c
@@ -1802,41 +1802,37 @@ uint32_t HELPER(neon_mul_f32)(uint32_t a, uint32_t b)
return float32_val(float32_mul(make_float32(a), make_float32(b), NFS));
}
-/* Floating point comparisons produce an integer result. */
-#define NEON_VOP_FCMP(name, ok) \
-uint32_t HELPER(neon_##name)(uint32_t a, uint32_t b) \
-{ \
- switch (float32_compare_quiet(make_float32(a), make_float32(b), NFS)) { \
- ok return ~0; \
- default: return 0; \
- } \
+/* Floating point comparisons produce an integer result.
+ * Note that EQ doesn't signal InvalidOp for QNaNs but GE and GT do.
+ * Softfloat routines return 0/1, which we convert to the 0/-1 Neon requires.
+ */
+uint32_t HELPER(neon_ceq_f32)(uint32_t a, uint32_t b)
+{
+ return -float32_eq_quiet(make_float32(a), make_float32(b), NFS);
+}
+
+uint32_t HELPER(neon_cge_f32)(uint32_t a, uint32_t b)
+{
+ return -float32_le(make_float32(b), make_float32(a), NFS);
}
-NEON_VOP_FCMP(ceq_f32, case float_relation_equal:)
-NEON_VOP_FCMP(cge_f32, case float_relation_equal: case float_relation_greater:)
-NEON_VOP_FCMP(cgt_f32, case float_relation_greater:)
+uint32_t HELPER(neon_cgt_f32)(uint32_t a, uint32_t b)
+{
+ return -float32_lt(make_float32(b), make_float32(a), NFS);
+}
uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b)
{
float32 f0 = float32_abs(make_float32(a));
float32 f1 = float32_abs(make_float32(b));
- switch (float32_compare_quiet(f0, f1, NFS)) {
- case float_relation_equal:
- case float_relation_greater:
- return ~0;
- default:
- return 0;
- }
+ return -float32_le(f1, f0, NFS);
}
uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b)
{
float32 f0 = float32_abs(make_float32(a));
float32 f1 = float32_abs(make_float32(b));
- if (float32_compare_quiet(f0, f1, NFS) == float_relation_greater) {
- return ~0;
- }
- return 0;
+ return -float32_lt(f1, f0, NFS);
}
#define ELEM(V, N, SIZE) (((V) >> ((N) * (SIZE))) & ((1ull << (SIZE)) - 1))
commit 5500b06cb5091c352a7ce6552da02c9e1958bf3b
Author: Peter Maydell <peter.maydell at linaro.org>
Date: Thu May 19 14:46:19 2011 +0100
target-arm: Use correct float status for Neon int-float conversions
The Neon versions of int-float conversions must use the "standard FPSCR"
rather than the default FPSCR. Implement this by having the helper
functions take a pointer to the appropriate float_status value rather
than simply taking a pointer to the entire CPUState, and making
translate.c pass a pointer to vfp.fp_status or vfp.standard_fp_status
appropriately for whether the instruction being translated is Neon
or VFP.
Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 05b3ccc..1cc492d 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -2526,99 +2526,39 @@ DO_VFP_cmp(s, float32)
DO_VFP_cmp(d, float64)
#undef DO_VFP_cmp
-/* Integer to float conversion. */
-float32 VFP_HELPER(uito, s)(uint32_t x, CPUState *env)
-{
- return uint32_to_float32(x, &env->vfp.fp_status);
-}
+/* Integer to float and float to integer conversions */
-float64 VFP_HELPER(uito, d)(uint32_t x, CPUState *env)
-{
- return uint32_to_float64(x, &env->vfp.fp_status);
-}
-
-float32 VFP_HELPER(sito, s)(uint32_t x, CPUState *env)
-{
- return int32_to_float32(x, &env->vfp.fp_status);
-}
-
-float64 VFP_HELPER(sito, d)(uint32_t x, CPUState *env)
-{
- return int32_to_float64(x, &env->vfp.fp_status);
-}
-
-/* Float to integer conversion. */
-uint32_t VFP_HELPER(toui, s)(float32 x, CPUState *env)
-{
- if (float32_is_any_nan(x)) {
- float_raise(float_flag_invalid, &env->vfp.fp_status);
- return 0;
- }
- return float32_to_uint32(x, &env->vfp.fp_status);
-}
-
-uint32_t VFP_HELPER(toui, d)(float64 x, CPUState *env)
-{
- if (float64_is_any_nan(x)) {
- float_raise(float_flag_invalid, &env->vfp.fp_status);
- return 0;
- }
- return float64_to_uint32(x, &env->vfp.fp_status);
-}
-
-uint32_t VFP_HELPER(tosi, s)(float32 x, CPUState *env)
-{
- if (float32_is_any_nan(x)) {
- float_raise(float_flag_invalid, &env->vfp.fp_status);
- return 0;
- }
- return float32_to_int32(x, &env->vfp.fp_status);
-}
-
-uint32_t VFP_HELPER(tosi, d)(float64 x, CPUState *env)
-{
- if (float64_is_any_nan(x)) {
- float_raise(float_flag_invalid, &env->vfp.fp_status);
- return 0;
- }
- return float64_to_int32(x, &env->vfp.fp_status);
+#define CONV_ITOF(name, fsz, sign) \
+ float##fsz HELPER(name)(uint32_t x, void *fpstp) \
+{ \
+ float_status *fpst = fpstp; \
+ return sign##int32_to_##float##fsz(x, fpst); \
}
-uint32_t VFP_HELPER(touiz, s)(float32 x, CPUState *env)
-{
- if (float32_is_any_nan(x)) {
- float_raise(float_flag_invalid, &env->vfp.fp_status);
- return 0;
- }
- return float32_to_uint32_round_to_zero(x, &env->vfp.fp_status);
+#define CONV_FTOI(name, fsz, sign, round) \
+uint32_t HELPER(name)(float##fsz x, void *fpstp) \
+{ \
+ float_status *fpst = fpstp; \
+ if (float##fsz##_is_any_nan(x)) { \
+ float_raise(float_flag_invalid, fpst); \
+ return 0; \
+ } \
+ return float##fsz##_to_##sign##int32##round(x, fpst); \
}
-uint32_t VFP_HELPER(touiz, d)(float64 x, CPUState *env)
-{
- if (float64_is_any_nan(x)) {
- float_raise(float_flag_invalid, &env->vfp.fp_status);
- return 0;
- }
- return float64_to_uint32_round_to_zero(x, &env->vfp.fp_status);
-}
+#define FLOAT_CONVS(name, p, fsz, sign) \
+CONV_ITOF(vfp_##name##to##p, fsz, sign) \
+CONV_FTOI(vfp_to##name##p, fsz, sign, ) \
+CONV_FTOI(vfp_to##name##z##p, fsz, sign, _round_to_zero)
-uint32_t VFP_HELPER(tosiz, s)(float32 x, CPUState *env)
-{
- if (float32_is_any_nan(x)) {
- float_raise(float_flag_invalid, &env->vfp.fp_status);
- return 0;
- }
- return float32_to_int32_round_to_zero(x, &env->vfp.fp_status);
-}
+FLOAT_CONVS(si, s, 32, )
+FLOAT_CONVS(si, d, 64, )
+FLOAT_CONVS(ui, s, 32, u)
+FLOAT_CONVS(ui, d, 64, u)
-uint32_t VFP_HELPER(tosiz, d)(float64 x, CPUState *env)
-{
- if (float64_is_any_nan(x)) {
- float_raise(float_flag_invalid, &env->vfp.fp_status);
- return 0;
- }
- return float64_to_int32_round_to_zero(x, &env->vfp.fp_status);
-}
+#undef CONV_ITOF
+#undef CONV_FTOI
+#undef FLOAT_CONVS
/* floating point conversion */
float64 VFP_HELPER(fcvtd, s)(float32 x, CPUState *env)
@@ -2641,23 +2581,25 @@ float32 VFP_HELPER(fcvts, d)(float64 x, CPUState *env)
/* VFP3 fixed point conversion. */
#define VFP_CONV_FIX(name, p, fsz, itype, sign) \
-float##fsz VFP_HELPER(name##to, p)(uint##fsz##_t x, uint32_t shift, \
- CPUState *env) \
+float##fsz HELPER(vfp_##name##to##p)(uint##fsz##_t x, uint32_t shift, \
+ void *fpstp) \
{ \
+ float_status *fpst = fpstp; \
float##fsz tmp; \
- tmp = sign##int32_to_##float##fsz ((itype##_t)x, &env->vfp.fp_status); \
- return float##fsz##_scalbn(tmp, -(int)shift, &env->vfp.fp_status); \
+ tmp = sign##int32_to_##float##fsz((itype##_t)x, fpst); \
+ return float##fsz##_scalbn(tmp, -(int)shift, fpst); \
} \
-uint##fsz##_t VFP_HELPER(to##name, p)(float##fsz x, uint32_t shift, \
- CPUState *env) \
+uint##fsz##_t HELPER(vfp_to##name##p)(float##fsz x, uint32_t shift, \
+ void *fpstp) \
{ \
+ float_status *fpst = fpstp; \
float##fsz tmp; \
if (float##fsz##_is_any_nan(x)) { \
- float_raise(float_flag_invalid, &env->vfp.fp_status); \
+ float_raise(float_flag_invalid, fpst); \
return 0; \
} \
- tmp = float##fsz##_scalbn(x, shift, &env->vfp.fp_status); \
- return float##fsz##_to_##itype##_round_to_zero(tmp, &env->vfp.fp_status); \
+ tmp = float##fsz##_scalbn(x, shift, fpst); \
+ return float##fsz##_to_##itype##_round_to_zero(tmp, fpst); \
}
VFP_CONV_FIX(sh, d, 64, int16, )
diff --git a/target-arm/helper.h b/target-arm/helper.h
index ae701e8..7d5533f 100644
--- a/target-arm/helper.h
+++ b/target-arm/helper.h
@@ -96,36 +96,36 @@ DEF_HELPER_3(vfp_cmped, void, f64, f64, env)
DEF_HELPER_2(vfp_fcvtds, f64, f32, env)
DEF_HELPER_2(vfp_fcvtsd, f32, f64, env)
-DEF_HELPER_2(vfp_uitos, f32, i32, env)
-DEF_HELPER_2(vfp_uitod, f64, i32, env)
-DEF_HELPER_2(vfp_sitos, f32, i32, env)
-DEF_HELPER_2(vfp_sitod, f64, i32, env)
-
-DEF_HELPER_2(vfp_touis, i32, f32, env)
-DEF_HELPER_2(vfp_touid, i32, f64, env)
-DEF_HELPER_2(vfp_touizs, i32, f32, env)
-DEF_HELPER_2(vfp_touizd, i32, f64, env)
-DEF_HELPER_2(vfp_tosis, i32, f32, env)
-DEF_HELPER_2(vfp_tosid, i32, f64, env)
-DEF_HELPER_2(vfp_tosizs, i32, f32, env)
-DEF_HELPER_2(vfp_tosizd, i32, f64, env)
-
-DEF_HELPER_3(vfp_toshs, i32, f32, i32, env)
-DEF_HELPER_3(vfp_tosls, i32, f32, i32, env)
-DEF_HELPER_3(vfp_touhs, i32, f32, i32, env)
-DEF_HELPER_3(vfp_touls, i32, f32, i32, env)
-DEF_HELPER_3(vfp_toshd, i64, f64, i32, env)
-DEF_HELPER_3(vfp_tosld, i64, f64, i32, env)
-DEF_HELPER_3(vfp_touhd, i64, f64, i32, env)
-DEF_HELPER_3(vfp_tould, i64, f64, i32, env)
-DEF_HELPER_3(vfp_shtos, f32, i32, i32, env)
-DEF_HELPER_3(vfp_sltos, f32, i32, i32, env)
-DEF_HELPER_3(vfp_uhtos, f32, i32, i32, env)
-DEF_HELPER_3(vfp_ultos, f32, i32, i32, env)
-DEF_HELPER_3(vfp_shtod, f64, i64, i32, env)
-DEF_HELPER_3(vfp_sltod, f64, i64, i32, env)
-DEF_HELPER_3(vfp_uhtod, f64, i64, i32, env)
-DEF_HELPER_3(vfp_ultod, f64, i64, i32, env)
+DEF_HELPER_2(vfp_uitos, f32, i32, ptr)
+DEF_HELPER_2(vfp_uitod, f64, i32, ptr)
+DEF_HELPER_2(vfp_sitos, f32, i32, ptr)
+DEF_HELPER_2(vfp_sitod, f64, i32, ptr)
+
+DEF_HELPER_2(vfp_touis, i32, f32, ptr)
+DEF_HELPER_2(vfp_touid, i32, f64, ptr)
+DEF_HELPER_2(vfp_touizs, i32, f32, ptr)
+DEF_HELPER_2(vfp_touizd, i32, f64, ptr)
+DEF_HELPER_2(vfp_tosis, i32, f32, ptr)
+DEF_HELPER_2(vfp_tosid, i32, f64, ptr)
+DEF_HELPER_2(vfp_tosizs, i32, f32, ptr)
+DEF_HELPER_2(vfp_tosizd, i32, f64, ptr)
+
+DEF_HELPER_3(vfp_toshs, i32, f32, i32, ptr)
+DEF_HELPER_3(vfp_tosls, i32, f32, i32, ptr)
+DEF_HELPER_3(vfp_touhs, i32, f32, i32, ptr)
+DEF_HELPER_3(vfp_touls, i32, f32, i32, ptr)
+DEF_HELPER_3(vfp_toshd, i64, f64, i32, ptr)
+DEF_HELPER_3(vfp_tosld, i64, f64, i32, ptr)
+DEF_HELPER_3(vfp_touhd, i64, f64, i32, ptr)
+DEF_HELPER_3(vfp_tould, i64, f64, i32, ptr)
+DEF_HELPER_3(vfp_shtos, f32, i32, i32, ptr)
+DEF_HELPER_3(vfp_sltos, f32, i32, i32, ptr)
+DEF_HELPER_3(vfp_uhtos, f32, i32, i32, ptr)
+DEF_HELPER_3(vfp_ultos, f32, i32, i32, ptr)
+DEF_HELPER_3(vfp_shtod, f64, i64, i32, ptr)
+DEF_HELPER_3(vfp_sltod, f64, i64, i32, ptr)
+DEF_HELPER_3(vfp_uhtod, f64, i64, i32, ptr)
+DEF_HELPER_3(vfp_ultod, f64, i64, i32, ptr)
DEF_HELPER_2(vfp_fcvt_f16_to_f32, f32, i32, env)
DEF_HELPER_2(vfp_fcvt_f32_to_f16, i32, f32, env)
diff --git a/target-arm/translate.c b/target-arm/translate.c
index a8a3b2c..1501db1 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -977,63 +977,73 @@ static inline void gen_vfp_F1_ld0(int dp)
tcg_gen_movi_i32(cpu_F1s, 0);
}
-static inline void gen_vfp_uito(int dp)
-{
- if (dp)
- gen_helper_vfp_uitod(cpu_F0d, cpu_F0s, cpu_env);
- else
- gen_helper_vfp_uitos(cpu_F0s, cpu_F0s, cpu_env);
-}
-
-static inline void gen_vfp_sito(int dp)
-{
- if (dp)
- gen_helper_vfp_sitod(cpu_F0d, cpu_F0s, cpu_env);
- else
- gen_helper_vfp_sitos(cpu_F0s, cpu_F0s, cpu_env);
-}
-
-static inline void gen_vfp_toui(int dp)
-{
- if (dp)
- gen_helper_vfp_touid(cpu_F0s, cpu_F0d, cpu_env);
- else
- gen_helper_vfp_touis(cpu_F0s, cpu_F0s, cpu_env);
+#define VFP_GEN_ITOF(name) \
+static inline void gen_vfp_##name(int dp, int neon) \
+{ \
+ TCGv statusptr = tcg_temp_new_i32(); \
+ int offset; \
+ if (neon) { \
+ offset = offsetof(CPUState, vfp.standard_fp_status); \
+ } else { \
+ offset = offsetof(CPUState, vfp.fp_status); \
+ } \
+ tcg_gen_addi_i32(statusptr, cpu_env, offset); \
+ if (dp) { \
+ gen_helper_vfp_##name##d(cpu_F0d, cpu_F0s, statusptr); \
+ } else { \
+ gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, statusptr); \
+ } \
+ tcg_temp_free_i32(statusptr); \
}
-static inline void gen_vfp_touiz(int dp)
-{
- if (dp)
- gen_helper_vfp_touizd(cpu_F0s, cpu_F0d, cpu_env);
- else
- gen_helper_vfp_touizs(cpu_F0s, cpu_F0s, cpu_env);
-}
+VFP_GEN_ITOF(uito)
+VFP_GEN_ITOF(sito)
+#undef VFP_GEN_ITOF
-static inline void gen_vfp_tosi(int dp)
-{
- if (dp)
- gen_helper_vfp_tosid(cpu_F0s, cpu_F0d, cpu_env);
- else
- gen_helper_vfp_tosis(cpu_F0s, cpu_F0s, cpu_env);
+#define VFP_GEN_FTOI(name) \
+static inline void gen_vfp_##name(int dp, int neon) \
+{ \
+ TCGv statusptr = tcg_temp_new_i32(); \
+ int offset; \
+ if (neon) { \
+ offset = offsetof(CPUState, vfp.standard_fp_status); \
+ } else { \
+ offset = offsetof(CPUState, vfp.fp_status); \
+ } \
+ tcg_gen_addi_i32(statusptr, cpu_env, offset); \
+ if (dp) { \
+ gen_helper_vfp_##name##d(cpu_F0s, cpu_F0d, statusptr); \
+ } else { \
+ gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, statusptr); \
+ } \
+ tcg_temp_free_i32(statusptr); \
}
-static inline void gen_vfp_tosiz(int dp)
-{
- if (dp)
- gen_helper_vfp_tosizd(cpu_F0s, cpu_F0d, cpu_env);
- else
- gen_helper_vfp_tosizs(cpu_F0s, cpu_F0s, cpu_env);
-}
+VFP_GEN_FTOI(toui)
+VFP_GEN_FTOI(touiz)
+VFP_GEN_FTOI(tosi)
+VFP_GEN_FTOI(tosiz)
+#undef VFP_GEN_FTOI
#define VFP_GEN_FIX(name) \
-static inline void gen_vfp_##name(int dp, int shift) \
+static inline void gen_vfp_##name(int dp, int shift, int neon) \
{ \
TCGv tmp_shift = tcg_const_i32(shift); \
- if (dp) \
- gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, tmp_shift, cpu_env);\
- else \
- gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, tmp_shift, cpu_env);\
+ TCGv statusptr = tcg_temp_new_i32(); \
+ int offset; \
+ if (neon) { \
+ offset = offsetof(CPUState, vfp.standard_fp_status); \
+ } else { \
+ offset = offsetof(CPUState, vfp.fp_status); \
+ } \
+ tcg_gen_addi_i32(statusptr, cpu_env, offset); \
+ if (dp) { \
+ gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, tmp_shift, statusptr); \
+ } else { \
+ gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, tmp_shift, statusptr); \
+ } \
tcg_temp_free_i32(tmp_shift); \
+ tcg_temp_free_i32(statusptr); \
}
VFP_GEN_FIX(tosh)
VFP_GEN_FIX(tosl)
@@ -3183,62 +3193,62 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn)
gen_helper_vfp_fcvtds(cpu_F0d, cpu_F0s, cpu_env);
break;
case 16: /* fuito */
- gen_vfp_uito(dp);
+ gen_vfp_uito(dp, 0);
break;
case 17: /* fsito */
- gen_vfp_sito(dp);
+ gen_vfp_sito(dp, 0);
break;
case 20: /* fshto */
if (!arm_feature(env, ARM_FEATURE_VFP3))
return 1;
- gen_vfp_shto(dp, 16 - rm);
+ gen_vfp_shto(dp, 16 - rm, 0);
break;
case 21: /* fslto */
if (!arm_feature(env, ARM_FEATURE_VFP3))
return 1;
- gen_vfp_slto(dp, 32 - rm);
+ gen_vfp_slto(dp, 32 - rm, 0);
break;
case 22: /* fuhto */
if (!arm_feature(env, ARM_FEATURE_VFP3))
return 1;
- gen_vfp_uhto(dp, 16 - rm);
+ gen_vfp_uhto(dp, 16 - rm, 0);
break;
case 23: /* fulto */
if (!arm_feature(env, ARM_FEATURE_VFP3))
return 1;
- gen_vfp_ulto(dp, 32 - rm);
+ gen_vfp_ulto(dp, 32 - rm, 0);
break;
case 24: /* ftoui */
- gen_vfp_toui(dp);
+ gen_vfp_toui(dp, 0);
break;
case 25: /* ftouiz */
- gen_vfp_touiz(dp);
+ gen_vfp_touiz(dp, 0);
break;
case 26: /* ftosi */
- gen_vfp_tosi(dp);
+ gen_vfp_tosi(dp, 0);
break;
case 27: /* ftosiz */
- gen_vfp_tosiz(dp);
+ gen_vfp_tosiz(dp, 0);
break;
case 28: /* ftosh */
if (!arm_feature(env, ARM_FEATURE_VFP3))
return 1;
- gen_vfp_tosh(dp, 16 - rm);
+ gen_vfp_tosh(dp, 16 - rm, 0);
break;
case 29: /* ftosl */
if (!arm_feature(env, ARM_FEATURE_VFP3))
return 1;
- gen_vfp_tosl(dp, 32 - rm);
+ gen_vfp_tosl(dp, 32 - rm, 0);
break;
case 30: /* ftouh */
if (!arm_feature(env, ARM_FEATURE_VFP3))
return 1;
- gen_vfp_touh(dp, 16 - rm);
+ gen_vfp_touh(dp, 16 - rm, 0);
break;
case 31: /* ftoul */
if (!arm_feature(env, ARM_FEATURE_VFP3))
return 1;
- gen_vfp_toul(dp, 32 - rm);
+ gen_vfp_toul(dp, 32 - rm, 0);
break;
default: /* undefined */
printf ("rn:%d\n", rn);
@@ -5251,14 +5261,14 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, pass));
if (!(op & 1)) {
if (u)
- gen_vfp_ulto(0, shift);
+ gen_vfp_ulto(0, shift, 1);
else
- gen_vfp_slto(0, shift);
+ gen_vfp_slto(0, shift, 1);
} else {
if (u)
- gen_vfp_toul(0, shift);
+ gen_vfp_toul(0, shift, 1);
else
- gen_vfp_tosl(0, shift);
+ gen_vfp_tosl(0, shift, 1);
}
tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, pass));
}
@@ -6071,16 +6081,16 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
gen_helper_rsqrte_f32(cpu_F0s, cpu_F0s, cpu_env);
break;
case NEON_2RM_VCVT_FS: /* VCVT.F32.S32 */
- gen_vfp_sito(0);
+ gen_vfp_sito(0, 1);
break;
case NEON_2RM_VCVT_FU: /* VCVT.F32.U32 */
- gen_vfp_uito(0);
+ gen_vfp_uito(0, 1);
break;
case NEON_2RM_VCVT_SF: /* VCVT.S32.F32 */
- gen_vfp_tosiz(0);
+ gen_vfp_tosiz(0, 1);
break;
case NEON_2RM_VCVT_UF: /* VCVT.U32.F32 */
- gen_vfp_touiz(0);
+ gen_vfp_touiz(0, 1);
break;
default:
/* Reserved op values were caught by the
commit 36802b6b1ed7887aeae5d027f86a969400f8824a
Author: Peter Maydell <peter.maydell at linaro.org>
Date: Thu May 19 14:46:18 2011 +0100
target-arm: Signal Underflow when denormal flushed to zero on output
On ARM the architecture mandates that when an output denormal is flushed to
zero we must set the FPSCR UFC (underflow) bit, so map softfloat's
float_flag_output_denormal accordingly.
Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/target-arm/helper.c b/target-arm/helper.c
index f072527..05b3ccc 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -2355,7 +2355,7 @@ static inline int vfp_exceptbits_from_host(int host_bits)
target_bits |= 2;
if (host_bits & float_flag_overflow)
target_bits |= 4;
- if (host_bits & float_flag_underflow)
+ if (host_bits & (float_flag_underflow | float_flag_output_denormal))
target_bits |= 8;
if (host_bits & float_flag_inexact)
target_bits |= 0x10;
commit e6afc87f804abee7d0479be5e8e31c56d885fafb
Author: Peter Maydell <peter.maydell at linaro.org>
Date: Thu May 19 14:46:17 2011 +0100
softfloat: Add new flag for when denormal result is flushed to zero
Add a new float_flag_output_denormal which is set when the result
of a floating point operation would be denormal but is flushed to
zero because we are in flush_to_zero mode. This is necessary because
some architectures signal this condition as an underflow and others
signal it as an inexact result.
Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index baba1dc..e3cd8a7 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -341,7 +341,10 @@ static float32 roundAndPackFloat32( flag zSign, int16 zExp, uint32_t zSig STATUS
return packFloat32( zSign, 0xFF, - ( roundIncrement == 0 ));
}
if ( zExp < 0 ) {
- if ( STATUS(flush_to_zero) ) return packFloat32( zSign, 0, 0 );
+ if (STATUS(flush_to_zero)) {
+ float_raise(float_flag_output_denormal STATUS_VAR);
+ return packFloat32(zSign, 0, 0);
+ }
isTiny =
( STATUS(float_detect_tininess) == float_tininess_before_rounding )
|| ( zExp < -1 )
@@ -520,7 +523,10 @@ static float64 roundAndPackFloat64( flag zSign, int16 zExp, uint64_t zSig STATUS
return packFloat64( zSign, 0x7FF, - ( roundIncrement == 0 ));
}
if ( zExp < 0 ) {
- if ( STATUS(flush_to_zero) ) return packFloat64( zSign, 0, 0 );
+ if (STATUS(flush_to_zero)) {
+ float_raise(float_flag_output_denormal STATUS_VAR);
+ return packFloat64(zSign, 0, 0);
+ }
isTiny =
( STATUS(float_detect_tininess) == float_tininess_before_rounding )
|| ( zExp < -1 )
@@ -699,7 +705,10 @@ static floatx80
goto overflow;
}
if ( zExp <= 0 ) {
- if ( STATUS(flush_to_zero) ) return packFloatx80( zSign, 0, 0 );
+ if (STATUS(flush_to_zero)) {
+ float_raise(float_flag_output_denormal STATUS_VAR);
+ return packFloatx80(zSign, 0, 0);
+ }
isTiny =
( STATUS(float_detect_tininess) == float_tininess_before_rounding )
|| ( zExp < 0 )
@@ -1030,7 +1039,10 @@ static float128
return packFloat128( zSign, 0x7FFF, 0, 0 );
}
if ( zExp < 0 ) {
- if ( STATUS(flush_to_zero) ) return packFloat128( zSign, 0, 0, 0 );
+ if (STATUS(flush_to_zero)) {
+ float_raise(float_flag_output_denormal STATUS_VAR);
+ return packFloat128(zSign, 0, 0, 0);
+ }
isTiny =
( STATUS(float_detect_tininess) == float_tininess_before_rounding )
|| ( zExp < -1 )
@@ -1761,7 +1773,12 @@ static float32 addFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM)
return a;
}
if ( aExp == 0 ) {
- if ( STATUS(flush_to_zero) ) return packFloat32( zSign, 0, 0 );
+ if (STATUS(flush_to_zero)) {
+ if (aSig | bSig) {
+ float_raise(float_flag_output_denormal STATUS_VAR);
+ }
+ return packFloat32(zSign, 0, 0);
+ }
return packFloat32( zSign, 0, ( aSig + bSig )>>6 );
}
zSig = 0x40000000 + aSig + bSig;
@@ -3120,7 +3137,12 @@ static float64 addFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM )
return a;
}
if ( aExp == 0 ) {
- if ( STATUS(flush_to_zero) ) return packFloat64( zSign, 0, 0 );
+ if (STATUS(flush_to_zero)) {
+ if (aSig | bSig) {
+ float_raise(float_flag_output_denormal STATUS_VAR);
+ }
+ return packFloat64(zSign, 0, 0);
+ }
return packFloat64( zSign, 0, ( aSig + bSig )>>9 );
}
zSig = LIT64( 0x4000000000000000 ) + aSig + bSig;
@@ -5282,7 +5304,12 @@ static float128 addFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM
}
add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
if ( aExp == 0 ) {
- if ( STATUS(flush_to_zero) ) return packFloat128( zSign, 0, 0, 0 );
+ if (STATUS(flush_to_zero)) {
+ if (zSig0 | zSig1) {
+ float_raise(float_flag_output_denormal STATUS_VAR);
+ }
+ return packFloat128(zSign, 0, 0, 0);
+ }
return packFloat128( zSign, 0, zSig0, zSig1 );
}
zSig2 = 0;
diff --git a/fpu/softfloat.h b/fpu/softfloat.h
index 5eff085..58c9b7b 100644
--- a/fpu/softfloat.h
+++ b/fpu/softfloat.h
@@ -193,7 +193,8 @@ enum {
float_flag_overflow = 8,
float_flag_underflow = 16,
float_flag_inexact = 32,
- float_flag_input_denormal = 64
+ float_flag_input_denormal = 64,
+ float_flag_output_denormal = 128
};
typedef struct float_status {
commit 43fe9bdb0f64237187aeab809bb98e1b46807538
Author: Peter Maydell <peter.maydell at linaro.org>
Date: Thu May 19 14:46:15 2011 +0100
target-arm: Signal InputDenormal for VRECPE, VRSQRTE, VRECPS, VRSQRTS
The helpers for VRECPE.F32, VSQRTE.F32, VRECPS and VRSQRTS handle denormals
as special cases, so we must set the InputDenormal exception flag ourselves.
Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 5ff6a9b..f072527 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -2720,6 +2720,9 @@ float32 HELPER(recps_f32)(float32 a, float32 b, CPUState *env)
float_status *s = &env->vfp.standard_fp_status;
if ((float32_is_infinity(a) && float32_is_zero_or_denormal(b)) ||
(float32_is_infinity(b) && float32_is_zero_or_denormal(a))) {
+ if (!(float32_is_zero(a) || float32_is_zero(b))) {
+ float_raise(float_flag_input_denormal, s);
+ }
return float32_two;
}
return float32_sub(float32_two, float32_mul(a, b, s), s);
@@ -2731,6 +2734,9 @@ float32 HELPER(rsqrts_f32)(float32 a, float32 b, CPUState *env)
float32 product;
if ((float32_is_infinity(a) && float32_is_zero_or_denormal(b)) ||
(float32_is_infinity(b) && float32_is_zero_or_denormal(a))) {
+ if (!(float32_is_zero(a) || float32_is_zero(b))) {
+ float_raise(float_flag_input_denormal, s);
+ }
return float32_one_point_five;
}
product = float32_mul(a, b, s);
@@ -2791,6 +2797,9 @@ float32 HELPER(recpe_f32)(float32 a, CPUState *env)
} else if (float32_is_infinity(a)) {
return float32_set_sign(float32_zero, float32_is_neg(a));
} else if (float32_is_zero_or_denormal(a)) {
+ if (!float32_is_zero(a)) {
+ float_raise(float_flag_input_denormal, s);
+ }
float_raise(float_flag_divbyzero, s);
return float32_set_sign(float32_infinity, float32_is_neg(a));
} else if (a_exp >= 253) {
@@ -2882,6 +2891,9 @@ float32 HELPER(rsqrte_f32)(float32 a, CPUState *env)
}
return float32_default_nan;
} else if (float32_is_zero_or_denormal(a)) {
+ if (!float32_is_zero(a)) {
+ float_raise(float_flag_input_denormal, s);
+ }
float_raise(float_flag_divbyzero, s);
return float32_set_sign(float32_infinity, float32_is_neg(a));
} else if (float32_is_neg(a)) {
commit 1146a817c1c46f298492188e5269b98f3a0e51e8
Author: Peter Maydell <peter.maydell at linaro.org>
Date: Thu May 19 14:46:14 2011 +0100
target-arm: Don't set FP exceptions in recip, recip_sqrt estimate fns
The functions which do the core estimation algorithms for the VRSQRTE
and VRECPE instructions should not set floating point exception flags,
so use a local fp status for doing these calculations.
Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 62ae72e..5ff6a9b 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -2749,7 +2749,11 @@ float32 HELPER(rsqrts_f32)(float32 a, float32 b, CPUState *env)
*/
static float64 recip_estimate(float64 a, CPUState *env)
{
- float_status *s = &env->vfp.standard_fp_status;
+ /* These calculations mustn't set any fp exception flags,
+ * so we use a local copy of the fp_status.
+ */
+ float_status dummy_status = env->vfp.standard_fp_status;
+ float_status *s = &dummy_status;
/* q = (int)(a * 512.0) */
float64 q = float64_mul(float64_512, a, s);
int64_t q_int = float64_to_int64_round_to_zero(q, s);
@@ -2812,7 +2816,11 @@ float32 HELPER(recpe_f32)(float32 a, CPUState *env)
*/
static float64 recip_sqrt_estimate(float64 a, CPUState *env)
{
- float_status *s = &env->vfp.standard_fp_status;
+ /* These calculations mustn't set any fp exception flags,
+ * so we use a local copy of the fp_status.
+ */
+ float_status dummy_status = env->vfp.standard_fp_status;
+ float_status *s = &dummy_status;
float64 q;
int64_t q_int;
commit 8c11ad25f40ee443000d2dbc0ef296ee210d86b4
Author: Aurelien Jarno <aurelien at aurel32.net>
Date: Tue May 17 18:25:45 2011 +0200
tcg: don't keep dead outputs in registers
If an op with dead outputs is not removed, because it has side effects
or has multiple output and only one dead, mark the registers as dead
instead of saving them. This avoid a few register spills on TCG targets
with low register count, especially with div2 and mul2 ops, or when a
qemu_ld* result is not used (prefetch emulation for example).
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 82d3e1d..fad92f9 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -1782,12 +1782,16 @@ static void tcg_reg_alloc_op(TCGContext *s,
if (!ts->fixed_reg) {
if (ts->val_type == TEMP_VAL_REG)
s->reg_to_temp[ts->reg] = -1;
- ts->val_type = TEMP_VAL_REG;
- ts->reg = reg;
- /* temp value is modified, so the value kept in memory is
- potentially not the same */
- ts->mem_coherent = 0;
- s->reg_to_temp[reg] = arg;
+ if (IS_DEAD_ARG(i)) {
+ ts->val_type = TEMP_VAL_DEAD;
+ } else {
+ ts->val_type = TEMP_VAL_REG;
+ ts->reg = reg;
+ /* temp value is modified, so the value kept in memory is
+ potentially not the same */
+ ts->mem_coherent = 0;
+ s->reg_to_temp[reg] = arg;
+ }
}
oarg_end:
new_args[i] = reg;
@@ -1981,10 +1985,14 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def,
} else {
if (ts->val_type == TEMP_VAL_REG)
s->reg_to_temp[ts->reg] = -1;
- ts->val_type = TEMP_VAL_REG;
- ts->reg = reg;
- ts->mem_coherent = 0;
- s->reg_to_temp[reg] = arg;
+ if (IS_DEAD_ARG(i)) {
+ ts->val_type = TEMP_VAL_DEAD;
+ } else {
+ ts->val_type = TEMP_VAL_REG;
+ ts->reg = reg;
+ ts->mem_coherent = 0;
+ s->reg_to_temp[reg] = arg;
+ }
}
}
commit 6b64b624cde336f3df1146483e7858f5fa814f95
Author: Aurelien Jarno <aurelien at aurel32.net>
Date: Tue May 17 18:25:45 2011 +0200
tcg: mark dead output argument in op_dead_args
If an op is not removed and has dead output arguments, mark it
in op_dead_args similarly to what is done for input arguments.
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 9a48cb9..82d3e1d 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -1245,8 +1245,12 @@ static void tcg_liveness_analysis(TCGContext *s)
do_not_remove_call:
/* output args are dead */
+ dead_args = 0;
for(i = 0; i < nb_oargs; i++) {
arg = args[i];
+ if (dead_temps[arg]) {
+ dead_args |= (1 << i);
+ }
dead_temps[arg] = 1;
}
@@ -1256,7 +1260,6 @@ static void tcg_liveness_analysis(TCGContext *s)
}
/* input args are live */
- dead_args = 0;
for(i = nb_oargs; i < nb_iargs + nb_oargs; i++) {
arg = args[i];
if (arg != TCG_CALL_DUMMY_ARG) {
@@ -1313,8 +1316,12 @@ static void tcg_liveness_analysis(TCGContext *s)
do_not_remove:
/* output args are dead */
+ dead_args = 0;
for(i = 0; i < nb_oargs; i++) {
arg = args[i];
+ if (dead_temps[arg]) {
+ dead_args |= (1 << i);
+ }
dead_temps[arg] = 1;
}
@@ -1327,7 +1334,6 @@ static void tcg_liveness_analysis(TCGContext *s)
}
/* input args are live */
- dead_args = 0;
for(i = nb_oargs; i < nb_oargs + nb_iargs; i++) {
arg = args[i];
if (dead_temps[arg]) {
commit 866cb6cb21b91809f3f5c49c7c4268573552a108
Author: Aurelien Jarno <aurelien at aurel32.net>
Date: Tue May 17 18:25:45 2011 +0200
tcg: replace op_dead_iargs by op_dead_args
Allow all args to be dead by replacing the input specific op_dead_iargs
variable by op_dead_args. Note this is a purely mechanical change.
Signed-off-by: Aurelien Jarno <aurelien at aurel32.net>
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 8748c05..9a48cb9 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -1193,7 +1193,7 @@ static inline void tcg_la_bb_end(TCGContext *s, uint8_t *dead_temps)
}
}
-/* Liveness analysis : update the opc_dead_iargs array to tell if a
+/* Liveness analysis : update the opc_dead_args array to tell if a
given input arguments is dead. Instructions updating dead
temporaries are removed. */
static void tcg_liveness_analysis(TCGContext *s)
@@ -1203,13 +1203,13 @@ static void tcg_liveness_analysis(TCGContext *s)
TCGArg *args;
const TCGOpDef *def;
uint8_t *dead_temps;
- unsigned int dead_iargs;
+ unsigned int dead_args;
gen_opc_ptr++; /* skip end */
nb_ops = gen_opc_ptr - gen_opc_buf;
- s->op_dead_iargs = tcg_malloc(nb_ops * sizeof(uint16_t));
+ s->op_dead_args = tcg_malloc(nb_ops * sizeof(uint16_t));
dead_temps = tcg_malloc(s->nb_temps);
memset(dead_temps, 1, s->nb_temps);
@@ -1256,17 +1256,17 @@ static void tcg_liveness_analysis(TCGContext *s)
}
/* input args are live */
- dead_iargs = 0;
- for(i = 0; i < nb_iargs; i++) {
- arg = args[i + nb_oargs];
+ dead_args = 0;
+ for(i = nb_oargs; i < nb_iargs + nb_oargs; i++) {
+ arg = args[i];
if (arg != TCG_CALL_DUMMY_ARG) {
if (dead_temps[arg]) {
- dead_iargs |= (1 << i);
+ dead_args |= (1 << i);
}
dead_temps[arg] = 0;
}
}
- s->op_dead_iargs[op_index] = dead_iargs;
+ s->op_dead_args[op_index] = dead_args;
}
args--;
}
@@ -1327,15 +1327,15 @@ static void tcg_liveness_analysis(TCGContext *s)
}
/* input args are live */
- dead_iargs = 0;
- for(i = 0; i < nb_iargs; i++) {
- arg = args[i + nb_oargs];
+ dead_args = 0;
+ for(i = nb_oargs; i < nb_oargs + nb_iargs; i++) {
+ arg = args[i];
if (dead_temps[arg]) {
- dead_iargs |= (1 << i);
+ dead_args |= (1 << i);
}
dead_temps[arg] = 0;
}
- s->op_dead_iargs[op_index] = dead_iargs;
+ s->op_dead_args[op_index] = dead_args;
}
break;
}
@@ -1352,8 +1352,8 @@ static void tcg_liveness_analysis(TCGContext *s)
int nb_ops;
nb_ops = gen_opc_ptr - gen_opc_buf;
- s->op_dead_iargs = tcg_malloc(nb_ops * sizeof(uint16_t));
- memset(s->op_dead_iargs, 0, nb_ops * sizeof(uint16_t));
+ s->op_dead_args = tcg_malloc(nb_ops * sizeof(uint16_t));
+ memset(s->op_dead_args, 0, nb_ops * sizeof(uint16_t));
}
#endif
@@ -1557,7 +1557,7 @@ static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs)
save_globals(s, allocated_regs);
}
-#define IS_DEAD_IARG(n) ((dead_iargs >> (n)) & 1)
+#define IS_DEAD_ARG(n) ((dead_args >> (n)) & 1)
static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args)
{
@@ -1582,7 +1582,7 @@ static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args)
static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def,
const TCGArg *args,
- unsigned int dead_iargs)
+ unsigned int dead_args)
{
TCGTemp *ts, *ots;
int reg;
@@ -1592,9 +1592,9 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def,
ts = &s->temps[args[1]];
arg_ct = &def->args_ct[0];
- /* XXX: always mark arg dead if IS_DEAD_IARG(0) */
+ /* XXX: always mark arg dead if IS_DEAD_ARG(1) */
if (ts->val_type == TEMP_VAL_REG) {
- if (IS_DEAD_IARG(0) && !ts->fixed_reg && !ots->fixed_reg) {
+ if (IS_DEAD_ARG(1) && !ts->fixed_reg && !ots->fixed_reg) {
/* the mov can be suppressed */
if (ots->val_type == TEMP_VAL_REG)
s->reg_to_temp[ots->reg] = -1;
@@ -1642,7 +1642,7 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def,
static void tcg_reg_alloc_op(TCGContext *s,
const TCGOpDef *def, TCGOpcode opc,
const TCGArg *args,
- unsigned int dead_iargs)
+ unsigned int dead_args)
{
TCGRegSet allocated_regs;
int i, k, nb_iargs, nb_oargs, reg;
@@ -1701,8 +1701,9 @@ static void tcg_reg_alloc_op(TCGContext *s,
/* if the input is aliased to an output and if it is
not dead after the instruction, we must allocate
a new register and move it */
- if (!IS_DEAD_IARG(i - nb_oargs))
+ if (!IS_DEAD_ARG(i)) {
goto allocate_in_reg;
+ }
}
}
reg = ts->reg;
@@ -1725,9 +1726,9 @@ static void tcg_reg_alloc_op(TCGContext *s,
tcg_reg_alloc_bb_end(s, allocated_regs);
} else {
/* mark dead temporaries and free the associated registers */
- for(i = 0; i < nb_iargs; i++) {
- arg = args[nb_oargs + i];
- if (IS_DEAD_IARG(i)) {
+ for(i = nb_oargs; i < nb_oargs + nb_iargs; i++) {
+ arg = args[i];
+ if (IS_DEAD_ARG(i)) {
ts = &s->temps[arg];
if (!ts->fixed_reg) {
if (ts->val_type == TEMP_VAL_REG)
@@ -1808,7 +1809,7 @@ static void tcg_reg_alloc_op(TCGContext *s,
static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def,
TCGOpcode opc, const TCGArg *args,
- unsigned int dead_iargs)
+ unsigned int dead_args)
{
int nb_iargs, nb_oargs, flags, nb_regs, i, reg, nb_params;
TCGArg arg, func_arg;
@@ -1930,9 +1931,9 @@ static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def,
/* mark dead temporaries and free the associated registers */
- for(i = 0; i < nb_iargs; i++) {
- arg = args[nb_oargs + i];
- if (IS_DEAD_IARG(i)) {
+ for(i = nb_oargs; i < nb_iargs + nb_oargs; i++) {
+ arg = args[i];
+ if (IS_DEAD_ARG(i)) {
ts = &s->temps[arg];
if (!ts->fixed_reg) {
if (ts->val_type == TEMP_VAL_REG)
@@ -2007,7 +2008,7 @@ static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf,
TCGOpcode opc;
int op_index;
const TCGOpDef *def;
- unsigned int dead_iargs;
+ unsigned int dead_args;
const TCGArg *args;
#ifdef DEBUG_DISAS
@@ -2058,8 +2059,8 @@ static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf,
#if TCG_TARGET_REG_BITS == 64
case INDEX_op_mov_i64:
#endif
- dead_iargs = s->op_dead_iargs[op_index];
- tcg_reg_alloc_mov(s, def, args, dead_iargs);
+ dead_args = s->op_dead_args[op_index];
+ tcg_reg_alloc_mov(s, def, args, dead_args);
break;
case INDEX_op_movi_i32:
#if TCG_TARGET_REG_BITS == 64
@@ -2095,8 +2096,8 @@ static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf,
tcg_out_label(s, args[0], (long)s->code_ptr);
break;
case INDEX_op_call:
- dead_iargs = s->op_dead_iargs[op_index];
- args += tcg_reg_alloc_call(s, def, opc, args, dead_iargs);
+ dead_args = s->op_dead_args[op_index];
+ args += tcg_reg_alloc_call(s, def, opc, args, dead_args);
goto next;
case INDEX_op_end:
goto the_end;
@@ -2104,8 +2105,8 @@ static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf,
/* Note: in order to speed up the code, it would be much
faster to have specialized register allocator functions for
some common argument patterns */
- dead_iargs = s->op_dead_iargs[op_index];
- tcg_reg_alloc_op(s, def, opc, args, dead_iargs);
+ dead_args = s->op_dead_args[op_index];
+ tcg_reg_alloc_op(s, def, opc, args, dead_args);
break;
}
args += def->nb_args;
diff --git a/tcg/tcg.h b/tcg/tcg.h
index cecef63..2b985ac 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -286,8 +286,8 @@ struct TCGContext {
uint16_t *tb_jmp_offset; /* != NULL if USE_DIRECT_JUMP */
/* liveness analysis */
- uint16_t *op_dead_iargs; /* for each operation, each bit tells if the
- corresponding input argument is dead */
+ uint16_t *op_dead_args; /* for each operation, each bit tells if the
+ corresponding argument is dead */
/* tells in which temporary a given register is. It does not take
into account fixed registers */
commit 6eba5c82cfeb28bd5056ad0e8923d98519181939
Merge: 5db070e... a57d23e...
Author: Aurelien Jarno <aurelien at aurel32.net>
Date: Mon May 23 22:36:17 2011 +0200
Merge branch 'trivial-patches' of git://repo.or.cz/qemu/stefanha
* 'trivial-patches' of git://repo.or.cz/qemu/stefanha:
Fix typos in comments (chek -> check)
hw/sd.c: Don't complain about SDIO commands CMD52/CMD53
hw/realview.c: Remove duplicate #include line
piix_pci: fix piix3_set_irq_pic()
commit 5db070eb1ab902572e46ed09e5947ffe504522bc
Merge: 05c8a1e... decb471...
Author: Aurelien Jarno <aurelien at aurel32.net>
Date: Mon May 23 22:35:37 2011 +0200
Merge branch 'ppc-next' of git://repo.or.cz/qemu/agraf
* 'ppc-next' of git://repo.or.cz/qemu/agraf:
Fix a bug in mtsr/mtsrin emulation on ppc64
pSeries: Clean up write-only variables
w32: Fix compilation and replace non-portable usage of ulong
commit 05c8a1e423736006580e4dd2bd94d0faafc9afdc
Merge: dcfd14b... fb8b273...
Author: Aurelien Jarno <aurelien at aurel32.net>
Date: Mon May 23 22:33:39 2011 +0200
Merge branch 's390-next' of git://repo.or.cz/qemu/agraf
* 's390-next' of git://repo.or.cz/qemu/agraf:
s390x: complain when allocating ram fails
s390x: fix memory detection for guests > 64GB
s390x: change mapping base to allow guests > 2GB
s390x: Fix debugging for unknown sigp order codes
s390x: build s390x by default
s390x: remove compatibility cc field
s390x: Adjust GDB stub
s390x: translate engine for s390x CPU
s390x: Adjust internal kvm code
s390x: Implement opcode helpers
s390x: helper functions for system emulation
s390x: Shift variables in CPUState for memset(0)
s390x: keep hint on virtio managing size
s390x: make kvm exported functions conditional on kvm
s390x: s390x-linux-user support
tcg: extend max tcg opcodes when using 64-on-32bit
s390x: fix smp support for kvm
commit a57d23e4f7e7e81c839a7b53a973ac71eefe91da
Author: Stefan Weil <weil at mail.berlios.de>
Date: Sat Apr 30 22:49:26 2011 +0200
Fix typos in comments (chek -> check)
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Signed-off-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
diff --git a/exec.c b/exec.c
index a6df2d6..563e974 100644
--- a/exec.c
+++ b/exec.c
@@ -2061,7 +2061,7 @@ void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
/* we modify the TLB cache so that the dirty bit will be set again
when accessing the range */
start1 = (unsigned long)qemu_safe_ram_ptr(start);
- /* Chek that we don't span multiple blocks - this breaks the
+ /* Check that we don't span multiple blocks - this breaks the
address comparisons below. */
if ((unsigned long)qemu_safe_ram_ptr(end - 1) - start1
!= (end - 1) - start) {
diff --git a/target-ppc/STATUS b/target-ppc/STATUS
index 32e7ffa..c8e9018 100644
--- a/target-ppc/STATUS
+++ b/target-ppc/STATUS
@@ -11,7 +11,7 @@ INSN: instruction set.
SPR: special purpose registers set
OK => all SPR registered (but some may be fake)
KO => some SPR are missing or should be removed
- ? => uncheked
+ ? => unchecked
MSR: MSR bits definitions
OK => all MSR bits properly defined
KO => MSR definition is incorrect
commit 39e594dbcd897849f2ca95b3310ea00fff29ea99
Author: Peter Maydell <peter.maydell at linaro.org>
Date: Fri May 20 10:11:53 2011 +0100
hw/sd.c: Don't complain about SDIO commands CMD52/CMD53
The SDIO specification introduces new commands 52 and 53.
Handle as illegal command but do not complain on stderr,
as SDIO-aware OSes (including Linux) may legitimately use
these in their probing for presence of an SDIO card.
Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
diff --git a/hw/sd.c b/hw/sd.c
index f44a970..cedfb20 100644
--- a/hw/sd.c
+++ b/hw/sd.c
@@ -1104,6 +1104,17 @@ static sd_rsp_type_t sd_normal_command(SDState *sd,
}
break;
+ case 52:
+ case 53:
+ /* CMD52, CMD53: reserved for SDIO cards
+ * (see the SDIO Simplified Specification V2.0)
+ * Handle as illegal command but do not complain
+ * on stderr, as some OSes may use these in their
+ * probing for presence of an SDIO card.
+ */
+ sd->card_status |= ILLEGAL_COMMAND;
+ return sd_r0;
+
/* Application specific commands (Class 8) */
case 55: /* CMD55: APP_CMD */
if (sd->rca != rca)
commit 1cd087251a488f8731efee4118332d0d6abdd815
Author: Peter Maydell <peter.maydell at linaro.org>
Date: Thu May 19 16:21:57 2011 +0100
hw/realview.c: Remove duplicate #include line
Remove a duplicate #include of sysbus.h.
Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
Signed-off-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
diff --git a/hw/realview.c b/hw/realview.c
index 96fb9da..82f3d82 100644
--- a/hw/realview.c
+++ b/hw/realview.c
@@ -17,7 +17,6 @@
#include "sysemu.h"
#include "boards.h"
#include "bitbang_i2c.h"
-#include "sysbus.h"
#include "blockdev.h"
#define SMP_BOOT_ADDR 0xe0000000
commit 09de0f469c3c2a277c7874f6c60992c8b94719a9
Author: TeLeMan <geleman at gmail.com>
Date: Mon May 16 19:50:55 2011 +0800
piix_pci: fix piix3_set_irq_pic()
If pic_irq is greater than 7, the irq level is always 0 on 32bits.
Signed-off-by: TeLeMan <geleman at gmail.com>
Signed-off-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
index 7f1c4cc..85a320e 100644
--- a/hw/piix_pci.c
+++ b/hw/piix_pci.c
@@ -312,7 +312,7 @@ static void piix3_set_irq_pic(PIIX3State *piix3, int pic_irq)
{
qemu_set_irq(piix3->pic[pic_irq],
!!(piix3->pic_levels &
- (((1UL << PIIX_NUM_PIRQS) - 1) <<
+ (((1ULL << PIIX_NUM_PIRQS) - 1) <<
(pic_irq * PIIX_NUM_PIRQS))));
}
commit dcfd14b3741983c466ad92fa2ae91eeafce3e5d5
Author: Blue Swirl <blauwirbel at gmail.com>
Date: Sat May 14 11:55:30 2011 +0000
Delete unused tb_invalidate_page_range
tb_invalidate_page_range() was intended to be used to invalidate an
area of a TB which the guest explicitly flushes from i-cache. However,
QEMU detects writes to code areas where TBs have been generated, so
his has never been useful.
Delete the function, adjust callers.
Reviewed-by: Richard Henderson <rth at twiddle.net>
Signed-off-by: Blue Swirl <blauwirbel at gmail.com>
diff --git a/cpu-exec.c b/cpu-exec.c
index 7323f3c..16a223e 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -704,19 +704,6 @@ int cpu_exec(CPUState *env1)
return ret;
}
-/* must only be called from the generated code as an exception can be
- generated */
-void tb_invalidate_page_range(target_ulong start, target_ulong end)
-{
- /* XXX: cannot enable it yet because it yields to MMU exception
- where NIP != read address on PowerPC */
-#if 0
- target_ulong phys_addr;
- phys_addr = get_phys_addr_code(env, start);
- tb_invalidate_phys_page_range(phys_addr, phys_addr + end - start, 0);
-#endif
-}
-
#if defined(TARGET_I386) && defined(CONFIG_USER_ONLY)
void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
diff --git a/exec-all.h b/exec-all.h
index 7c2d29f..cf3a704 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -95,7 +95,6 @@ void QEMU_NORETURN cpu_loop_exit(void);
int page_unprotect(target_ulong address, unsigned long pc, void *puc);
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
int is_cpu_write_access);
-void tb_invalidate_page_range(target_ulong start, target_ulong end);
void tlb_flush_page(CPUState *env, target_ulong addr);
void tlb_flush(CPUState *env, int flush_global);
#if !defined(CONFIG_USER_ONLY)
diff --git a/linux-user/main.c b/linux-user/main.c
index a4996e7..8336639 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -455,24 +455,6 @@ void cpu_loop(CPUX86State *env)
#ifdef TARGET_ARM
-static void arm_cache_flush(abi_ulong start, abi_ulong last)
-{
- abi_ulong addr, last1;
-
- if (last < start)
- return;
- addr = start;
- for(;;) {
- last1 = ((addr + TARGET_PAGE_SIZE) & TARGET_PAGE_MASK) - 1;
- if (last1 > last)
- last1 = last;
- tb_invalidate_page_range(addr, last1 + 1);
- if (last1 == last)
- break;
- addr = last1 + 1;
- }
-}
-
/* Handle a jump to the kernel code page. */
static int
do_kernel_trap(CPUARMState *env)
@@ -717,7 +699,7 @@ void cpu_loop(CPUARMState *env)
}
if (n == ARM_NR_cacheflush) {
- arm_cache_flush(env->regs[0], env->regs[1]);
+ /* nop */
} else if (n == ARM_NR_semihosting
|| n == ARM_NR_thumb_semihosting) {
env->regs[0] = do_arm_semihosting (env);
@@ -733,7 +715,7 @@ void cpu_loop(CPUARMState *env)
if ( n > ARM_NR_BASE) {
switch (n) {
case ARM_NR_cacheflush:
- arm_cache_flush(env->regs[0], env->regs[1]);
+ /* nop */
break;
case ARM_NR_set_tls:
cpu_set_tls(env, env->regs[0]);
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
index e165444..c52a371 100644
--- a/target-ppc/op_helper.c
+++ b/target-ppc/op_helper.c
@@ -362,7 +362,6 @@ void helper_icbi(target_ulong addr)
* do the load "by hand".
*/
ldl(addr);
- tb_invalidate_page_range(addr, addr + env->icache_line_size);
}
// XXX: to be tested
diff --git a/target-sparc/helper.h b/target-sparc/helper.h
index 12e8557..023f4d6 100644
--- a/target-sparc/helper.h
+++ b/target-sparc/helper.h
@@ -35,7 +35,6 @@ DEF_HELPER_2(check_align, void, tl, i32)
DEF_HELPER_0(debug, void)
DEF_HELPER_0(save, void)
DEF_HELPER_0(restore, void)
-DEF_HELPER_1(flush, void, tl)
DEF_HELPER_2(udiv, tl, tl, tl)
DEF_HELPER_2(udiv_cc, tl, tl, tl)
DEF_HELPER_2(sdiv, tl, tl, tl)
diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c
index ffffb8c..b38691e 100644
--- a/target-sparc/op_helper.c
+++ b/target-sparc/op_helper.c
@@ -4092,12 +4092,6 @@ void helper_write_softint(uint64_t value)
}
#endif
-void helper_flush(target_ulong addr)
-{
- addr &= ~7;
- tb_invalidate_page_range(addr, addr + 8);
-}
-
#ifdef TARGET_SPARC64
#ifdef DEBUG_PCALL
static const char * const excp_names[0x80] = {
diff --git a/target-sparc/translate.c b/target-sparc/translate.c
index fe99f0b..0cc47e9 100644
--- a/target-sparc/translate.c
+++ b/target-sparc/translate.c
@@ -4226,7 +4226,7 @@ static void disas_sparc_insn(DisasContext * dc)
case 0x3b: /* flush */
if (!((dc)->def->features & CPU_FEATURE_FLUSH))
goto unimp_flush;
- gen_helper_flush(cpu_dst);
+ /* nop */
break;
case 0x3c: /* save */
save_state(dc, cpu_cond);
commit decb471488dd9e7e7ab9957f120cb501c4489f63
Author: David Gibson <david at gibson.dropbear.id.au>
Date: Fri May 20 13:34:59 2011 +1000
Fix a bug in mtsr/mtsrin emulation on ppc64
Early ppc64 CPUs include a hack to partially simulate the ppc32 segment
registers, by translating writes to them into writes to the SLB. This is
not used by any current Linux kernel, but it is used by the openbios used
in the qemu mac99 model.
Commit 81762d6dd0d430d87024f2c83e9c4dcc4329fb7d, cleaning up the SLB
handling introduced a bug in this code, breaking the openbios currently in
qemu. Specifically, there was an off by one error bitshuffling the
register format used by mtsr into the format needed for the SLB load,
causing the flag bits to end up in the wrong place. This caused the
storage keys to be wrong under openbios, meaning that the translation code
incorrectly thought a legitimate access was a permission violation.
This patch fixes the bug, at the same time it fixes some build bug in the
MMU debugging code (only exposed when DEBUG_MMU is enabled).
Signed-off-by: David Gibson <david at gibson.dropbear.id.au>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
index 4238be6..4700632 100644
--- a/target-ppc/helper.c
+++ b/target-ppc/helper.c
@@ -606,7 +606,7 @@ static inline int _find_pte(CPUState *env, mmu_ctx_t *ctx, int is_64b, int h,
r = pte64_check(ctx, pte0, pte1, h, rw, type);
LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " "
TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n",
- pteg_base + (i * 16), pte0, pte1, (int)(pte0 & 1), h,
+ pteg_off + (i * 16), pte0, pte1, (int)(pte0 & 1), h,
(int)((pte0 >> 1) & 1), ctx->ptem);
} else
#endif
@@ -621,7 +621,7 @@ static inline int _find_pte(CPUState *env, mmu_ctx_t *ctx, int is_64b, int h,
r = pte32_check(ctx, pte0, pte1, h, rw, type);
LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " "
TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n",
- pteg_base + (i * 8), pte0, pte1, (int)(pte0 >> 31), h,
+ pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h,
(int)((pte0 >> 6) & 1), ctx->ptem);
}
switch (r) {
@@ -918,8 +918,7 @@ static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
if (eaddr != 0xEFFFFFFF)
LOG_MMU("1 htab=" TARGET_FMT_plx "/" TARGET_FMT_plx
" vsid=" TARGET_FMT_lx " api=" TARGET_FMT_lx
- " hash=" TARGET_FMT_plx " pg_addr="
- TARGET_FMT_plx "\n", env->htab_base,
+ " hash=" TARGET_FMT_plx "\n", env->htab_base,
env->htab_mask, vsid, ctx->ptem, ctx->hash[1]);
ret2 = find_pte(env, ctx, 1, rw, type,
target_page_bits);
@@ -2140,7 +2139,7 @@ void ppc_store_sr (CPUPPCState *env, int srnum, target_ulong value)
/* VSID = VSID */
rs |= (value & 0xfffffff) << 12;
/* flags = flags */
- rs |= ((value >> 27) & 0xf) << 9;
+ rs |= ((value >> 27) & 0xf) << 8;
ppc_store_slb(env, rb, rs);
} else
commit 1235a9cf179df04cd0dc30fce0089161d18f3168
Author: David Gibson <david at gibson.dropbear.id.au>
Date: Tue May 17 16:47:04 2011 +1000
pSeries: Clean up write-only variables
A few pieces of the pSeries emulation code have variables which are set
but never used, which causes warnings on gcc 4.6. This patch removes
these instances.
Signed-off-by: David Gibson <david at gibson.dropbear.id.au>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c
index 5281ba2..43c441d 100644
--- a/hw/spapr_hcall.c
+++ b/hw/spapr_hcall.c
@@ -100,22 +100,18 @@ 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 porder;
- target_ulong i, pa;
+ target_ulong i;
uint8_t *hpte;
/* only handle 4k and 16M pages for now */
- porder = 12;
if (pteh & HPTE_V_LARGE) {
#if 0 /* We don't support 64k pages yet */
if ((ptel & 0xf000) == 0x1000) {
/* 64k page */
- porder = 16;
} else
#endif
if ((ptel & 0xff000) == 0) {
/* 16M page */
- porder = 24;
/* lowest AVA bit must be 0 for 16M pages */
if (pteh & 0x80) {
return H_PARAMETER;
@@ -125,7 +121,6 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr,
}
}
- pa = ptel & HPTE_R_RPN;
/* FIXME: bounds check the pa? */
/* Check WIMG */
diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c
index ff3a78f..c18efc7 100644
--- a/hw/spapr_llan.c
+++ b/hw/spapr_llan.c
@@ -185,9 +185,6 @@ static NetClientInfo net_spapr_vlan_info = {
static int spapr_vlan_init(VIOsPAPRDevice *sdev)
{
VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev;
- VIOsPAPRBus *bus;
-
- bus = DO_UPCAST(VIOsPAPRBus, bus, sdev->qdev.parent_bus);
qemu_macaddr_default_if_unset(&dev->nicconf.macaddr);
commit 6d42fb313bc517f1b23daea867efb9968874745e
Author: Stefan Weil <weil at mail.berlios.de>
Date: Sat May 14 23:38:04 2011 +0000
w32: Fix compilation and replace non-portable usage of ulong
ulong is undefined for w32 (and maybe other) compilations.
Replace it by uintptr_t (which also fixes compilation for w64
and is a better choice for pointer to integer conversions).
Cc: Aurelien Jarno <aurelien at aurel32.net>
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Reviewed-by: Aurelien Jarno <aurelien at aurel32.net>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 7a6a7df..8e4582f 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -1929,8 +1929,8 @@ static inline void cpu_set_tls(CPUState *env, target_ulong newtls)
#if !defined(CONFIG_USER_ONLY)
static inline int booke206_tlbe_id(CPUState *env, ppcemb_tlb_t *tlbe)
{
- ulong tlbel = (ulong)tlbe;
- ulong tlbl = (ulong)env->tlb;
+ uintptr_t tlbel = (uintptr_t)tlbe;
+ uintptr_t tlbl = (uintptr_t)env->tlb;
return (tlbel - tlbl) / sizeof(env->tlb[0]);
}
commit fb8b273579eaa1e6cee4017e4b23104e17a36f07
Author: Alexander Graf <agraf at suse.de>
Date: Fri May 20 17:33:28 2011 +0200
s390x: complain when allocating ram fails
While trying out the > 64GB guest RAM patch, I hit some virtual address
limitations of my host system, which resulted in mmap failing. Unfortunately,
qemu didn't tell me about this failure, but just used the NULL pointer
happily, resulting in either segmentation faults or other fun errors.
To spare other users from tracing this down, let's print a nice message
instead so the user can figure out what's wrong from there.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/exec.c b/exec.c
index 3f96d44..a4785b2 100644
--- a/exec.c
+++ b/exec.c
@@ -2918,6 +2918,10 @@ ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name,
new_block->host = mmap((void*)0x800000000, size,
PROT_EXEC|PROT_READ|PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+ if (new_block->host == MAP_FAILED) {
+ fprintf(stderr, "Allocating RAM failed\n");
+ abort();
+ }
#else
if (xen_mapcache_enabled()) {
xen_ram_alloc(new_block->offset, size);
commit 22486aa04aa300db0cc5887d9612b4d486f0edac
Author: Christian Borntraeger <borntraeger at de.ibm.com>
Date: Thu May 12 10:50:44 2011 +0200
s390x: fix memory detection for guests > 64GB
the s390 memory detection has a 16bit field that specifies the amount of
increments. This patch adopts the memory size to always fit into that
scheme. This also fixes virtio detection for these guests, since the
descriptor page is located after the main memory.
Signed-off-by: Christian Borntraeger <borntraeger at de.ibm.com>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c
index 698ff6f..3eba7ea 100644
--- a/hw/s390-virtio.c
+++ b/hw/s390-virtio.c
@@ -131,7 +131,7 @@ int s390_virtio_hypercall(CPUState *env, uint64_t mem, uint64_t hypercall)
}
/* PC hardware initialisation */
-static void s390_init(ram_addr_t ram_size,
+static void s390_init(ram_addr_t my_ram_size,
const char *boot_device,
const char *kernel_filename,
const char *kernel_cmdline,
@@ -143,19 +143,29 @@ static void s390_init(ram_addr_t ram_size,
ram_addr_t kernel_size = 0;
ram_addr_t initrd_offset;
ram_addr_t initrd_size = 0;
+ int shift = 0;
uint8_t *storage_keys;
int i;
+ /* s390x ram size detection needs a 16bit multiplier + an increment. So
+ guests > 64GB can be specified in 2MB steps etc. */
+ while ((my_ram_size >> (20 + shift)) > 65535) {
+ shift++;
+ }
+ my_ram_size = my_ram_size >> (20 + shift) << (20 + shift);
+
+ /* lets propagate the changed ram size into the global variable. */
+ ram_size = my_ram_size;
/* get a BUS */
- s390_bus = s390_virtio_bus_init(&ram_size);
+ s390_bus = s390_virtio_bus_init(&my_ram_size);
/* allocate RAM */
- ram_addr = qemu_ram_alloc(NULL, "s390.ram", ram_size);
- cpu_register_physical_memory(0, ram_size, ram_addr);
+ ram_addr = qemu_ram_alloc(NULL, "s390.ram", my_ram_size);
+ cpu_register_physical_memory(0, my_ram_size, ram_addr);
/* allocate storage keys */
- storage_keys = qemu_mallocz(ram_size / TARGET_PAGE_SIZE);
+ storage_keys = qemu_mallocz(my_ram_size / TARGET_PAGE_SIZE);
/* init CPUs */
if (cpu_model == NULL) {
diff --git a/target-s390x/op_helper.c b/target-s390x/op_helper.c
index 9153940..49760a4 100644
--- a/target-s390x/op_helper.c
+++ b/target-s390x/op_helper.c
@@ -2361,6 +2361,7 @@ static void ext_interrupt(CPUState *env, int type, uint32_t param,
int sclp_service_call(CPUState *env, uint32_t sccb, uint64_t code)
{
int r = 0;
+ int shift = 0;
#ifdef DEBUG_HELPER
printf("sclp(0x%x, 0x%" PRIx64 ")\n", sccb, code);
@@ -2375,8 +2376,11 @@ int sclp_service_call(CPUState *env, uint32_t sccb, uint64_t code)
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);
+ while ((ram_size >> (20 + shift)) > 65535) {
+ shift++;
+ }
+ stw_phys(sccb + SCP_MEM_CODE, ram_size >> (20 + shift));
+ stb_phys(sccb + SCP_INCREMENT, 1 << shift);
stw_phys(sccb + SCP_RESPONSE_CODE, 0x10);
if (kvm_enabled()) {
commit ff83678aee5269619338645ec23b00b1c7506fca
Author: Christian Borntraeger <borntraeger at de.ibm.com>
Date: Tue May 10 14:49:10 2011 +0200
s390x: change mapping base to allow guests > 2GB
the current s390x qemu memory layout is
0x1000000: guest start
0x80000000: qemu binary
which limits the amount of available memory to <2GB.
This patch moves the guest pages to 32GB to not collide with the binary
and to leave some space for the program break of qemu.
Signed-off-by: Christian Borntraeger <borntraeger at de.ibm.com>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/exec.c b/exec.c
index a6df2d6..3f96d44 100644
--- a/exec.c
+++ b/exec.c
@@ -2910,10 +2910,14 @@ ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name,
#endif
} else {
#if defined(TARGET_S390X) && defined(CONFIG_KVM)
- /* XXX S390 KVM requires the topmost vma of the RAM to be < 256GB */
- new_block->host = mmap((void*)0x1000000, size,
+ /* S390 KVM requires the topmost vma of the RAM to be smaller than
+ an system defined value, which is at least 256GB. Larger systems
+ have larger values. We put the guest between the end of data
+ segment (system break) and this value. We use 32GB as a base to
+ have enough room for the system break to grow. */
+ new_block->host = mmap((void*)0x800000000, size,
PROT_EXEC|PROT_READ|PROT_WRITE,
- MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
#else
if (xen_mapcache_enabled()) {
xen_ram_alloc(new_block->offset, size);
commit a74cdab44d6d7a5077991bd2c8b426c2978a80da
Author: Christian Borntraeger <borntraeger at de.ibm.com>
Date: Wed May 4 10:30:12 2011 +0200
s390x: Fix debugging for unknown sigp order codes
On unknown sigp order codes we print a debug message. This patch
fixes the output, since we want to see the order_code and not
the register numbers.
Patch applies on agraf tree.
Signed-off-by: Christian Borntraeger <borntraeger at de.ibm.com>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index c927e61..4beb794 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -377,7 +377,7 @@ static int handle_sigp(CPUState *env, struct kvm_run *run, uint8_t ipa1)
r = s390_cpu_initial_reset(target_env);
break;
default:
- fprintf(stderr, "KVM: unknown SIGP: 0x%x\n", ipa1);
+ fprintf(stderr, "KVM: unknown SIGP: 0x%x\n", order_code);
break;
}
commit 0f3301d406bb84def304bcdce833629fd7d2858a
Author: Alexander Graf <agraf at suse.de>
Date: Wed Mar 23 08:23:47 2011 +0100
s390x: build s390x by default
This patch enables building of s390x-softmmu and s390x-linux-user
targets by default.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/configure b/configure
index d7dba5d..bc54293 100755
--- a/configure
+++ b/configure
@@ -848,6 +848,7 @@ sh4-softmmu \
sh4eb-softmmu \
sparc-softmmu \
sparc64-softmmu \
+s390x-softmmu \
"
fi
# the following are Linux specific
@@ -873,6 +874,7 @@ sparc-linux-user \
sparc64-linux-user \
sparc32plus-linux-user \
unicore32-linux-user \
+s390x-linux-user \
"
fi
# the following are Darwin specific
diff --git a/default-configs/s390x-linux-user.mak b/default-configs/s390x-linux-user.mak
new file mode 100644
index 0000000..a243c99
--- /dev/null
+++ b/default-configs/s390x-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for s390x-linux-user
commit fe9b35b78f0116fce8fa1b95ebb3526e31a73086
Author: Alexander Graf <agraf at suse.de>
Date: Wed Apr 13 13:30:00 2011 +0200
s390x: remove compatibility cc field
Remove the now unused cc field that was only required to not break
bisectability.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 125b939..4e5c391 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -67,7 +67,6 @@ typedef struct CPUS390XState {
PSW psw;
- uint32_t cc;
uint32_t cc_op;
uint64_t cc_src;
uint64_t cc_dst;
commit 59467bacfae8113b03123e651d831682ea73c586
Author: Alexander Graf <agraf at suse.de>
Date: Wed Mar 23 10:58:07 2011 +0100
s390x: Adjust GDB stub
We have successfully lazilized cc computation, so we need to manually
trigger its calculation when gdb wants to fetch it. We also changed the
variable name, so writing it writes into a different field now.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/gdbstub.c b/gdbstub.c
index 0838948..ae856f9 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1436,7 +1436,11 @@ static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
/* XXX */
break;
case S390_PC_REGNUM: GET_REGL(env->psw.addr); break;
- case S390_CC_REGNUM: GET_REG32(env->cc); break;
+ case S390_CC_REGNUM:
+ env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst,
+ env->cc_vr);
+ GET_REG32(env->cc_op);
+ break;
}
return 0;
@@ -1462,7 +1466,7 @@ static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
/* XXX */
break;
case S390_PC_REGNUM: env->psw.addr = tmpl; break;
- case S390_CC_REGNUM: env->cc = tmp32; r=4; break;
+ case S390_CC_REGNUM: env->cc_op = tmp32; r=4; break;
}
return r;
commit e023e832d0ac7d863658074cb39be2b78a29ee64
Author: Alexander Graf <agraf at suse.de>
Date: Wed Mar 23 10:58:07 2011 +0100
s390x: translate engine for s390x CPU
This is the main meat part of the patch set. It implements emulation for an
s390x CPU.
The code does all the optimizations that are common for TCG code:
- direct branches
- cc optimization
- unrolling of simple microcode loops
I'm still open for suggestions on speedups of course :).
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index f995384..8e71df3 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -2,6 +2,7 @@
* S/390 translation
*
* Copyright (c) 2009 Ulrich Hecht
+ * Copyright (c) 2010 Alexander Graf
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -16,6 +17,22 @@
* 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+/* #define DEBUG_ILLEGAL_INSTRUCTIONS */
+/* #define DEBUG_INLINE_BRANCHES */
+#define S390X_DEBUG_DISAS
+/* #define S390X_DEBUG_DISAS_VERBOSE */
+
+#ifdef S390X_DEBUG_DISAS_VERBOSE
+# define LOG_DISAS(...) qemu_log(__VA_ARGS__)
+#else
+# define LOG_DISAS(...) do { } while (0)
+#endif
#include "cpu.h"
#include "exec-all.h"
@@ -23,18 +40,60 @@
#include "tcg-op.h"
#include "qemu-log.h"
+/* global register indexes */
+static TCGv_ptr cpu_env;
+
+#include "gen-icount.h"
+#include "helpers.h"
+#define GEN_HELPER 1
+#include "helpers.h"
+
+typedef struct DisasContext DisasContext;
+struct DisasContext {
+ uint64_t pc;
+ int is_jmp;
+ enum cc_op cc_op;
+ struct TranslationBlock *tb;
+};
+
+#define DISAS_EXCP 4
+
+static void gen_op_calc_cc(DisasContext *s);
+
+#ifdef DEBUG_INLINE_BRANCHES
+static uint64_t inline_branch_hit[CC_OP_MAX];
+static uint64_t inline_branch_miss[CC_OP_MAX];
+#endif
+
+static inline void debug_insn(uint64_t insn)
+{
+ LOG_DISAS("insn: 0x%" PRIx64 "\n", insn);
+}
+
+static inline uint64_t pc_to_link_info(DisasContext *s, uint64_t pc)
+{
+ if (!(s->tb->flags & FLAG_MASK_64)) {
+ if (s->tb->flags & FLAG_MASK_32) {
+ return pc | 0x80000000;
+ }
+ }
+ return pc;
+}
+
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]);
+ cpu_fprintf(f, "R%02d=%016" PRIx64, 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=%016" PRIx64, i, *(uint64_t *)&env->fregs[i]);
if ((i % 4) == 3) {
@@ -43,22 +102,5122 @@ void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
cpu_fprintf(f, " ");
}
}
- cpu_fprintf(f, "PSW=mask %016lx addr %016lx cc %02x\n", env->psw.mask, env->psw.addr, env->cc);
+
+ cpu_fprintf(f, "\n");
+
+#ifndef CONFIG_USER_ONLY
+ for (i = 0; i < 16; i++) {
+ cpu_fprintf(f, "C%02d=%016" PRIx64, i, env->cregs[i]);
+ if ((i % 4) == 3) {
+ cpu_fprintf(f, "\n");
+ } else {
+ cpu_fprintf(f, " ");
+ }
+ }
+#endif
+
+ cpu_fprintf(f, "\n");
+
+ if (env->cc_op > 3) {
+ cpu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %15s\n",
+ env->psw.mask, env->psw.addr, cc_name(env->cc_op));
+ } else {
+ cpu_fprintf(f, "PSW=mask %016" PRIx64 " addr %016" PRIx64 " cc %02x\n",
+ env->psw.mask, env->psw.addr, env->cc_op);
+ }
+
+#ifdef DEBUG_INLINE_BRANCHES
+ for (i = 0; i < CC_OP_MAX; i++) {
+ cpu_fprintf(f, " %15s = %10ld\t%10ld\n", cc_name(i),
+ inline_branch_miss[i], inline_branch_hit[i]);
+ }
+#endif
}
+static TCGv_i64 psw_addr;
+static TCGv_i64 psw_mask;
+
+static TCGv_i32 cc_op;
+static TCGv_i64 cc_src;
+static TCGv_i64 cc_dst;
+static TCGv_i64 cc_vr;
+
+static char cpu_reg_names[10*3 + 6*4];
+static TCGv_i64 regs[16];
+
+static uint8_t gen_opc_cc_op[OPC_BUF_SIZE];
+
void s390x_translate_init(void)
{
+ int i;
+ size_t cpu_reg_names_size = sizeof(cpu_reg_names);
+ char *p;
+
+ cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+ psw_addr = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUState, psw.addr),
+ "psw_addr");
+ psw_mask = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUState, psw.mask),
+ "psw_mask");
+
+ cc_op = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUState, cc_op),
+ "cc_op");
+ cc_src = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUState, cc_src),
+ "cc_src");
+ cc_dst = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUState, cc_dst),
+ "cc_dst");
+ cc_vr = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUState, cc_vr),
+ "cc_vr");
+
+ p = cpu_reg_names;
+ for (i = 0; i < 16; i++) {
+ snprintf(p, cpu_reg_names_size, "r%d", i);
+ regs[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, regs[i]), p);
+ p += (i < 10) ? 3 : 4;
+ cpu_reg_names_size -= (i < 10) ? 3 : 4;
+ }
+}
+
+static inline TCGv_i64 load_reg(int reg)
+{
+ TCGv_i64 r = tcg_temp_new_i64();
+ tcg_gen_mov_i64(r, regs[reg]);
+ return r;
+}
+
+static inline TCGv_i64 load_freg(int reg)
+{
+ TCGv_i64 r = tcg_temp_new_i64();
+ tcg_gen_ld_i64(r, cpu_env, offsetof(CPUState, fregs[reg].d));
+ return r;
+}
+
+static inline TCGv_i32 load_freg32(int reg)
+{
+ TCGv_i32 r = tcg_temp_new_i32();
+ tcg_gen_ld_i32(r, cpu_env, offsetof(CPUState, fregs[reg].l.upper));
+ return r;
+}
+
+static inline TCGv_i32 load_reg32(int reg)
+{
+ TCGv_i32 r = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(r, regs[reg]);
+ return r;
+}
+
+static inline TCGv_i64 load_reg32_i64(int reg)
+{
+ TCGv_i64 r = tcg_temp_new_i64();
+ tcg_gen_ext32s_i64(r, regs[reg]);
+ return r;
+}
+
+static inline void store_reg(int reg, TCGv_i64 v)
+{
+ tcg_gen_mov_i64(regs[reg], v);
+}
+
+static inline void store_freg(int reg, TCGv_i64 v)
+{
+ tcg_gen_st_i64(v, cpu_env, offsetof(CPUState, fregs[reg].d));
+}
+
+static inline void store_reg32(int reg, TCGv_i32 v)
+{
+#if HOST_LONG_BITS == 32
+ tcg_gen_mov_i32(TCGV_LOW(regs[reg]), v);
+#else
+ TCGv_i64 tmp = tcg_temp_new_i64();
+ tcg_gen_extu_i32_i64(tmp, v);
+ /* 32 bit register writes keep the upper half */
+ tcg_gen_deposit_i64(regs[reg], regs[reg], tmp, 0, 32);
+ tcg_temp_free_i64(tmp);
+#endif
+}
+
+static inline void store_reg32_i64(int reg, TCGv_i64 v)
+{
+ /* 32 bit register writes keep the upper half */
+#if HOST_LONG_BITS == 32
+ tcg_gen_mov_i32(TCGV_LOW(regs[reg]), TCGV_LOW(v));
+#else
+ tcg_gen_deposit_i64(regs[reg], regs[reg], v, 0, 32);
+#endif
+}
+
+static inline void store_reg16(int reg, TCGv_i32 v)
+{
+ TCGv_i64 tmp = tcg_temp_new_i64();
+ tcg_gen_extu_i32_i64(tmp, v);
+ /* 16 bit register writes keep the upper bytes */
+ tcg_gen_deposit_i64(regs[reg], regs[reg], tmp, 0, 16);
+ tcg_temp_free_i64(tmp);
+}
+
+static inline void store_reg8(int reg, TCGv_i64 v)
+{
+ /* 8 bit register writes keep the upper bytes */
+ tcg_gen_deposit_i64(regs[reg], regs[reg], v, 0, 8);
+}
+
+static inline void store_freg32(int reg, TCGv_i32 v)
+{
+ tcg_gen_st_i32(v, cpu_env, offsetof(CPUState, fregs[reg].l.upper));
+}
+
+static inline void update_psw_addr(DisasContext *s)
+{
+ /* psw.addr */
+ tcg_gen_movi_i64(psw_addr, s->pc);
+}
+
+static inline void potential_page_fault(DisasContext *s)
+{
+#ifndef CONFIG_USER_ONLY
+ update_psw_addr(s);
+ gen_op_calc_cc(s);
+#endif
+}
+
+static inline uint64_t ld_code2(uint64_t pc)
+{
+ return (uint64_t)lduw_code(pc);
+}
+
+static inline uint64_t ld_code4(uint64_t pc)
+{
+ return (uint64_t)ldl_code(pc);
+}
+
+static inline uint64_t ld_code6(uint64_t pc)
+{
+ uint64_t opc;
+ opc = (uint64_t)lduw_code(pc) << 32;
+ opc |= (uint64_t)(uint32_t)ldl_code(pc+2);
+ return opc;
+}
+
+static inline int get_mem_index(DisasContext *s)
+{
+ switch (s->tb->flags & FLAG_MASK_ASC) {
+ case PSW_ASC_PRIMARY >> 32:
+ return 0;
+ case PSW_ASC_SECONDARY >> 32:
+ return 1;
+ case PSW_ASC_HOME >> 32:
+ return 2;
+ default:
+ tcg_abort();
+ break;
+ }
+}
+
+static inline void gen_debug(DisasContext *s)
+{
+ TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG);
+ update_psw_addr(s);
+ gen_op_calc_cc(s);
+ gen_helper_exception(tmp);
+ tcg_temp_free_i32(tmp);
+ s->is_jmp = DISAS_EXCP;
+}
+
+#ifdef CONFIG_USER_ONLY
+
+static void gen_illegal_opcode(DisasContext *s, int ilc)
+{
+ TCGv_i32 tmp = tcg_const_i32(EXCP_SPEC);
+ update_psw_addr(s);
+ gen_op_calc_cc(s);
+ gen_helper_exception(tmp);
+ tcg_temp_free_i32(tmp);
+ s->is_jmp = DISAS_EXCP;
+}
+
+#else /* CONFIG_USER_ONLY */
+
+static void debug_print_inst(DisasContext *s, int ilc)
+{
+#ifdef DEBUG_ILLEGAL_INSTRUCTIONS
+ uint64_t inst = 0;
+
+ switch (ilc & 3) {
+ case 1:
+ inst = ld_code2(s->pc);
+ break;
+ case 2:
+ inst = ld_code4(s->pc);
+ break;
+ case 3:
+ inst = ld_code6(s->pc);
+ break;
+ }
+
+ fprintf(stderr, "Illegal instruction [%d at %016" PRIx64 "]: 0x%016"
+ PRIx64 "\n", ilc, s->pc, inst);
+#endif
+}
+
+static void gen_program_exception(DisasContext *s, int ilc, int code)
+{
+ TCGv_i32 tmp;
+
+ debug_print_inst(s, ilc);
+
+ /* remember what pgm exeption this was */
+ tmp = tcg_const_i32(code);
+ tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, int_pgm_code));
+ tcg_temp_free_i32(tmp);
+
+ tmp = tcg_const_i32(ilc);
+ tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, int_pgm_ilc));
+ tcg_temp_free_i32(tmp);
+
+ /* advance past instruction */
+ s->pc += (ilc * 2);
+ update_psw_addr(s);
+
+ /* save off cc */
+ gen_op_calc_cc(s);
+
+ /* trigger exception */
+ tmp = tcg_const_i32(EXCP_PGM);
+ gen_helper_exception(tmp);
+ tcg_temp_free_i32(tmp);
+
+ /* end TB here */
+ s->is_jmp = DISAS_EXCP;
+}
+
+
+static void gen_illegal_opcode(DisasContext *s, int ilc)
+{
+ gen_program_exception(s, ilc, PGM_SPECIFICATION);
+}
+
+static void gen_privileged_exception(DisasContext *s, int ilc)
+{
+ gen_program_exception(s, ilc, PGM_PRIVILEGED);
+}
+
+static void check_privileged(DisasContext *s, int ilc)
+{
+ if (s->tb->flags & (PSW_MASK_PSTATE >> 32)) {
+ gen_privileged_exception(s, ilc);
+ }
+}
+
+#endif /* CONFIG_USER_ONLY */
+
+static TCGv_i64 get_address(DisasContext *s, int x2, int b2, int d2)
+{
+ TCGv_i64 tmp;
+
+ /* 31-bitify the immediate part; register contents are dealt with below */
+ if (!(s->tb->flags & FLAG_MASK_64)) {
+ d2 &= 0x7fffffffUL;
+ }
+
+ if (x2) {
+ if (d2) {
+ tmp = tcg_const_i64(d2);
+ tcg_gen_add_i64(tmp, tmp, regs[x2]);
+ } else {
+ tmp = load_reg(x2);
+ }
+ if (b2) {
+ tcg_gen_add_i64(tmp, tmp, regs[b2]);
+ }
+ } else if (b2) {
+ if (d2) {
+ tmp = tcg_const_i64(d2);
+ tcg_gen_add_i64(tmp, tmp, regs[b2]);
+ } else {
+ tmp = load_reg(b2);
+ }
+ } else {
+ tmp = tcg_const_i64(d2);
+ }
+
+ /* 31-bit mode mask if there are values loaded from registers */
+ if (!(s->tb->flags & FLAG_MASK_64) && (x2 || b2)) {
+ tcg_gen_andi_i64(tmp, tmp, 0x7fffffffUL);
+ }
+
+ return tmp;
+}
+
+static void gen_op_movi_cc(DisasContext *s, uint32_t val)
+{
+ s->cc_op = CC_OP_CONST0 + val;
+}
+
+static void gen_op_update1_cc_i64(DisasContext *s, enum cc_op op, TCGv_i64 dst)
+{
+ tcg_gen_discard_i64(cc_src);
+ tcg_gen_mov_i64(cc_dst, dst);
+ tcg_gen_discard_i64(cc_vr);
+ s->cc_op = op;
+}
+
+static void gen_op_update1_cc_i32(DisasContext *s, enum cc_op op, TCGv_i32 dst)
+{
+ tcg_gen_discard_i64(cc_src);
+ tcg_gen_extu_i32_i64(cc_dst, dst);
+ tcg_gen_discard_i64(cc_vr);
+ s->cc_op = op;
+}
+
+static void gen_op_update2_cc_i64(DisasContext *s, enum cc_op op, TCGv_i64 src,
+ TCGv_i64 dst)
+{
+ tcg_gen_mov_i64(cc_src, src);
+ tcg_gen_mov_i64(cc_dst, dst);
+ tcg_gen_discard_i64(cc_vr);
+ s->cc_op = op;
+}
+
+static void gen_op_update2_cc_i32(DisasContext *s, enum cc_op op, TCGv_i32 src,
+ TCGv_i32 dst)
+{
+ tcg_gen_extu_i32_i64(cc_src, src);
+ tcg_gen_extu_i32_i64(cc_dst, dst);
+ tcg_gen_discard_i64(cc_vr);
+ s->cc_op = op;
+}
+
+static void gen_op_update3_cc_i64(DisasContext *s, enum cc_op op, TCGv_i64 src,
+ TCGv_i64 dst, TCGv_i64 vr)
+{
+ tcg_gen_mov_i64(cc_src, src);
+ tcg_gen_mov_i64(cc_dst, dst);
+ tcg_gen_mov_i64(cc_vr, vr);
+ s->cc_op = op;
+}
+
+static void gen_op_update3_cc_i32(DisasContext *s, enum cc_op op, TCGv_i32 src,
+ TCGv_i32 dst, TCGv_i32 vr)
+{
+ tcg_gen_extu_i32_i64(cc_src, src);
+ tcg_gen_extu_i32_i64(cc_dst, dst);
+ tcg_gen_extu_i32_i64(cc_vr, vr);
+ s->cc_op = op;
+}
+
+static inline void set_cc_nz_u32(DisasContext *s, TCGv_i32 val)
+{
+ gen_op_update1_cc_i32(s, CC_OP_NZ, val);
+}
+
+static inline void set_cc_nz_u64(DisasContext *s, TCGv_i64 val)
+{
+ gen_op_update1_cc_i64(s, CC_OP_NZ, val);
+}
+
+static inline void cmp_32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2,
+ enum cc_op cond)
+{
+ gen_op_update2_cc_i32(s, cond, v1, v2);
+}
+
+static inline void cmp_64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2,
+ enum cc_op cond)
+{
+ gen_op_update2_cc_i64(s, cond, v1, v2);
+}
+
+static inline void cmp_s32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2)
+{
+ cmp_32(s, v1, v2, CC_OP_LTGT_32);
+}
+
+static inline void cmp_u32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2)
+{
+ cmp_32(s, v1, v2, CC_OP_LTUGTU_32);
+}
+
+static inline void cmp_s32c(DisasContext *s, TCGv_i32 v1, int32_t v2)
+{
+ /* XXX optimize for the constant? put it in s? */
+ TCGv_i32 tmp = tcg_const_i32(v2);
+ cmp_32(s, v1, tmp, CC_OP_LTGT_32);
+ tcg_temp_free_i32(tmp);
+}
+
+static inline void cmp_u32c(DisasContext *s, TCGv_i32 v1, uint32_t v2)
+{
+ TCGv_i32 tmp = tcg_const_i32(v2);
+ cmp_32(s, v1, tmp, CC_OP_LTUGTU_32);
+ tcg_temp_free_i32(tmp);
+}
+
+static inline void cmp_s64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2)
+{
+ cmp_64(s, v1, v2, CC_OP_LTGT_64);
+}
+
+static inline void cmp_u64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2)
+{
+ cmp_64(s, v1, v2, CC_OP_LTUGTU_64);
+}
+
+static inline void cmp_s64c(DisasContext *s, TCGv_i64 v1, int64_t v2)
+{
+ TCGv_i64 tmp = tcg_const_i64(v2);
+ cmp_s64(s, v1, tmp);
+ tcg_temp_free_i64(tmp);
+}
+
+static inline void cmp_u64c(DisasContext *s, TCGv_i64 v1, uint64_t v2)
+{
+ TCGv_i64 tmp = tcg_const_i64(v2);
+ cmp_u64(s, v1, tmp);
+ tcg_temp_free_i64(tmp);
+}
+
+static inline void set_cc_s32(DisasContext *s, TCGv_i32 val)
+{
+ gen_op_update1_cc_i32(s, CC_OP_LTGT0_32, val);
+}
+
+static inline void set_cc_s64(DisasContext *s, TCGv_i64 val)
+{
+ gen_op_update1_cc_i64(s, CC_OP_LTGT0_64, val);
+}
+
+static void set_cc_add64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2, TCGv_i64 vr)
+{
+ gen_op_update3_cc_i64(s, CC_OP_ADD_64, v1, v2, vr);
+}
+
+static void set_cc_addu64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2,
+ TCGv_i64 vr)
+{
+ gen_op_update3_cc_i64(s, CC_OP_ADDU_64, v1, v2, vr);
+}
+
+static void set_cc_sub64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2, TCGv_i64 vr)
+{
+ gen_op_update3_cc_i64(s, CC_OP_SUB_64, v1, v2, vr);
+}
+
+static void set_cc_subu64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2,
+ TCGv_i64 vr)
+{
+ gen_op_update3_cc_i64(s, CC_OP_SUBU_64, v1, v2, vr);
+}
+
+static void set_cc_abs64(DisasContext *s, TCGv_i64 v1)
+{
+ gen_op_update1_cc_i64(s, CC_OP_ABS_64, v1);
+}
+
+static void set_cc_nabs64(DisasContext *s, TCGv_i64 v1)
+{
+ gen_op_update1_cc_i64(s, CC_OP_NABS_64, v1);
+}
+
+static void set_cc_add32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2, TCGv_i32 vr)
+{
+ gen_op_update3_cc_i32(s, CC_OP_ADD_32, v1, v2, vr);
+}
+
+static void set_cc_addu32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2,
+ TCGv_i32 vr)
+{
+ gen_op_update3_cc_i32(s, CC_OP_ADDU_32, v1, v2, vr);
+}
+
+static void set_cc_sub32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2, TCGv_i32 vr)
+{
+ gen_op_update3_cc_i32(s, CC_OP_SUB_32, v1, v2, vr);
+}
+
+static void set_cc_subu32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2,
+ TCGv_i32 vr)
+{
+ gen_op_update3_cc_i32(s, CC_OP_SUBU_32, v1, v2, vr);
+}
+
+static void set_cc_abs32(DisasContext *s, TCGv_i32 v1)
+{
+ gen_op_update1_cc_i32(s, CC_OP_ABS_32, v1);
+}
+
+static void set_cc_nabs32(DisasContext *s, TCGv_i32 v1)
+{
+ gen_op_update1_cc_i32(s, CC_OP_NABS_32, v1);
+}
+
+static void set_cc_comp32(DisasContext *s, TCGv_i32 v1)
+{
+ gen_op_update1_cc_i32(s, CC_OP_COMP_32, v1);
+}
+
+static void set_cc_comp64(DisasContext *s, TCGv_i64 v1)
+{
+ gen_op_update1_cc_i64(s, CC_OP_COMP_64, v1);
+}
+
+static void set_cc_icm(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2)
+{
+ gen_op_update2_cc_i32(s, CC_OP_ICM, v1, v2);
+}
+
+static void set_cc_cmp_f32_i64(DisasContext *s, TCGv_i32 v1, TCGv_i64 v2)
+{
+ tcg_gen_extu_i32_i64(cc_src, v1);
+ tcg_gen_mov_i64(cc_dst, v2);
+ tcg_gen_discard_i64(cc_vr);
+ s->cc_op = CC_OP_LTGT_F32;
+}
+
+static void set_cc_nz_f32(DisasContext *s, TCGv_i32 v1)
+{
+ gen_op_update1_cc_i32(s, CC_OP_NZ_F32, v1);
+}
+
+static inline void set_cc_nz_f64(DisasContext *s, TCGv_i64 v1)
+{
+ gen_op_update1_cc_i64(s, CC_OP_NZ_F64, v1);
+}
+
+/* CC value is in env->cc_op */
+static inline void set_cc_static(DisasContext *s)
+{
+ tcg_gen_discard_i64(cc_src);
+ tcg_gen_discard_i64(cc_dst);
+ tcg_gen_discard_i64(cc_vr);
+ s->cc_op = CC_OP_STATIC;
+}
+
+static inline void gen_op_set_cc_op(DisasContext *s)
+{
+ if (s->cc_op != CC_OP_DYNAMIC && s->cc_op != CC_OP_STATIC) {
+ tcg_gen_movi_i32(cc_op, s->cc_op);
+ }
+}
+
+static inline void gen_update_cc_op(DisasContext *s)
+{
+ gen_op_set_cc_op(s);
+}
+
+/* calculates cc into cc_op */
+static void gen_op_calc_cc(DisasContext *s)
+{
+ TCGv_i32 local_cc_op = tcg_const_i32(s->cc_op);
+ TCGv_i64 dummy = tcg_const_i64(0);
+
+ switch (s->cc_op) {
+ case CC_OP_CONST0:
+ case CC_OP_CONST1:
+ case CC_OP_CONST2:
+ case CC_OP_CONST3:
+ /* s->cc_op is the cc value */
+ tcg_gen_movi_i32(cc_op, s->cc_op - CC_OP_CONST0);
+ break;
+ case CC_OP_STATIC:
+ /* env->cc_op already is the cc value */
+ break;
+ case CC_OP_NZ:
+ case CC_OP_ABS_64:
+ case CC_OP_NABS_64:
+ case CC_OP_ABS_32:
+ case CC_OP_NABS_32:
+ case CC_OP_LTGT0_32:
+ case CC_OP_LTGT0_64:
+ case CC_OP_COMP_32:
+ case CC_OP_COMP_64:
+ case CC_OP_NZ_F32:
+ case CC_OP_NZ_F64:
+ /* 1 argument */
+ gen_helper_calc_cc(cc_op, local_cc_op, dummy, cc_dst, dummy);
+ break;
+ case CC_OP_ICM:
+ case CC_OP_LTGT_32:
+ case CC_OP_LTGT_64:
+ case CC_OP_LTUGTU_32:
+ case CC_OP_LTUGTU_64:
+ case CC_OP_TM_32:
+ case CC_OP_TM_64:
+ case CC_OP_LTGT_F32:
+ case CC_OP_LTGT_F64:
+ case CC_OP_SLAG:
+ /* 2 arguments */
+ gen_helper_calc_cc(cc_op, local_cc_op, cc_src, cc_dst, dummy);
+ break;
+ case CC_OP_ADD_64:
+ case CC_OP_ADDU_64:
+ case CC_OP_SUB_64:
+ case CC_OP_SUBU_64:
+ case CC_OP_ADD_32:
+ case CC_OP_ADDU_32:
+ case CC_OP_SUB_32:
+ case CC_OP_SUBU_32:
+ /* 3 arguments */
+ gen_helper_calc_cc(cc_op, local_cc_op, cc_src, cc_dst, cc_vr);
+ break;
+ case CC_OP_DYNAMIC:
+ /* unknown operation - assume 3 arguments and cc_op in env */
+ gen_helper_calc_cc(cc_op, cc_op, cc_src, cc_dst, cc_vr);
+ break;
+ default:
+ tcg_abort();
+ }
+
+ tcg_temp_free_i32(local_cc_op);
+
+ /* We now have cc in cc_op as constant */
+ set_cc_static(s);
+}
+
+static inline void decode_rr(DisasContext *s, uint64_t insn, int *r1, int *r2)
+{
+ debug_insn(insn);
+
+ *r1 = (insn >> 4) & 0xf;
+ *r2 = insn & 0xf;
+}
+
+static inline TCGv_i64 decode_rx(DisasContext *s, uint64_t insn, int *r1,
+ int *x2, int *b2, int *d2)
+{
+ debug_insn(insn);
+
+ *r1 = (insn >> 20) & 0xf;
+ *x2 = (insn >> 16) & 0xf;
+ *b2 = (insn >> 12) & 0xf;
+ *d2 = insn & 0xfff;
+
+ return get_address(s, *x2, *b2, *d2);
+}
+
+static inline void decode_rs(DisasContext *s, uint64_t insn, int *r1, int *r3,
+ int *b2, int *d2)
+{
+ debug_insn(insn);
+
+ *r1 = (insn >> 20) & 0xf;
+ /* aka m3 */
+ *r3 = (insn >> 16) & 0xf;
+ *b2 = (insn >> 12) & 0xf;
+ *d2 = insn & 0xfff;
+}
+
+static inline TCGv_i64 decode_si(DisasContext *s, uint64_t insn, int *i2,
+ int *b1, int *d1)
+{
+ debug_insn(insn);
+
+ *i2 = (insn >> 16) & 0xff;
+ *b1 = (insn >> 12) & 0xf;
+ *d1 = insn & 0xfff;
+
+ return get_address(s, 0, *b1, *d1);
+}
+
+static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong pc)
+{
+ TranslationBlock *tb;
+
+ gen_update_cc_op(s);
+
+ tb = s->tb;
+ /* NOTE: we handle the case where the TB spans two pages here */
+ if ((pc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK) ||
+ (pc & TARGET_PAGE_MASK) == ((s->pc - 1) & TARGET_PAGE_MASK)) {
+ /* jump to same page: we can use a direct jump */
+ tcg_gen_goto_tb(tb_num);
+ tcg_gen_movi_i64(psw_addr, pc);
+ tcg_gen_exit_tb((long)tb + tb_num);
+ } else {
+ /* jump to another page: currently not optimized */
+ tcg_gen_movi_i64(psw_addr, pc);
+ tcg_gen_exit_tb(0);
+ }
+}
+
+static inline void account_noninline_branch(DisasContext *s, int cc_op)
+{
+#ifdef DEBUG_INLINE_BRANCHES
+ inline_branch_miss[cc_op]++;
+#endif
+}
+
+static inline void account_inline_branch(DisasContext *s)
+{
+#ifdef DEBUG_INLINE_BRANCHES
+ inline_branch_hit[s->cc_op]++;
+#endif
+}
+
+static void gen_jcc(DisasContext *s, uint32_t mask, int skip)
+{
+ TCGv_i32 tmp, tmp2, r;
+ TCGv_i64 tmp64;
+ int old_cc_op;
+
+ switch (s->cc_op) {
+ case CC_OP_LTGT0_32:
+ tmp = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(tmp, cc_dst);
+ switch (mask) {
+ case 0x8 | 0x4: /* dst <= 0 */
+ tcg_gen_brcondi_i32(TCG_COND_GT, tmp, 0, skip);
+ break;
+ case 0x8 | 0x2: /* dst >= 0 */
+ tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, skip);
+ break;
+ case 0x8: /* dst == 0 */
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, skip);
+ break;
+ case 0x7: /* dst != 0 */
+ case 0x6: /* dst != 0 */
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, skip);
+ break;
+ case 0x4: /* dst < 0 */
+ tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, skip);
+ break;
+ case 0x2: /* dst > 0 */
+ tcg_gen_brcondi_i32(TCG_COND_LE, tmp, 0, skip);
+ break;
+ default:
+ tcg_temp_free_i32(tmp);
+ goto do_dynamic;
+ }
+ account_inline_branch(s);
+ tcg_temp_free_i32(tmp);
+ break;
+ case CC_OP_LTGT0_64:
+ switch (mask) {
+ case 0x8 | 0x4: /* dst <= 0 */
+ tcg_gen_brcondi_i64(TCG_COND_GT, cc_dst, 0, skip);
+ break;
+ case 0x8 | 0x2: /* dst >= 0 */
+ tcg_gen_brcondi_i64(TCG_COND_LT, cc_dst, 0, skip);
+ break;
+ case 0x8: /* dst == 0 */
+ tcg_gen_brcondi_i64(TCG_COND_NE, cc_dst, 0, skip);
+ break;
+ case 0x7: /* dst != 0 */
+ case 0x6: /* dst != 0 */
+ tcg_gen_brcondi_i64(TCG_COND_EQ, cc_dst, 0, skip);
+ break;
+ case 0x4: /* dst < 0 */
+ tcg_gen_brcondi_i64(TCG_COND_GE, cc_dst, 0, skip);
+ break;
+ case 0x2: /* dst > 0 */
+ tcg_gen_brcondi_i64(TCG_COND_LE, cc_dst, 0, skip);
+ break;
+ default:
+ goto do_dynamic;
+ }
+ account_inline_branch(s);
+ break;
+ case CC_OP_LTGT_32:
+ tmp = tcg_temp_new_i32();
+ tmp2 = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(tmp, cc_src);
+ tcg_gen_trunc_i64_i32(tmp2, cc_dst);
+ switch (mask) {
+ case 0x8 | 0x4: /* src <= dst */
+ tcg_gen_brcond_i32(TCG_COND_GT, tmp, tmp2, skip);
+ break;
+ case 0x8 | 0x2: /* src >= dst */
+ tcg_gen_brcond_i32(TCG_COND_LT, tmp, tmp2, skip);
+ break;
+ case 0x8: /* src == dst */
+ tcg_gen_brcond_i32(TCG_COND_NE, tmp, tmp2, skip);
+ break;
+ case 0x7: /* src != dst */
+ case 0x6: /* src != dst */
+ tcg_gen_brcond_i32(TCG_COND_EQ, tmp, tmp2, skip);
+ break;
+ case 0x4: /* src < dst */
+ tcg_gen_brcond_i32(TCG_COND_GE, tmp, tmp2, skip);
+ break;
+ case 0x2: /* src > dst */
+ tcg_gen_brcond_i32(TCG_COND_LE, tmp, tmp2, skip);
+ break;
+ default:
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(tmp2);
+ goto do_dynamic;
+ }
+ account_inline_branch(s);
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(tmp2);
+ break;
+ case CC_OP_LTGT_64:
+ switch (mask) {
+ case 0x8 | 0x4: /* src <= dst */
+ tcg_gen_brcond_i64(TCG_COND_GT, cc_src, cc_dst, skip);
+ break;
+ case 0x8 | 0x2: /* src >= dst */
+ tcg_gen_brcond_i64(TCG_COND_LT, cc_src, cc_dst, skip);
+ break;
+ case 0x8: /* src == dst */
+ tcg_gen_brcond_i64(TCG_COND_NE, cc_src, cc_dst, skip);
+ break;
+ case 0x7: /* src != dst */
+ case 0x6: /* src != dst */
+ tcg_gen_brcond_i64(TCG_COND_EQ, cc_src, cc_dst, skip);
+ break;
+ case 0x4: /* src < dst */
+ tcg_gen_brcond_i64(TCG_COND_GE, cc_src, cc_dst, skip);
+ break;
+ case 0x2: /* src > dst */
+ tcg_gen_brcond_i64(TCG_COND_LE, cc_src, cc_dst, skip);
+ break;
+ default:
+ goto do_dynamic;
+ }
+ account_inline_branch(s);
+ break;
+ case CC_OP_LTUGTU_32:
+ tmp = tcg_temp_new_i32();
+ tmp2 = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(tmp, cc_src);
+ tcg_gen_trunc_i64_i32(tmp2, cc_dst);
+ switch (mask) {
+ case 0x8 | 0x4: /* src <= dst */
+ tcg_gen_brcond_i32(TCG_COND_GTU, tmp, tmp2, skip);
+ break;
+ case 0x8 | 0x2: /* src >= dst */
+ tcg_gen_brcond_i32(TCG_COND_LTU, tmp, tmp2, skip);
+ break;
+ case 0x8: /* src == dst */
+ tcg_gen_brcond_i32(TCG_COND_NE, tmp, tmp2, skip);
+ break;
+ case 0x7: /* src != dst */
+ case 0x6: /* src != dst */
+ tcg_gen_brcond_i32(TCG_COND_EQ, tmp, tmp2, skip);
+ break;
+ case 0x4: /* src < dst */
+ tcg_gen_brcond_i32(TCG_COND_GEU, tmp, tmp2, skip);
+ break;
+ case 0x2: /* src > dst */
+ tcg_gen_brcond_i32(TCG_COND_LEU, tmp, tmp2, skip);
+ break;
+ default:
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(tmp2);
+ goto do_dynamic;
+ }
+ account_inline_branch(s);
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(tmp2);
+ break;
+ case CC_OP_LTUGTU_64:
+ switch (mask) {
+ case 0x8 | 0x4: /* src <= dst */
+ tcg_gen_brcond_i64(TCG_COND_GTU, cc_src, cc_dst, skip);
+ break;
+ case 0x8 | 0x2: /* src >= dst */
+ tcg_gen_brcond_i64(TCG_COND_LTU, cc_src, cc_dst, skip);
+ break;
+ case 0x8: /* src == dst */
+ tcg_gen_brcond_i64(TCG_COND_NE, cc_src, cc_dst, skip);
+ break;
+ case 0x7: /* src != dst */
+ case 0x6: /* src != dst */
+ tcg_gen_brcond_i64(TCG_COND_EQ, cc_src, cc_dst, skip);
+ break;
+ case 0x4: /* src < dst */
+ tcg_gen_brcond_i64(TCG_COND_GEU, cc_src, cc_dst, skip);
+ break;
+ case 0x2: /* src > dst */
+ tcg_gen_brcond_i64(TCG_COND_LEU, cc_src, cc_dst, skip);
+ break;
+ default:
+ goto do_dynamic;
+ }
+ account_inline_branch(s);
+ break;
+ case CC_OP_NZ:
+ switch (mask) {
+ /* dst == 0 || dst != 0 */
+ case 0x8 | 0x4:
+ case 0x8 | 0x4 | 0x2:
+ case 0x8 | 0x4 | 0x2 | 0x1:
+ case 0x8 | 0x4 | 0x1:
+ break;
+ /* dst == 0 */
+ case 0x8:
+ case 0x8 | 0x2:
+ case 0x8 | 0x2 | 0x1:
+ case 0x8 | 0x1:
+ tcg_gen_brcondi_i64(TCG_COND_NE, cc_dst, 0, skip);
+ break;
+ /* dst != 0 */
+ case 0x4:
+ case 0x4 | 0x2:
+ case 0x4 | 0x2 | 0x1:
+ case 0x4 | 0x1:
+ tcg_gen_brcondi_i64(TCG_COND_EQ, cc_dst, 0, skip);
+ break;
+ default:
+ goto do_dynamic;
+ }
+ account_inline_branch(s);
+ break;
+ case CC_OP_TM_32:
+ tmp = tcg_temp_new_i32();
+ tmp2 = tcg_temp_new_i32();
+
+ tcg_gen_trunc_i64_i32(tmp, cc_src);
+ tcg_gen_trunc_i64_i32(tmp2, cc_dst);
+ tcg_gen_and_i32(tmp, tmp, tmp2);
+ switch (mask) {
+ case 0x8: /* val & mask == 0 */
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, skip);
+ break;
+ case 0x4 | 0x2 | 0x1: /* val & mask != 0 */
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, skip);
+ break;
+ default:
+ goto do_dynamic;
+ }
+ tcg_temp_free_i32(tmp);
+ account_inline_branch(s);
+ break;
+ case CC_OP_TM_64:
+ tmp64 = tcg_temp_new_i64();
+
+ tcg_gen_and_i64(tmp64, cc_src, cc_dst);
+ switch (mask) {
+ case 0x8: /* val & mask == 0 */
+ tcg_gen_brcondi_i64(TCG_COND_NE, tmp64, 0, skip);
+ break;
+ case 0x4 | 0x2 | 0x1: /* val & mask != 0 */
+ tcg_gen_brcondi_i64(TCG_COND_EQ, tmp64, 0, skip);
+ break;
+ default:
+ goto do_dynamic;
+ }
+ tcg_temp_free_i64(tmp64);
+ account_inline_branch(s);
+ break;
+ case CC_OP_ICM:
+ switch (mask) {
+ case 0x8: /* val == 0 */
+ tcg_gen_brcondi_i64(TCG_COND_NE, cc_dst, 0, skip);
+ break;
+ case 0x4 | 0x2 | 0x1: /* val != 0 */
+ case 0x4 | 0x2: /* val != 0 */
+ tcg_gen_brcondi_i64(TCG_COND_EQ, cc_dst, 0, skip);
+ break;
+ default:
+ goto do_dynamic;
+ }
+ account_inline_branch(s);
+ break;
+ case CC_OP_STATIC:
+ old_cc_op = s->cc_op;
+ goto do_dynamic_nocccalc;
+ case CC_OP_DYNAMIC:
+ default:
+do_dynamic:
+ old_cc_op = s->cc_op;
+ /* calculate cc value */
+ gen_op_calc_cc(s);
+
+do_dynamic_nocccalc:
+ /* jump based on cc */
+ account_noninline_branch(s, old_cc_op);
+
+ switch (mask) {
+ case 0x8 | 0x4 | 0x2 | 0x1:
+ /* always true */
+ break;
+ case 0x8 | 0x4 | 0x2: /* cc != 3 */
+ tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 3, skip);
+ break;
+ case 0x8 | 0x4 | 0x1: /* cc != 2 */
+ tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 2, skip);
+ break;
+ case 0x8 | 0x2 | 0x1: /* cc != 1 */
+ tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 1, skip);
+ break;
+ case 0x8 | 0x2: /* cc == 0 ||Â cc == 2 */
+ tmp = tcg_temp_new_i32();
+ tcg_gen_andi_i32(tmp, cc_op, 1);
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, skip);
+ tcg_temp_free_i32(tmp);
+ break;
+ case 0x8 | 0x4: /* cc < 2 */
+ tcg_gen_brcondi_i32(TCG_COND_GEU, cc_op, 2, skip);
+ break;
+ case 0x8: /* cc == 0 */
+ tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 0, skip);
+ break;
+ case 0x4 | 0x2 | 0x1: /* cc != 0 */
+ tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 0, skip);
+ break;
+ case 0x4 | 0x1: /* cc == 1 ||Â cc == 3 */
+ tmp = tcg_temp_new_i32();
+ tcg_gen_andi_i32(tmp, cc_op, 1);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, skip);
+ tcg_temp_free_i32(tmp);
+ break;
+ case 0x4: /* cc == 1 */
+ tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 1, skip);
+ break;
+ case 0x2 | 0x1: /* cc > 1 */
+ tcg_gen_brcondi_i32(TCG_COND_LEU, cc_op, 1, skip);
+ break;
+ case 0x2: /* cc == 2 */
+ tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 2, skip);
+ break;
+ case 0x1: /* cc == 3 */
+ tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 3, skip);
+ break;
+ default: /* cc is masked by something else */
+ tmp = tcg_const_i32(3);
+ /* 3 - cc */
+ tcg_gen_sub_i32(tmp, tmp, cc_op);
+ tmp2 = tcg_const_i32(1);
+ /* 1 << (3 - cc) */
+ tcg_gen_shl_i32(tmp2, tmp2, tmp);
+ r = tcg_const_i32(mask);
+ /* mask & (1 << (3 - cc)) */
+ tcg_gen_and_i32(r, r, tmp2);
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(tmp2);
+
+ tcg_gen_brcondi_i32(TCG_COND_EQ, r, 0, skip);
+ tcg_temp_free_i32(r);
+ break;
+ }
+ break;
+ }
+}
+
+static void gen_bcr(DisasContext *s, uint32_t mask, TCGv_i64 target,
+ uint64_t offset)
+{
+ int skip;
+
+ if (mask == 0xf) {
+ /* unconditional */
+ tcg_gen_mov_i64(psw_addr, target);
+ tcg_gen_exit_tb(0);
+ } else if (mask == 0) {
+ /* ignore cc and never match */
+ gen_goto_tb(s, 0, offset + 2);
+ } else {
+ TCGv_i64 new_addr = tcg_temp_local_new_i64();
+
+ tcg_gen_mov_i64(new_addr, target);
+ skip = gen_new_label();
+ gen_jcc(s, mask, skip);
+ tcg_gen_mov_i64(psw_addr, new_addr);
+ tcg_temp_free_i64(new_addr);
+ tcg_gen_exit_tb(0);
+ gen_set_label(skip);
+ tcg_temp_free_i64(new_addr);
+ gen_goto_tb(s, 1, offset + 2);
+ }
+}
+
+static void gen_brc(uint32_t mask, DisasContext *s, int32_t offset)
+{
+ int skip;
+
+ if (mask == 0xf) {
+ /* unconditional */
+ gen_goto_tb(s, 0, s->pc + offset);
+ } else if (mask == 0) {
+ /* ignore cc and never match */
+ gen_goto_tb(s, 0, s->pc + 4);
+ } else {
+ skip = gen_new_label();
+ gen_jcc(s, mask, skip);
+ gen_goto_tb(s, 0, s->pc + offset);
+ gen_set_label(skip);
+ gen_goto_tb(s, 1, s->pc + 4);
+ }
+ s->is_jmp = DISAS_TB_JUMP;
+}
+
+static void gen_op_mvc(DisasContext *s, int l, TCGv_i64 s1, TCGv_i64 s2)
+{
+ TCGv_i64 tmp, tmp2;
+ int i;
+ int l_memset = gen_new_label();
+ int l_out = gen_new_label();
+ TCGv_i64 dest = tcg_temp_local_new_i64();
+ TCGv_i64 src = tcg_temp_local_new_i64();
+ TCGv_i32 vl;
+
+ /* Find out if we should use the inline version of mvc */
+ switch (l) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 11:
+ case 15:
+ /* use inline */
+ break;
+ default:
+ /* Fall back to helper */
+ vl = tcg_const_i32(l);
+ potential_page_fault(s);
+ gen_helper_mvc(vl, s1, s2);
+ tcg_temp_free_i32(vl);
+ return;
+ }
+
+ tcg_gen_mov_i64(dest, s1);
+ tcg_gen_mov_i64(src, s2);
+
+ if (!(s->tb->flags & FLAG_MASK_64)) {
+ /* XXX what if we overflow while moving? */
+ tcg_gen_andi_i64(dest, dest, 0x7fffffffUL);
+ tcg_gen_andi_i64(src, src, 0x7fffffffUL);
+ }
+
+ tmp = tcg_temp_new_i64();
+ tcg_gen_addi_i64(tmp, src, 1);
+ tcg_gen_brcond_i64(TCG_COND_EQ, dest, tmp, l_memset);
+ tcg_temp_free_i64(tmp);
+
+ switch (l) {
+ case 0:
+ tmp = tcg_temp_new_i64();
+
+ tcg_gen_qemu_ld8u(tmp, src, get_mem_index(s));
+ tcg_gen_qemu_st8(tmp, dest, get_mem_index(s));
+
+ tcg_temp_free_i64(tmp);
+ break;
+ case 1:
+ tmp = tcg_temp_new_i64();
+
+ tcg_gen_qemu_ld16u(tmp, src, get_mem_index(s));
+ tcg_gen_qemu_st16(tmp, dest, get_mem_index(s));
+
+ tcg_temp_free_i64(tmp);
+ break;
+ case 3:
+ tmp = tcg_temp_new_i64();
+
+ tcg_gen_qemu_ld32u(tmp, src, get_mem_index(s));
+ tcg_gen_qemu_st32(tmp, dest, get_mem_index(s));
+
+ tcg_temp_free_i64(tmp);
+ break;
+ case 4:
+ tmp = tcg_temp_new_i64();
+ tmp2 = tcg_temp_new_i64();
+
+ tcg_gen_qemu_ld32u(tmp, src, get_mem_index(s));
+ tcg_gen_addi_i64(src, src, 4);
+ tcg_gen_qemu_ld8u(tmp2, src, get_mem_index(s));
+ tcg_gen_qemu_st32(tmp, dest, get_mem_index(s));
+ tcg_gen_addi_i64(dest, dest, 4);
+ tcg_gen_qemu_st8(tmp2, dest, get_mem_index(s));
+
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 7:
+ tmp = tcg_temp_new_i64();
+
+ tcg_gen_qemu_ld64(tmp, src, get_mem_index(s));
+ tcg_gen_qemu_st64(tmp, dest, get_mem_index(s));
+
+ tcg_temp_free_i64(tmp);
+ break;
+ default:
+ /* The inline version can become too big for too uneven numbers, only
+ use it on known good lengths */
+ tmp = tcg_temp_new_i64();
+ tmp2 = tcg_const_i64(8);
+ for (i = 0; (i + 7) <= l; i += 8) {
+ tcg_gen_qemu_ld64(tmp, src, get_mem_index(s));
+ tcg_gen_qemu_st64(tmp, dest, get_mem_index(s));
+
+ tcg_gen_add_i64(src, src, tmp2);
+ tcg_gen_add_i64(dest, dest, tmp2);
+ }
+
+ tcg_temp_free_i64(tmp2);
+ tmp2 = tcg_const_i64(1);
+
+ for (; i <= l; i++) {
+ tcg_gen_qemu_ld8u(tmp, src, get_mem_index(s));
+ tcg_gen_qemu_st8(tmp, dest, get_mem_index(s));
+
+ tcg_gen_add_i64(src, src, tmp2);
+ tcg_gen_add_i64(dest, dest, tmp2);
+ }
+
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp);
+ break;
+ }
+
+ tcg_gen_br(l_out);
+
+ gen_set_label(l_memset);
+ /* memset case (dest == (src + 1)) */
+
+ tmp = tcg_temp_new_i64();
+ tmp2 = tcg_temp_new_i64();
+ /* fill tmp with the byte */
+ tcg_gen_qemu_ld8u(tmp, src, get_mem_index(s));
+ tcg_gen_shli_i64(tmp2, tmp, 8);
+ tcg_gen_or_i64(tmp, tmp, tmp2);
+ tcg_gen_shli_i64(tmp2, tmp, 16);
+ tcg_gen_or_i64(tmp, tmp, tmp2);
+ tcg_gen_shli_i64(tmp2, tmp, 32);
+ tcg_gen_or_i64(tmp, tmp, tmp2);
+ tcg_temp_free_i64(tmp2);
+
+ tmp2 = tcg_const_i64(8);
+
+ for (i = 0; (i + 7) <= l; i += 8) {
+ tcg_gen_qemu_st64(tmp, dest, get_mem_index(s));
+ tcg_gen_addi_i64(dest, dest, 8);
+ }
+
+ tcg_temp_free_i64(tmp2);
+ tmp2 = tcg_const_i64(1);
+
+ for (; i <= l; i++) {
+ tcg_gen_qemu_st8(tmp, dest, get_mem_index(s));
+ tcg_gen_addi_i64(dest, dest, 1);
+ }
+
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp);
+
+ gen_set_label(l_out);
+
+ tcg_temp_free(dest);
+ tcg_temp_free(src);
+}
+
+static void gen_op_clc(DisasContext *s, int l, TCGv_i64 s1, TCGv_i64 s2)
+{
+ TCGv_i64 tmp;
+ TCGv_i64 tmp2;
+ TCGv_i32 vl;
+
+ /* check for simple 32bit or 64bit match */
+ switch (l) {
+ case 0:
+ tmp = tcg_temp_new_i64();
+ tmp2 = tcg_temp_new_i64();
+
+ tcg_gen_qemu_ld8u(tmp, s1, get_mem_index(s));
+ tcg_gen_qemu_ld8u(tmp2, s2, get_mem_index(s));
+ cmp_u64(s, tmp, tmp2);
+
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ return;
+ case 1:
+ tmp = tcg_temp_new_i64();
+ tmp2 = tcg_temp_new_i64();
+
+ tcg_gen_qemu_ld16u(tmp, s1, get_mem_index(s));
+ tcg_gen_qemu_ld16u(tmp2, s2, get_mem_index(s));
+ cmp_u64(s, tmp, tmp2);
+
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ return;
+ case 3:
+ tmp = tcg_temp_new_i64();
+ tmp2 = tcg_temp_new_i64();
+
+ tcg_gen_qemu_ld32u(tmp, s1, get_mem_index(s));
+ tcg_gen_qemu_ld32u(tmp2, s2, get_mem_index(s));
+ cmp_u64(s, tmp, tmp2);
+
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ return;
+ case 7:
+ tmp = tcg_temp_new_i64();
+ tmp2 = tcg_temp_new_i64();
+
+ tcg_gen_qemu_ld64(tmp, s1, get_mem_index(s));
+ tcg_gen_qemu_ld64(tmp2, s2, get_mem_index(s));
+ cmp_u64(s, tmp, tmp2);
+
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ return;
+ }
+
+ potential_page_fault(s);
+ vl = tcg_const_i32(l);
+ gen_helper_clc(cc_op, vl, s1, s2);
+ tcg_temp_free_i32(vl);
+ set_cc_static(s);
+}
+
+static void disas_e3(DisasContext* s, int op, int r1, int x2, int b2, int d2)
+{
+ TCGv_i64 addr, tmp, tmp2, tmp3, tmp4;
+ TCGv_i32 tmp32_1, tmp32_2, tmp32_3;
+
+ LOG_DISAS("disas_e3: op 0x%x r1 %d x2 %d b2 %d d2 %d\n",
+ op, r1, x2, b2, d2);
+ addr = get_address(s, x2, b2, d2);
+ switch (op) {
+ case 0x2: /* LTG R1,D2(X2,B2) [RXY] */
+ case 0x4: /* lg r1,d2(x2,b2) */
+ tcg_gen_qemu_ld64(regs[r1], addr, get_mem_index(s));
+ if (op == 0x2) {
+ set_cc_s64(s, regs[r1]);
+ }
+ break;
+ case 0x12: /* LT R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_1, tmp2);
+ store_reg32(r1, tmp32_1);
+ set_cc_s32(s, tmp32_1);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0xc: /* MSG R1,D2(X2,B2) [RXY] */
+ case 0x1c: /* MSGF R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ if (op == 0xc) {
+ tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s));
+ } else {
+ tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s));
+ }
+ tcg_gen_mul_i64(regs[r1], regs[r1], tmp2);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0xd: /* DSG R1,D2(X2,B2) [RXY] */
+ case 0x1d: /* DSGF R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ if (op == 0x1d) {
+ tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s));
+ } else {
+ tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s));
+ }
+ tmp4 = load_reg(r1 + 1);
+ tmp3 = tcg_temp_new_i64();
+ tcg_gen_div_i64(tmp3, tmp4, tmp2);
+ store_reg(r1 + 1, tmp3);
+ tcg_gen_rem_i64(tmp3, tmp4, tmp2);
+ store_reg(r1, tmp3);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ tcg_temp_free_i64(tmp4);
+ break;
+ case 0x8: /* AG R1,D2(X2,B2) [RXY] */
+ case 0xa: /* ALG R1,D2(X2,B2) [RXY] */
+ case 0x18: /* AGF R1,D2(X2,B2) [RXY] */
+ case 0x1a: /* ALGF R1,D2(X2,B2) [RXY] */
+ if (op == 0x1a) {
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s));
+ } else if (op == 0x18) {
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s));
+ } else {
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s));
+ }
+ tmp4 = load_reg(r1);
+ tmp3 = tcg_temp_new_i64();
+ tcg_gen_add_i64(tmp3, tmp4, tmp2);
+ store_reg(r1, tmp3);
+ switch (op) {
+ case 0x8:
+ case 0x18:
+ set_cc_add64(s, tmp4, tmp2, tmp3);
+ break;
+ case 0xa:
+ case 0x1a:
+ set_cc_addu64(s, tmp4, tmp2, tmp3);
+ break;
+ default:
+ tcg_abort();
+ }
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ tcg_temp_free_i64(tmp4);
+ break;
+ case 0x9: /* SG R1,D2(X2,B2) [RXY] */
+ case 0xb: /* SLG R1,D2(X2,B2) [RXY] */
+ case 0x19: /* SGF R1,D2(X2,B2) [RXY] */
+ case 0x1b: /* SLGF R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ if (op == 0x19) {
+ tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s));
+ } else if (op == 0x1b) {
+ tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s));
+ } else {
+ tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s));
+ }
+ tmp4 = load_reg(r1);
+ tmp3 = tcg_temp_new_i64();
+ tcg_gen_sub_i64(tmp3, tmp4, tmp2);
+ store_reg(r1, tmp3);
+ switch (op) {
+ case 0x9:
+ case 0x19:
+ set_cc_sub64(s, tmp4, tmp2, tmp3);
+ break;
+ case 0xb:
+ case 0x1b:
+ set_cc_subu64(s, tmp4, tmp2, tmp3);
+ break;
+ default:
+ tcg_abort();
+ }
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ tcg_temp_free_i64(tmp4);
+ break;
+ case 0xf: /* LRVG R1,D2(X2,B2) [RXE] */
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s));
+ tcg_gen_bswap64_i64(tmp2, tmp2);
+ store_reg(r1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x14: /* LGF R1,D2(X2,B2) [RXY] */
+ case 0x16: /* LLGF R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s));
+ if (op == 0x14) {
+ tcg_gen_ext32s_i64(tmp2, tmp2);
+ }
+ store_reg(r1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x15: /* LGH R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld16s(tmp2, addr, get_mem_index(s));
+ store_reg(r1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x17: /* LLGT R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s));
+ tcg_gen_andi_i64(tmp2, tmp2, 0x7fffffffULL);
+ store_reg(r1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x1e: /* LRV R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ tcg_gen_bswap32_i32(tmp32_1, tmp32_1);
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x1f: /* LRVH R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld16u(tmp2, addr, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ tcg_gen_bswap16_i32(tmp32_1, tmp32_1);
+ store_reg16(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x20: /* CG R1,D2(X2,B2) [RXY] */
+ case 0x21: /* CLG R1,D2(X2,B2) */
+ case 0x30: /* CGF R1,D2(X2,B2) [RXY] */
+ case 0x31: /* CLGF R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ switch (op) {
+ case 0x20:
+ case 0x21:
+ tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s));
+ break;
+ case 0x30:
+ tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s));
+ break;
+ case 0x31:
+ tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s));
+ break;
+ default:
+ tcg_abort();
+ }
+ switch (op) {
+ case 0x20:
+ case 0x30:
+ cmp_s64(s, regs[r1], tmp2);
+ break;
+ case 0x21:
+ case 0x31:
+ cmp_u64(s, regs[r1], tmp2);
+ break;
+ default:
+ tcg_abort();
+ }
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x24: /* stg r1, d2(x2,b2) */
+ tcg_gen_qemu_st64(regs[r1], addr, get_mem_index(s));
+ break;
+ case 0x3e: /* STRV R1,D2(X2,B2) [RXY] */
+ tmp32_1 = load_reg32(r1);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_bswap32_i32(tmp32_1, tmp32_1);
+ tcg_gen_extu_i32_i64(tmp2, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_gen_qemu_st32(tmp2, addr, get_mem_index(s));
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x50: /* STY R1,D2(X2,B2) [RXY] */
+ tmp32_1 = load_reg32(r1);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_extu_i32_i64(tmp2, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_gen_qemu_st32(tmp2, addr, get_mem_index(s));
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x57: /* XY R1,D2(X2,B2) [RXY] */
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = tcg_temp_new_i32();
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_2, tmp2);
+ tcg_temp_free_i64(tmp2);
+ tcg_gen_xor_i32(tmp32_2, tmp32_1, tmp32_2);
+ store_reg32(r1, tmp32_2);
+ set_cc_nz_u32(s, tmp32_2);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x58: /* LY R1,D2(X2,B2) [RXY] */
+ tmp3 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld32u(tmp3, addr, get_mem_index(s));
+ store_reg32_i64(r1, tmp3);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x5a: /* AY R1,D2(X2,B2) [RXY] */
+ case 0x5b: /* SY R1,D2(X2,B2) [RXY] */
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = tcg_temp_new_i32();
+ tmp32_3 = tcg_temp_new_i32();
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_2, tmp2);
+ tcg_temp_free_i64(tmp2);
+ switch (op) {
+ case 0x5a:
+ tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2);
+ break;
+ case 0x5b:
+ tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2);
+ break;
+ default:
+ tcg_abort();
+ }
+ store_reg32(r1, tmp32_3);
+ switch (op) {
+ case 0x5a:
+ set_cc_add32(s, tmp32_1, tmp32_2, tmp32_3);
+ break;
+ case 0x5b:
+ set_cc_sub32(s, tmp32_1, tmp32_2, tmp32_3);
+ break;
+ default:
+ tcg_abort();
+ }
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+ case 0x71: /* LAY R1,D2(X2,B2) [RXY] */
+ store_reg(r1, addr);
+ break;
+ case 0x72: /* STCY R1,D2(X2,B2) [RXY] */
+ tmp32_1 = load_reg32(r1);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_ext_i32_i64(tmp2, tmp32_1);
+ tcg_gen_qemu_st8(tmp2, addr, get_mem_index(s));
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x73: /* ICY R1,D2(X2,B2) [RXY] */
+ tmp3 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld8u(tmp3, addr, get_mem_index(s));
+ store_reg8(r1, tmp3);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x76: /* LB R1,D2(X2,B2) [RXY] */
+ case 0x77: /* LGB R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld8s(tmp2, addr, get_mem_index(s));
+ switch (op) {
+ case 0x76:
+ tcg_gen_ext8s_i64(tmp2, tmp2);
+ store_reg32_i64(r1, tmp2);
+ break;
+ case 0x77:
+ tcg_gen_ext8s_i64(tmp2, tmp2);
+ store_reg(r1, tmp2);
+ break;
+ default:
+ tcg_abort();
+ }
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x78: /* LHY R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld16s(tmp2, addr, get_mem_index(s));
+ store_reg32_i64(r1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x80: /* NG R1,D2(X2,B2) [RXY] */
+ case 0x81: /* OG R1,D2(X2,B2) [RXY] */
+ case 0x82: /* XG R1,D2(X2,B2) [RXY] */
+ tmp3 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld64(tmp3, addr, get_mem_index(s));
+ switch (op) {
+ case 0x80:
+ tcg_gen_and_i64(regs[r1], regs[r1], tmp3);
+ break;
+ case 0x81:
+ tcg_gen_or_i64(regs[r1], regs[r1], tmp3);
+ break;
+ case 0x82:
+ tcg_gen_xor_i64(regs[r1], regs[r1], tmp3);
+ break;
+ default:
+ tcg_abort();
+ }
+ set_cc_nz_u64(s, regs[r1]);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x86: /* MLG R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = tcg_const_i32(r1);
+ tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s));
+ gen_helper_mlg(tmp32_1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x87: /* DLG R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = tcg_const_i32(r1);
+ tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s));
+ gen_helper_dlg(tmp32_1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x88: /* ALCG R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tmp3 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s));
+ /* XXX possible optimization point */
+ gen_op_calc_cc(s);
+ tcg_gen_extu_i32_i64(tmp3, cc_op);
+ tcg_gen_shri_i64(tmp3, tmp3, 1);
+ tcg_gen_andi_i64(tmp3, tmp3, 1);
+ tcg_gen_add_i64(tmp3, tmp2, tmp3);
+ tcg_gen_add_i64(tmp3, regs[r1], tmp3);
+ store_reg(r1, tmp3);
+ set_cc_addu64(s, regs[r1], tmp2, tmp3);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x89: /* SLBG R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = tcg_const_i32(r1);
+ tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s));
+ /* XXX possible optimization point */
+ gen_op_calc_cc(s);
+ gen_helper_slbg(cc_op, cc_op, tmp32_1, regs[r1], tmp2);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x90: /* LLGC R1,D2(X2,B2) [RXY] */
+ tcg_gen_qemu_ld8u(regs[r1], addr, get_mem_index(s));
+ break;
+ case 0x91: /* LLGH R1,D2(X2,B2) [RXY] */
+ tcg_gen_qemu_ld16u(regs[r1], addr, get_mem_index(s));
+ break;
+ case 0x94: /* LLC R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld8u(tmp2, addr, get_mem_index(s));
+ store_reg32_i64(r1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x95: /* LLH R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld16u(tmp2, addr, get_mem_index(s));
+ store_reg32_i64(r1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x96: /* ML R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tmp3 = load_reg((r1 + 1) & 15);
+ tcg_gen_ext32u_i64(tmp3, tmp3);
+ tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s));
+ tcg_gen_mul_i64(tmp2, tmp2, tmp3);
+ store_reg32_i64((r1 + 1) & 15, tmp2);
+ tcg_gen_shri_i64(tmp2, tmp2, 32);
+ store_reg32_i64(r1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x97: /* DL R1,D2(X2,B2) [RXY] */
+ /* reg(r1) = reg(r1, r1+1) % ld32(addr) */
+ /* reg(r1+1) = reg(r1, r1+1) / ld32(addr) */
+ tmp = load_reg(r1);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s));
+ tmp3 = load_reg((r1 + 1) & 15);
+ tcg_gen_ext32u_i64(tmp2, tmp2);
+ tcg_gen_ext32u_i64(tmp3, tmp3);
+ tcg_gen_shli_i64(tmp, tmp, 32);
+ tcg_gen_or_i64(tmp, tmp, tmp3);
+
+ tcg_gen_rem_i64(tmp3, tmp, tmp2);
+ tcg_gen_div_i64(tmp, tmp, tmp2);
+ store_reg32_i64((r1 + 1) & 15, tmp);
+ store_reg32_i64(r1, tmp3);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x98: /* ALC R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = tcg_temp_new_i32();
+ tmp32_3 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_2, tmp2);
+ /* XXX possible optimization point */
+ gen_op_calc_cc(s);
+ gen_helper_addc_u32(tmp32_3, cc_op, tmp32_1, tmp32_2);
+ set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3);
+ store_reg32(r1, tmp32_3);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+ case 0x99: /* SLB R1,D2(X2,B2) [RXY] */
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_2, tmp2);
+ /* XXX possible optimization point */
+ gen_op_calc_cc(s);
+ gen_helper_slb(cc_op, cc_op, tmp32_1, tmp32_2);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ default:
+ LOG_DISAS("illegal e3 operation 0x%x\n", op);
+ gen_illegal_opcode(s, 3);
+ break;
+ }
+ tcg_temp_free_i64(addr);
+}
+
+#ifndef CONFIG_USER_ONLY
+static void disas_e5(DisasContext* s, uint64_t insn)
+{
+ TCGv_i64 tmp, tmp2;
+ int op = (insn >> 32) & 0xff;
+
+ tmp = get_address(s, 0, (insn >> 28) & 0xf, (insn >> 16) & 0xfff);
+ tmp2 = get_address(s, 0, (insn >> 12) & 0xf, insn & 0xfff);
+
+ LOG_DISAS("disas_e5: insn %" PRIx64 "\n", insn);
+ switch (op) {
+ case 0x01: /* TPROT D1(B1),D2(B2) [SSE] */
+ /* Test Protection */
+ potential_page_fault(s);
+ gen_helper_tprot(cc_op, tmp, tmp2);
+ set_cc_static(s);
+ break;
+ default:
+ LOG_DISAS("illegal e5 operation 0x%x\n", op);
+ gen_illegal_opcode(s, 3);
+ break;
+ }
+
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+}
+#endif
+
+static void disas_eb(DisasContext *s, int op, int r1, int r3, int b2, int d2)
+{
+ TCGv_i64 tmp, tmp2, tmp3, tmp4;
+ TCGv_i32 tmp32_1, tmp32_2;
+ int i, stm_len;
+ int ilc = 3;
+
+ LOG_DISAS("disas_eb: op 0x%x r1 %d r3 %d b2 %d d2 0x%x\n",
+ op, r1, r3, b2, d2);
+ switch (op) {
+ case 0xc: /* SRLG R1,R3,D2(B2) [RSY] */
+ case 0xd: /* SLLG R1,R3,D2(B2) [RSY] */
+ case 0xa: /* SRAG R1,R3,D2(B2) [RSY] */
+ case 0xb: /* SLAG R1,R3,D2(B2) [RSY] */
+ case 0x1c: /* RLLG R1,R3,D2(B2) [RSY] */
+ if (b2) {
+ tmp = get_address(s, 0, b2, d2);
+ tcg_gen_andi_i64(tmp, tmp, 0x3f);
+ } else {
+ tmp = tcg_const_i64(d2 & 0x3f);
+ }
+ switch (op) {
+ case 0xc:
+ tcg_gen_shr_i64(regs[r1], regs[r3], tmp);
+ break;
+ case 0xd:
+ tcg_gen_shl_i64(regs[r1], regs[r3], tmp);
+ break;
+ case 0xa:
+ tcg_gen_sar_i64(regs[r1], regs[r3], tmp);
+ break;
+ case 0xb:
+ tmp2 = tcg_temp_new_i64();
+ tmp3 = tcg_temp_new_i64();
+ gen_op_update2_cc_i64(s, CC_OP_SLAG, regs[r3], tmp);
+ tcg_gen_shl_i64(tmp2, regs[r3], tmp);
+ /* override sign bit with source sign */
+ tcg_gen_andi_i64(tmp2, tmp2, ~0x8000000000000000ULL);
+ tcg_gen_andi_i64(tmp3, regs[r3], 0x8000000000000000ULL);
+ tcg_gen_or_i64(regs[r1], tmp2, tmp3);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x1c:
+ tcg_gen_rotl_i64(regs[r1], regs[r3], tmp);
+ break;
+ default:
+ tcg_abort();
+ break;
+ }
+ if (op == 0xa) {
+ set_cc_s64(s, regs[r1]);
+ }
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x1d: /* RLL R1,R3,D2(B2) [RSY] */
+ if (b2) {
+ tmp = get_address(s, 0, b2, d2);
+ tcg_gen_andi_i64(tmp, tmp, 0x3f);
+ } else {
+ tmp = tcg_const_i64(d2 & 0x3f);
+ }
+ tmp32_1 = tcg_temp_new_i32();
+ tmp32_2 = load_reg32(r3);
+ tcg_gen_trunc_i64_i32(tmp32_1, tmp);
+ switch (op) {
+ case 0x1d:
+ tcg_gen_rotl_i32(tmp32_1, tmp32_2, tmp32_1);
+ break;
+ default:
+ tcg_abort();
+ break;
+ }
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x4: /* LMG R1,R3,D2(B2) [RSE] */
+ case 0x24: /* STMG R1,R3,D2(B2) [RSE] */
+ stm_len = 8;
+ goto do_mh;
+ case 0x26: /* STMH R1,R3,D2(B2) [RSE] */
+ case 0x96: /* LMH R1,R3,D2(B2) [RSE] */
+ stm_len = 4;
+do_mh:
+ /* Apparently, unrolling lmg/stmg of any size gains performance -
+ even for very long ones... */
+ tmp = get_address(s, 0, b2, d2);
+ tmp3 = tcg_const_i64(stm_len);
+ tmp4 = tcg_const_i64(32);
+ for (i = r1;; i = (i + 1) % 16) {
+ switch (op) {
+ case 0x4:
+ tcg_gen_qemu_ld64(regs[i], tmp, get_mem_index(s));
+ break;
+ case 0x96:
+ tmp2 = tcg_temp_new_i64();
+#if HOST_LONG_BITS == 32
+ tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(TCGV_HIGH(regs[i]), tmp2);
+#else
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s));
+ tcg_gen_shl_i64(tmp2, tmp2, 4);
+ tcg_gen_ext32u_i64(regs[i], regs[i]);
+ tcg_gen_or_i64(regs[i], regs[i], tmp2);
+#endif
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x24:
+ tcg_gen_qemu_st64(regs[i], tmp, get_mem_index(s));
+ break;
+ case 0x26:
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_shr_i64(tmp2, regs[i], tmp4);
+ tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s));
+ tcg_temp_free_i64(tmp2);
+ break;
+ default:
+ tcg_abort();
+ }
+ if (i == r3) {
+ break;
+ }
+ tcg_gen_add_i64(tmp, tmp, tmp3);
+ }
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp4);
+ break;
+ case 0x2c: /* STCMH R1,M3,D2(B2) [RSY] */
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r3);
+ potential_page_fault(s);
+ gen_helper_stcmh(tmp32_1, tmp, tmp32_2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+#ifndef CONFIG_USER_ONLY
+ case 0x2f: /* LCTLG R1,R3,D2(B2) [RSE] */
+ /* Load Control */
+ check_privileged(s, ilc);
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r3);
+ potential_page_fault(s);
+ gen_helper_lctlg(tmp32_1, tmp, tmp32_2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x25: /* STCTG R1,R3,D2(B2) [RSE] */
+ /* Store Control */
+ check_privileged(s, ilc);
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r3);
+ potential_page_fault(s);
+ gen_helper_stctg(tmp32_1, tmp, tmp32_2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+#endif
+ case 0x30: /* CSG R1,R3,D2(B2) [RSY] */
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r3);
+ potential_page_fault(s);
+ /* XXX rewrite in tcg */
+ gen_helper_csg(cc_op, tmp32_1, tmp, tmp32_2);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x3e: /* CDSG R1,R3,D2(B2) [RSY] */
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r3);
+ potential_page_fault(s);
+ /* XXX rewrite in tcg */
+ gen_helper_cdsg(cc_op, tmp32_1, tmp, tmp32_2);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x51: /* TMY D1(B1),I2 [SIY] */
+ tmp = get_address(s, 0, b2, d2); /* SIY -> this is the destination */
+ tmp2 = tcg_const_i64((r1 << 4) | r3);
+ tcg_gen_qemu_ld8u(tmp, tmp, get_mem_index(s));
+ /* yes, this is a 32 bit operation with 64 bit tcg registers, because
+ that incurs less conversions */
+ cmp_64(s, tmp, tmp2, CC_OP_TM_32);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x52: /* MVIY D1(B1),I2 [SIY] */
+ tmp = get_address(s, 0, b2, d2); /* SIY -> this is the destination */
+ tmp2 = tcg_const_i64((r1 << 4) | r3);
+ tcg_gen_qemu_st8(tmp2, tmp, get_mem_index(s));
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x55: /* CLIY D1(B1),I2 [SIY] */
+ tmp3 = get_address(s, 0, b2, d2); /* SIY -> this is the 1st operand */
+ tmp = tcg_temp_new_i64();
+ tmp32_1 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld8u(tmp, tmp3, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_1, tmp);
+ cmp_u32c(s, tmp32_1, (r1 << 4) | r3);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp3);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x80: /* ICMH R1,M3,D2(B2) [RSY] */
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r3);
+ potential_page_fault(s);
+ /* XXX split CC calculation out */
+ gen_helper_icmh(cc_op, tmp32_1, tmp, tmp32_2);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ default:
+ LOG_DISAS("illegal eb operation 0x%x\n", op);
+ gen_illegal_opcode(s, ilc);
+ break;
+ }
+}
+
+static void disas_ed(DisasContext *s, int op, int r1, int x2, int b2, int d2,
+ int r1b)
+{
+ TCGv_i32 tmp_r1, tmp32;
+ TCGv_i64 addr, tmp;
+ addr = get_address(s, x2, b2, d2);
+ tmp_r1 = tcg_const_i32(r1);
+ switch (op) {
+ case 0x5: /* LXDB R1,D2(X2,B2) [RXE] */
+ potential_page_fault(s);
+ gen_helper_lxdb(tmp_r1, addr);
+ break;
+ case 0x9: /* CEB R1,D2(X2,B2) [RXE] */
+ tmp = tcg_temp_new_i64();
+ tmp32 = load_freg32(r1);
+ tcg_gen_qemu_ld32u(tmp, addr, get_mem_index(s));
+ set_cc_cmp_f32_i64(s, tmp32, tmp);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32);
+ break;
+ case 0xa: /* AEB R1,D2(X2,B2) [RXE] */
+ tmp = tcg_temp_new_i64();
+ tmp32 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32u(tmp, addr, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32, tmp);
+ gen_helper_aeb(tmp_r1, tmp32);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32);
+
+ tmp32 = load_freg32(r1);
+ set_cc_nz_f32(s, tmp32);
+ tcg_temp_free_i32(tmp32);
+ break;
+ case 0xb: /* SEB R1,D2(X2,B2) [RXE] */
+ tmp = tcg_temp_new_i64();
+ tmp32 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32u(tmp, addr, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32, tmp);
+ gen_helper_seb(tmp_r1, tmp32);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32);
+
+ tmp32 = load_freg32(r1);
+ set_cc_nz_f32(s, tmp32);
+ tcg_temp_free_i32(tmp32);
+ break;
+ case 0xd: /* DEB R1,D2(X2,B2) [RXE] */
+ tmp = tcg_temp_new_i64();
+ tmp32 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32u(tmp, addr, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32, tmp);
+ gen_helper_deb(tmp_r1, tmp32);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32);
+ break;
+ case 0x10: /* TCEB R1,D2(X2,B2) [RXE] */
+ potential_page_fault(s);
+ gen_helper_tceb(cc_op, tmp_r1, addr);
+ set_cc_static(s);
+ break;
+ case 0x11: /* TCDB R1,D2(X2,B2) [RXE] */
+ potential_page_fault(s);
+ gen_helper_tcdb(cc_op, tmp_r1, addr);
+ set_cc_static(s);
+ break;
+ case 0x12: /* TCXB R1,D2(X2,B2) [RXE] */
+ potential_page_fault(s);
+ gen_helper_tcxb(cc_op, tmp_r1, addr);
+ set_cc_static(s);
+ break;
+ case 0x17: /* MEEB R1,D2(X2,B2) [RXE] */
+ tmp = tcg_temp_new_i64();
+ tmp32 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32u(tmp, addr, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32, tmp);
+ gen_helper_meeb(tmp_r1, tmp32);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32);
+ break;
+ case 0x19: /* CDB R1,D2(X2,B2) [RXE] */
+ potential_page_fault(s);
+ gen_helper_cdb(cc_op, tmp_r1, addr);
+ set_cc_static(s);
+ break;
+ case 0x1a: /* ADB R1,D2(X2,B2) [RXE] */
+ potential_page_fault(s);
+ gen_helper_adb(cc_op, tmp_r1, addr);
+ set_cc_static(s);
+ break;
+ case 0x1b: /* SDB R1,D2(X2,B2) [RXE] */
+ potential_page_fault(s);
+ gen_helper_sdb(cc_op, tmp_r1, addr);
+ set_cc_static(s);
+ break;
+ case 0x1c: /* MDB R1,D2(X2,B2) [RXE] */
+ potential_page_fault(s);
+ gen_helper_mdb(tmp_r1, addr);
+ break;
+ case 0x1d: /* DDB R1,D2(X2,B2) [RXE] */
+ potential_page_fault(s);
+ gen_helper_ddb(tmp_r1, addr);
+ break;
+ case 0x1e: /* MADB R1,R3,D2(X2,B2) [RXF] */
+ /* for RXF insns, r1 is R3 and r1b is R1 */
+ tmp32 = tcg_const_i32(r1b);
+ potential_page_fault(s);
+ gen_helper_madb(tmp32, addr, tmp_r1);
+ tcg_temp_free_i32(tmp32);
+ break;
+ default:
+ LOG_DISAS("illegal ed operation 0x%x\n", op);
+ gen_illegal_opcode(s, 3);
+ return;
+ }
+ tcg_temp_free_i32(tmp_r1);
+ tcg_temp_free_i64(addr);
+}
+
+static void disas_a5(DisasContext *s, int op, int r1, int i2)
+{
+ TCGv_i64 tmp, tmp2;
+ TCGv_i32 tmp32;
+ LOG_DISAS("disas_a5: op 0x%x r1 %d i2 0x%x\n", op, r1, i2);
+ switch (op) {
+ case 0x0: /* IIHH R1,I2 [RI] */
+ tmp = tcg_const_i64(i2);
+ tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 48, 16);
+ break;
+ case 0x1: /* IIHL R1,I2 [RI] */
+ tmp = tcg_const_i64(i2);
+ tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 32, 16);
+ break;
+ case 0x2: /* IILH R1,I2 [RI] */
+ tmp = tcg_const_i64(i2);
+ tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 16, 16);
+ break;
+ case 0x3: /* IILL R1,I2 [RI] */
+ tmp = tcg_const_i64(i2);
+ tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 0, 16);
+ break;
+ case 0x4: /* NIHH R1,I2 [RI] */
+ case 0x8: /* OIHH R1,I2 [RI] */
+ tmp = load_reg(r1);
+ tmp32 = tcg_temp_new_i32();
+ switch (op) {
+ case 0x4:
+ tmp2 = tcg_const_i64((((uint64_t)i2) << 48)
+ | 0x0000ffffffffffffULL);
+ tcg_gen_and_i64(tmp, tmp, tmp2);
+ break;
+ case 0x8:
+ tmp2 = tcg_const_i64(((uint64_t)i2) << 48);
+ tcg_gen_or_i64(tmp, tmp, tmp2);
+ break;
+ default:
+ tcg_abort();
+ }
+ store_reg(r1, tmp);
+ tcg_gen_shri_i64(tmp2, tmp, 48);
+ tcg_gen_trunc_i64_i32(tmp32, tmp2);
+ set_cc_nz_u32(s, tmp32);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32);
+ break;
+ case 0x5: /* NIHL R1,I2 [RI] */
+ case 0x9: /* OIHL R1,I2 [RI] */
+ tmp = load_reg(r1);
+ tmp32 = tcg_temp_new_i32();
+ switch (op) {
+ case 0x5:
+ tmp2 = tcg_const_i64((((uint64_t)i2) << 32)
+ | 0xffff0000ffffffffULL);
+ tcg_gen_and_i64(tmp, tmp, tmp2);
+ break;
+ case 0x9:
+ tmp2 = tcg_const_i64(((uint64_t)i2) << 32);
+ tcg_gen_or_i64(tmp, tmp, tmp2);
+ break;
+ default:
+ tcg_abort();
+ }
+ store_reg(r1, tmp);
+ tcg_gen_shri_i64(tmp2, tmp, 32);
+ tcg_gen_trunc_i64_i32(tmp32, tmp2);
+ tcg_gen_andi_i32(tmp32, tmp32, 0xffff);
+ set_cc_nz_u32(s, tmp32);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32);
+ break;
+ case 0x6: /* NILH R1,I2 [RI] */
+ case 0xa: /* OILH R1,I2 [RI] */
+ tmp = load_reg(r1);
+ tmp32 = tcg_temp_new_i32();
+ switch (op) {
+ case 0x6:
+ tmp2 = tcg_const_i64((((uint64_t)i2) << 16)
+ | 0xffffffff0000ffffULL);
+ tcg_gen_and_i64(tmp, tmp, tmp2);
+ break;
+ case 0xa:
+ tmp2 = tcg_const_i64(((uint64_t)i2) << 16);
+ tcg_gen_or_i64(tmp, tmp, tmp2);
+ break;
+ default:
+ tcg_abort();
+ }
+ store_reg(r1, tmp);
+ tcg_gen_shri_i64(tmp, tmp, 16);
+ tcg_gen_trunc_i64_i32(tmp32, tmp);
+ tcg_gen_andi_i32(tmp32, tmp32, 0xffff);
+ set_cc_nz_u32(s, tmp32);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32);
+ break;
+ case 0x7: /* NILL R1,I2 [RI] */
+ case 0xb: /* OILL R1,I2 [RI] */
+ tmp = load_reg(r1);
+ tmp32 = tcg_temp_new_i32();
+ switch (op) {
+ case 0x7:
+ tmp2 = tcg_const_i64(i2 | 0xffffffffffff0000ULL);
+ tcg_gen_and_i64(tmp, tmp, tmp2);
+ break;
+ case 0xb:
+ tmp2 = tcg_const_i64(i2);
+ tcg_gen_or_i64(tmp, tmp, tmp2);
+ break;
+ default:
+ tcg_abort();
+ }
+ store_reg(r1, tmp);
+ tcg_gen_trunc_i64_i32(tmp32, tmp);
+ tcg_gen_andi_i32(tmp32, tmp32, 0xffff);
+ set_cc_nz_u32(s, tmp32); /* signedness should not matter here */
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32);
+ break;
+ case 0xc: /* LLIHH R1,I2 [RI] */
+ tmp = tcg_const_i64( ((uint64_t)i2) << 48 );
+ store_reg(r1, tmp);
+ break;
+ case 0xd: /* LLIHL R1,I2 [RI] */
+ tmp = tcg_const_i64( ((uint64_t)i2) << 32 );
+ store_reg(r1, tmp);
+ break;
+ case 0xe: /* LLILH R1,I2 [RI] */
+ tmp = tcg_const_i64( ((uint64_t)i2) << 16 );
+ store_reg(r1, tmp);
+ break;
+ case 0xf: /* LLILL R1,I2 [RI] */
+ tmp = tcg_const_i64(i2);
+ store_reg(r1, tmp);
+ break;
+ default:
+ LOG_DISAS("illegal a5 operation 0x%x\n", op);
+ gen_illegal_opcode(s, 2);
+ return;
+ }
+ tcg_temp_free_i64(tmp);
+}
+
+static void disas_a7(DisasContext *s, int op, int r1, int i2)
+{
+ TCGv_i64 tmp, tmp2;
+ TCGv_i32 tmp32_1, tmp32_2, tmp32_3;
+ int l1;
+
+ LOG_DISAS("disas_a7: op 0x%x r1 %d i2 0x%x\n", op, r1, i2);
+ switch (op) {
+ case 0x0: /* TMLH or TMH R1,I2 [RI] */
+ case 0x1: /* TMLL or TML R1,I2 [RI] */
+ case 0x2: /* TMHH R1,I2 [RI] */
+ case 0x3: /* TMHL R1,I2 [RI] */
+ tmp = load_reg(r1);
+ tmp2 = tcg_const_i64((uint16_t)i2);
+ switch (op) {
+ case 0x0:
+ tcg_gen_shri_i64(tmp, tmp, 16);
+ break;
+ case 0x1:
+ break;
+ case 0x2:
+ tcg_gen_shri_i64(tmp, tmp, 48);
+ break;
+ case 0x3:
+ tcg_gen_shri_i64(tmp, tmp, 32);
+ break;
+ }
+ tcg_gen_andi_i64(tmp, tmp, 0xffff);
+ cmp_64(s, tmp, tmp2, CC_OP_TM_64);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x4: /* brc m1, i2 */
+ gen_brc(r1, s, i2 * 2LL);
+ return;
+ case 0x5: /* BRAS R1,I2 [RI] */
+ tmp = tcg_const_i64(pc_to_link_info(s, s->pc + 4));
+ store_reg(r1, tmp);
+ tcg_temp_free_i64(tmp);
+ gen_goto_tb(s, 0, s->pc + i2 * 2LL);
+ s->is_jmp = DISAS_TB_JUMP;
+ break;
+ case 0x6: /* BRCT R1,I2 [RI] */
+ tmp32_1 = load_reg32(r1);
+ tcg_gen_subi_i32(tmp32_1, tmp32_1, 1);
+ store_reg32(r1, tmp32_1);
+ gen_update_cc_op(s);
+ l1 = gen_new_label();
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp32_1, 0, l1);
+ gen_goto_tb(s, 0, s->pc + (i2 * 2LL));
+ gen_set_label(l1);
+ gen_goto_tb(s, 1, s->pc + 4);
+ s->is_jmp = DISAS_TB_JUMP;
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x7: /* BRCTG R1,I2 [RI] */
+ tmp = load_reg(r1);
+ tcg_gen_subi_i64(tmp, tmp, 1);
+ store_reg(r1, tmp);
+ gen_update_cc_op(s);
+ l1 = gen_new_label();
+ tcg_gen_brcondi_i64(TCG_COND_EQ, tmp, 0, l1);
+ gen_goto_tb(s, 0, s->pc + (i2 * 2LL));
+ gen_set_label(l1);
+ gen_goto_tb(s, 1, s->pc + 4);
+ s->is_jmp = DISAS_TB_JUMP;
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x8: /* lhi r1, i2 */
+ tmp32_1 = tcg_const_i32(i2);
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x9: /* lghi r1, i2 */
+ tmp = tcg_const_i64(i2);
+ store_reg(r1, tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0xa: /* AHI R1,I2 [RI] */
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = tcg_temp_new_i32();
+ tmp32_3 = tcg_const_i32(i2);
+
+ if (i2 < 0) {
+ tcg_gen_subi_i32(tmp32_2, tmp32_1, -i2);
+ } else {
+ tcg_gen_add_i32(tmp32_2, tmp32_1, tmp32_3);
+ }
+
+ store_reg32(r1, tmp32_2);
+ set_cc_add32(s, tmp32_1, tmp32_3, tmp32_2);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+ case 0xb: /* aghi r1, i2 */
+ tmp = load_reg(r1);
+ tmp2 = tcg_const_i64(i2);
+
+ if (i2 < 0) {
+ tcg_gen_subi_i64(regs[r1], tmp, -i2);
+ } else {
+ tcg_gen_add_i64(regs[r1], tmp, tmp2);
+ }
+ set_cc_add64(s, tmp, tmp2, regs[r1]);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0xc: /* MHI R1,I2 [RI] */
+ tmp32_1 = load_reg32(r1);
+ tcg_gen_muli_i32(tmp32_1, tmp32_1, i2);
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0xd: /* MGHI R1,I2 [RI] */
+ tmp = load_reg(r1);
+ tcg_gen_muli_i64(tmp, tmp, i2);
+ store_reg(r1, tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0xe: /* CHI R1,I2 [RI] */
+ tmp32_1 = load_reg32(r1);
+ cmp_s32c(s, tmp32_1, i2);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0xf: /* CGHI R1,I2 [RI] */
+ tmp = load_reg(r1);
+ cmp_s64c(s, tmp, i2);
+ tcg_temp_free_i64(tmp);
+ break;
+ default:
+ LOG_DISAS("illegal a7 operation 0x%x\n", op);
+ gen_illegal_opcode(s, 2);
+ return;
+ }
+}
+
+static void disas_b2(DisasContext *s, int op, uint32_t insn)
+{
+ TCGv_i64 tmp, tmp2, tmp3;
+ TCGv_i32 tmp32_1, tmp32_2, tmp32_3;
+ int r1, r2;
+ int ilc = 2;
+#ifndef CONFIG_USER_ONLY
+ int r3, d2, b2;
+#endif
+
+ r1 = (insn >> 4) & 0xf;
+ r2 = insn & 0xf;
+
+ LOG_DISAS("disas_b2: op 0x%x r1 %d r2 %d\n", op, r1, r2);
+
+ switch (op) {
+ case 0x22: /* IPM R1 [RRE] */
+ tmp32_1 = tcg_const_i32(r1);
+ gen_op_calc_cc(s);
+ gen_helper_ipm(cc_op, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x41: /* CKSM R1,R2 [RRE] */
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r2);
+ potential_page_fault(s);
+ gen_helper_cksm(tmp32_1, tmp32_2);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ gen_op_movi_cc(s, 0);
+ break;
+ case 0x4e: /* SAR R1,R2 [RRE] */
+ tmp32_1 = load_reg32(r2);
+ tcg_gen_st_i32(tmp32_1, cpu_env, offsetof(CPUState, aregs[r1]));
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x4f: /* EAR R1,R2 [RRE] */
+ tmp32_1 = tcg_temp_new_i32();
+ tcg_gen_ld_i32(tmp32_1, cpu_env, offsetof(CPUState, aregs[r2]));
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x52: /* MSR R1,R2 [RRE] */
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = load_reg32(r2);
+ tcg_gen_mul_i32(tmp32_1, tmp32_1, tmp32_2);
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x54: /* MVPG R1,R2 [RRE] */
+ tmp = load_reg(0);
+ tmp2 = load_reg(r1);
+ tmp3 = load_reg(r2);
+ potential_page_fault(s);
+ gen_helper_mvpg(tmp, tmp2, tmp3);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ /* XXX check CCO bit and set CC accordingly */
+ gen_op_movi_cc(s, 0);
+ break;
+ case 0x55: /* MVST R1,R2 [RRE] */
+ tmp32_1 = load_reg32(0);
+ tmp32_2 = tcg_const_i32(r1);
+ tmp32_3 = tcg_const_i32(r2);
+ potential_page_fault(s);
+ gen_helper_mvst(tmp32_1, tmp32_2, tmp32_3);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ gen_op_movi_cc(s, 1);
+ break;
+ case 0x5d: /* CLST R1,R2 [RRE] */
+ tmp32_1 = load_reg32(0);
+ tmp32_2 = tcg_const_i32(r1);
+ tmp32_3 = tcg_const_i32(r2);
+ potential_page_fault(s);
+ gen_helper_clst(cc_op, tmp32_1, tmp32_2, tmp32_3);
+ set_cc_static(s);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+ case 0x5e: /* SRST R1,R2 [RRE] */
+ tmp32_1 = load_reg32(0);
+ tmp32_2 = tcg_const_i32(r1);
+ tmp32_3 = tcg_const_i32(r2);
+ potential_page_fault(s);
+ gen_helper_srst(cc_op, tmp32_1, tmp32_2, tmp32_3);
+ set_cc_static(s);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+
+#ifndef CONFIG_USER_ONLY
+ case 0x02: /* STIDP D2(B2) [S] */
+ /* Store CPU ID */
+ check_privileged(s, ilc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ potential_page_fault(s);
+ gen_helper_stidp(tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x04: /* SCK D2(B2) [S] */
+ /* Set Clock */
+ check_privileged(s, ilc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ potential_page_fault(s);
+ gen_helper_sck(cc_op, tmp);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x05: /* STCK D2(B2) [S] */
+ /* Store Clock */
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ potential_page_fault(s);
+ gen_helper_stck(cc_op, tmp);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x06: /* SCKC D2(B2) [S] */
+ /* Set Clock Comparator */
+ check_privileged(s, ilc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ potential_page_fault(s);
+ gen_helper_sckc(tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x07: /* STCKC D2(B2) [S] */
+ /* Store Clock Comparator */
+ check_privileged(s, ilc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ potential_page_fault(s);
+ gen_helper_stckc(tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x08: /* SPT D2(B2) [S] */
+ /* Set CPU Timer */
+ check_privileged(s, ilc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ potential_page_fault(s);
+ gen_helper_spt(tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x09: /* STPT D2(B2) [S] */
+ /* Store CPU Timer */
+ check_privileged(s, ilc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ potential_page_fault(s);
+ gen_helper_stpt(tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x0a: /* SPKA D2(B2) [S] */
+ /* Set PSW Key from Address */
+ check_privileged(s, ilc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_andi_i64(tmp2, psw_mask, ~PSW_MASK_KEY);
+ tcg_gen_shli_i64(tmp, tmp, PSW_SHIFT_KEY - 4);
+ tcg_gen_or_i64(psw_mask, tmp2, tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x0d: /* PTLB [S] */
+ /* Purge TLB */
+ check_privileged(s, ilc);
+ gen_helper_ptlb();
+ break;
+ case 0x10: /* SPX D2(B2) [S] */
+ /* Set Prefix Register */
+ check_privileged(s, ilc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ potential_page_fault(s);
+ gen_helper_spx(tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x11: /* STPX D2(B2) [S] */
+ /* Store Prefix */
+ check_privileged(s, ilc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_ld_i64(tmp2, cpu_env, offsetof(CPUState, psa));
+ tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s));
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x12: /* STAP D2(B2) [S] */
+ /* Store CPU Address */
+ check_privileged(s, ilc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = tcg_temp_new_i32();
+ tcg_gen_ld_i32(tmp32_1, cpu_env, offsetof(CPUState, cpu_num));
+ tcg_gen_extu_i32_i64(tmp2, tmp32_1);
+ tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s));
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x21: /* IPTE R1,R2 [RRE] */
+ /* Invalidate PTE */
+ check_privileged(s, ilc);
+ r1 = (insn >> 4) & 0xf;
+ r2 = insn & 0xf;
+ tmp = load_reg(r1);
+ tmp2 = load_reg(r2);
+ gen_helper_ipte(tmp, tmp2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x29: /* ISKE R1,R2 [RRE] */
+ /* Insert Storage Key Extended */
+ check_privileged(s, ilc);
+ r1 = (insn >> 4) & 0xf;
+ r2 = insn & 0xf;
+ tmp = load_reg(r2);
+ tmp2 = tcg_temp_new_i64();
+ gen_helper_iske(tmp2, tmp);
+ store_reg(r1, tmp2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x2a: /* RRBE R1,R2 [RRE] */
+ /* Set Storage Key Extended */
+ check_privileged(s, ilc);
+ r1 = (insn >> 4) & 0xf;
+ r2 = insn & 0xf;
+ tmp32_1 = load_reg32(r1);
+ tmp = load_reg(r2);
+ gen_helper_rrbe(cc_op, tmp32_1, tmp);
+ set_cc_static(s);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x2b: /* SSKE R1,R2 [RRE] */
+ /* Set Storage Key Extended */
+ check_privileged(s, ilc);
+ r1 = (insn >> 4) & 0xf;
+ r2 = insn & 0xf;
+ tmp32_1 = load_reg32(r1);
+ tmp = load_reg(r2);
+ gen_helper_sske(tmp32_1, tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x34: /* STCH ? */
+ /* Store Subchannel */
+ check_privileged(s, ilc);
+ gen_op_movi_cc(s, 3);
+ break;
+ case 0x46: /* STURA R1,R2 [RRE] */
+ /* Store Using Real Address */
+ check_privileged(s, ilc);
+ r1 = (insn >> 4) & 0xf;
+ r2 = insn & 0xf;
+ tmp32_1 = load_reg32(r1);
+ tmp = load_reg(r2);
+ potential_page_fault(s);
+ gen_helper_stura(tmp, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x50: /* CSP R1,R2 [RRE] */
+ /* Compare And Swap And Purge */
+ check_privileged(s, ilc);
+ r1 = (insn >> 4) & 0xf;
+ r2 = insn & 0xf;
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r2);
+ gen_helper_csp(cc_op, tmp32_1, tmp32_2);
+ set_cc_static(s);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x5f: /* CHSC ? */
+ /* Channel Subsystem Call */
+ check_privileged(s, ilc);
+ gen_op_movi_cc(s, 3);
+ break;
+ case 0x78: /* STCKE D2(B2) [S] */
+ /* Store Clock Extended */
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ potential_page_fault(s);
+ gen_helper_stcke(cc_op, tmp);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x79: /* SACF D2(B2) [S] */
+ /* Store Clock Extended */
+ check_privileged(s, ilc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ potential_page_fault(s);
+ gen_helper_sacf(tmp);
+ tcg_temp_free_i64(tmp);
+ /* addressing mode has changed, so end the block */
+ s->pc += ilc * 2;
+ update_psw_addr(s);
+ s->is_jmp = DISAS_EXCP;
+ break;
+ case 0x7d: /* STSI D2,(B2) [S] */
+ check_privileged(s, ilc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = load_reg32(0);
+ tmp32_2 = load_reg32(1);
+ potential_page_fault(s);
+ gen_helper_stsi(cc_op, tmp, tmp32_1, tmp32_2);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x9d: /* LFPC D2(B2) [S] */
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_1, tmp2);
+ tcg_gen_st_i32(tmp32_1, cpu_env, offsetof(CPUState, fpc));
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0xb1: /* STFL D2(B2) [S] */
+ /* Store Facility List (CPU features) at 200 */
+ check_privileged(s, ilc);
+ tmp2 = tcg_const_i64(0xc0000000);
+ tmp = tcg_const_i64(200);
+ tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s));
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0xb2: /* LPSWE D2(B2) [S] */
+ /* Load PSW Extended */
+ check_privileged(s, ilc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp3 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld64(tmp2, tmp, get_mem_index(s));
+ tcg_gen_addi_i64(tmp, tmp, 8);
+ tcg_gen_qemu_ld64(tmp3, tmp, get_mem_index(s));
+ gen_helper_load_psw(tmp2, tmp3);
+ /* we need to keep cc_op intact */
+ s->is_jmp = DISAS_JUMP;
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x20: /* SERVC R1,R2 [RRE] */
+ /* SCLP Service call (PV hypercall) */
+ check_privileged(s, ilc);
+ potential_page_fault(s);
+ tmp32_1 = load_reg32(r2);
+ tmp = load_reg(r1);
+ gen_helper_servc(cc_op, tmp32_1, tmp);
+ set_cc_static(s);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i64(tmp);
+ break;
+#endif
+ default:
+ LOG_DISAS("illegal b2 operation 0x%x\n", op);
+ gen_illegal_opcode(s, ilc);
+ break;
+ }
+}
+
+static void disas_b3(DisasContext *s, int op, int m3, int r1, int r2)
+{
+ TCGv_i64 tmp;
+ TCGv_i32 tmp32_1, tmp32_2, tmp32_3;
+ LOG_DISAS("disas_b3: op 0x%x m3 0x%x r1 %d r2 %d\n", op, m3, r1, r2);
+#define FP_HELPER(i) \
+ tmp32_1 = tcg_const_i32(r1); \
+ tmp32_2 = tcg_const_i32(r2); \
+ gen_helper_ ## i (tmp32_1, tmp32_2); \
+ tcg_temp_free_i32(tmp32_1); \
+ tcg_temp_free_i32(tmp32_2);
+
+#define FP_HELPER_CC(i) \
+ tmp32_1 = tcg_const_i32(r1); \
+ tmp32_2 = tcg_const_i32(r2); \
+ gen_helper_ ## i (cc_op, tmp32_1, tmp32_2); \
+ set_cc_static(s); \
+ tcg_temp_free_i32(tmp32_1); \
+ tcg_temp_free_i32(tmp32_2);
+
+ switch (op) {
+ case 0x0: /* LPEBR R1,R2 [RRE] */
+ FP_HELPER_CC(lpebr);
+ break;
+ case 0x2: /* LTEBR R1,R2 [RRE] */
+ FP_HELPER_CC(ltebr);
+ break;
+ case 0x3: /* LCEBR R1,R2 [RRE] */
+ FP_HELPER_CC(lcebr);
+ break;
+ case 0x4: /* LDEBR R1,R2 [RRE] */
+ FP_HELPER(ldebr);
+ break;
+ case 0x5: /* LXDBR R1,R2 [RRE] */
+ FP_HELPER(lxdbr);
+ break;
+ case 0x9: /* CEBR R1,R2 [RRE] */
+ FP_HELPER_CC(cebr);
+ break;
+ case 0xa: /* AEBR R1,R2 [RRE] */
+ FP_HELPER_CC(aebr);
+ break;
+ case 0xb: /* SEBR R1,R2 [RRE] */
+ FP_HELPER_CC(sebr);
+ break;
+ case 0xd: /* DEBR R1,R2 [RRE] */
+ FP_HELPER(debr);
+ break;
+ case 0x10: /* LPDBR R1,R2 [RRE] */
+ FP_HELPER_CC(lpdbr);
+ break;
+ case 0x12: /* LTDBR R1,R2 [RRE] */
+ FP_HELPER_CC(ltdbr);
+ break;
+ case 0x13: /* LCDBR R1,R2 [RRE] */
+ FP_HELPER_CC(lcdbr);
+ break;
+ case 0x15: /* SQBDR R1,R2 [RRE] */
+ FP_HELPER(sqdbr);
+ break;
+ case 0x17: /* MEEBR R1,R2 [RRE] */
+ FP_HELPER(meebr);
+ break;
+ case 0x19: /* CDBR R1,R2 [RRE] */
+ FP_HELPER_CC(cdbr);
+ break;
+ case 0x1a: /* ADBR R1,R2 [RRE] */
+ FP_HELPER_CC(adbr);
+ break;
+ case 0x1b: /* SDBR R1,R2 [RRE] */
+ FP_HELPER_CC(sdbr);
+ break;
+ case 0x1c: /* MDBR R1,R2 [RRE] */
+ FP_HELPER(mdbr);
+ break;
+ case 0x1d: /* DDBR R1,R2 [RRE] */
+ FP_HELPER(ddbr);
+ break;
+ case 0xe: /* MAEBR R1,R3,R2 [RRF] */
+ case 0x1e: /* MADBR R1,R3,R2 [RRF] */
+ case 0x1f: /* MSDBR R1,R3,R2 [RRF] */
+ /* for RRF insns, m3 is R1, r1 is R3, and r2 is R2 */
+ tmp32_1 = tcg_const_i32(m3);
+ tmp32_2 = tcg_const_i32(r2);
+ tmp32_3 = tcg_const_i32(r1);
+ switch (op) {
+ case 0xe:
+ gen_helper_maebr(tmp32_1, tmp32_3, tmp32_2);
+ break;
+ case 0x1e:
+ gen_helper_madbr(tmp32_1, tmp32_3, tmp32_2);
+ break;
+ case 0x1f:
+ gen_helper_msdbr(tmp32_1, tmp32_3, tmp32_2);
+ break;
+ default:
+ tcg_abort();
+ }
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+ case 0x40: /* LPXBR R1,R2 [RRE] */
+ FP_HELPER_CC(lpxbr);
+ break;
+ case 0x42: /* LTXBR R1,R2 [RRE] */
+ FP_HELPER_CC(ltxbr);
+ break;
+ case 0x43: /* LCXBR R1,R2 [RRE] */
+ FP_HELPER_CC(lcxbr);
+ break;
+ case 0x44: /* LEDBR R1,R2 [RRE] */
+ FP_HELPER(ledbr);
+ break;
+ case 0x45: /* LDXBR R1,R2 [RRE] */
+ FP_HELPER(ldxbr);
+ break;
+ case 0x46: /* LEXBR R1,R2 [RRE] */
+ FP_HELPER(lexbr);
+ break;
+ case 0x49: /* CXBR R1,R2 [RRE] */
+ FP_HELPER_CC(cxbr);
+ break;
+ case 0x4a: /* AXBR R1,R2 [RRE] */
+ FP_HELPER_CC(axbr);
+ break;
+ case 0x4b: /* SXBR R1,R2 [RRE] */
+ FP_HELPER_CC(sxbr);
+ break;
+ case 0x4c: /* MXBR R1,R2 [RRE] */
+ FP_HELPER(mxbr);
+ break;
+ case 0x4d: /* DXBR R1,R2 [RRE] */
+ FP_HELPER(dxbr);
+ break;
+ case 0x65: /* LXR R1,R2 [RRE] */
+ tmp = load_freg(r2);
+ store_freg(r1, tmp);
+ tcg_temp_free_i64(tmp);
+ tmp = load_freg(r2 + 2);
+ store_freg(r1 + 2, tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x74: /* LZER R1 [RRE] */
+ tmp32_1 = tcg_const_i32(r1);
+ gen_helper_lzer(tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x75: /* LZDR R1 [RRE] */
+ tmp32_1 = tcg_const_i32(r1);
+ gen_helper_lzdr(tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x76: /* LZXR R1 [RRE] */
+ tmp32_1 = tcg_const_i32(r1);
+ gen_helper_lzxr(tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x84: /* SFPC R1 [RRE] */
+ tmp32_1 = load_reg32(r1);
+ tcg_gen_st_i32(tmp32_1, cpu_env, offsetof(CPUState, fpc));
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x8c: /* EFPC R1 [RRE] */
+ tmp32_1 = tcg_temp_new_i32();
+ tcg_gen_ld_i32(tmp32_1, cpu_env, offsetof(CPUState, fpc));
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x94: /* CEFBR R1,R2 [RRE] */
+ case 0x95: /* CDFBR R1,R2 [RRE] */
+ case 0x96: /* CXFBR R1,R2 [RRE] */
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = load_reg32(r2);
+ switch (op) {
+ case 0x94:
+ gen_helper_cefbr(tmp32_1, tmp32_2);
+ break;
+ case 0x95:
+ gen_helper_cdfbr(tmp32_1, tmp32_2);
+ break;
+ case 0x96:
+ gen_helper_cxfbr(tmp32_1, tmp32_2);
+ break;
+ default:
+ tcg_abort();
+ }
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x98: /* CFEBR R1,R2 [RRE] */
+ case 0x99: /* CFDBR R1,R2 [RRE] */
+ case 0x9a: /* CFXBR R1,R2 [RRE] */
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r2);
+ tmp32_3 = tcg_const_i32(m3);
+ switch (op) {
+ case 0x98:
+ gen_helper_cfebr(cc_op, tmp32_1, tmp32_2, tmp32_3);
+ break;
+ case 0x99:
+ gen_helper_cfdbr(cc_op, tmp32_1, tmp32_2, tmp32_3);
+ break;
+ case 0x9a:
+ gen_helper_cfxbr(cc_op, tmp32_1, tmp32_2, tmp32_3);
+ break;
+ default:
+ tcg_abort();
+ }
+ set_cc_static(s);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+ case 0xa4: /* CEGBR R1,R2 [RRE] */
+ case 0xa5: /* CDGBR R1,R2 [RRE] */
+ tmp32_1 = tcg_const_i32(r1);
+ tmp = load_reg(r2);
+ switch (op) {
+ case 0xa4:
+ gen_helper_cegbr(tmp32_1, tmp);
+ break;
+ case 0xa5:
+ gen_helper_cdgbr(tmp32_1, tmp);
+ break;
+ default:
+ tcg_abort();
+ }
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0xa6: /* CXGBR R1,R2 [RRE] */
+ tmp32_1 = tcg_const_i32(r1);
+ tmp = load_reg(r2);
+ gen_helper_cxgbr(tmp32_1, tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0xa8: /* CGEBR R1,R2 [RRE] */
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r2);
+ tmp32_3 = tcg_const_i32(m3);
+ gen_helper_cgebr(cc_op, tmp32_1, tmp32_2, tmp32_3);
+ set_cc_static(s);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+ case 0xa9: /* CGDBR R1,R2 [RRE] */
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r2);
+ tmp32_3 = tcg_const_i32(m3);
+ gen_helper_cgdbr(cc_op, tmp32_1, tmp32_2, tmp32_3);
+ set_cc_static(s);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+ case 0xaa: /* CGXBR R1,R2 [RRE] */
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r2);
+ tmp32_3 = tcg_const_i32(m3);
+ gen_helper_cgxbr(cc_op, tmp32_1, tmp32_2, tmp32_3);
+ set_cc_static(s);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+ default:
+ LOG_DISAS("illegal b3 operation 0x%x\n", op);
+ gen_illegal_opcode(s, 2);
+ break;
+ }
+
+#undef FP_HELPER_CC
+#undef FP_HELPER
+}
+
+static void disas_b9(DisasContext *s, int op, int r1, int r2)
+{
+ TCGv_i64 tmp, tmp2, tmp3;
+ TCGv_i32 tmp32_1, tmp32_2, tmp32_3;
+
+ LOG_DISAS("disas_b9: op 0x%x r1 %d r2 %d\n", op, r1, r2);
+ switch (op) {
+ case 0x0: /* LPGR R1,R2 [RRE] */
+ case 0x1: /* LNGR R1,R2 [RRE] */
+ case 0x2: /* LTGR R1,R2 [RRE] */
+ case 0x3: /* LCGR R1,R2 [RRE] */
+ case 0x10: /* LPGFR R1,R2 [RRE] */
+ case 0x11: /* LNFGR R1,R2 [RRE] */
+ case 0x12: /* LTGFR R1,R2 [RRE] */
+ case 0x13: /* LCGFR R1,R2 [RRE] */
+ if (op & 0x10) {
+ tmp = load_reg32_i64(r2);
+ } else {
+ tmp = load_reg(r2);
+ }
+ switch (op & 0xf) {
+ case 0x0: /* LP?GR */
+ set_cc_abs64(s, tmp);
+ gen_helper_abs_i64(tmp, tmp);
+ store_reg(r1, tmp);
+ break;
+ case 0x1: /* LN?GR */
+ set_cc_nabs64(s, tmp);
+ gen_helper_nabs_i64(tmp, tmp);
+ store_reg(r1, tmp);
+ break;
+ case 0x2: /* LT?GR */
+ if (r1 != r2) {
+ store_reg(r1, tmp);
+ }
+ set_cc_s64(s, tmp);
+ break;
+ case 0x3: /* LC?GR */
+ tcg_gen_neg_i64(regs[r1], tmp);
+ set_cc_comp64(s, regs[r1]);
+ break;
+ }
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x4: /* LGR R1,R2 [RRE] */
+ store_reg(r1, regs[r2]);
+ break;
+ case 0x6: /* LGBR R1,R2 [RRE] */
+ tmp2 = load_reg(r2);
+ tcg_gen_ext8s_i64(tmp2, tmp2);
+ store_reg(r1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x8: /* AGR R1,R2 [RRE] */
+ case 0xa: /* ALGR R1,R2 [RRE] */
+ tmp = load_reg(r1);
+ tmp2 = load_reg(r2);
+ tmp3 = tcg_temp_new_i64();
+ tcg_gen_add_i64(tmp3, tmp, tmp2);
+ store_reg(r1, tmp3);
+ switch (op) {
+ case 0x8:
+ set_cc_add64(s, tmp, tmp2, tmp3);
+ break;
+ case 0xa:
+ set_cc_addu64(s, tmp, tmp2, tmp3);
+ break;
+ default:
+ tcg_abort();
+ }
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x9: /* SGR R1,R2 [RRE] */
+ case 0xb: /* SLGR R1,R2 [RRE] */
+ case 0x1b: /* SLGFR R1,R2 [RRE] */
+ case 0x19: /* SGFR R1,R2 [RRE] */
+ tmp = load_reg(r1);
+ switch (op) {
+ case 0x1b:
+ tmp32_1 = load_reg32(r2);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_extu_i32_i64(tmp2, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x19:
+ tmp32_1 = load_reg32(r2);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_ext_i32_i64(tmp2, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ default:
+ tmp2 = load_reg(r2);
+ break;
+ }
+ tmp3 = tcg_temp_new_i64();
+ tcg_gen_sub_i64(tmp3, tmp, tmp2);
+ store_reg(r1, tmp3);
+ switch (op) {
+ case 0x9:
+ case 0x19:
+ set_cc_sub64(s, tmp, tmp2, tmp3);
+ break;
+ case 0xb:
+ case 0x1b:
+ set_cc_subu64(s, tmp, tmp2, tmp3);
+ break;
+ default:
+ tcg_abort();
+ }
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0xc: /* MSGR R1,R2 [RRE] */
+ case 0x1c: /* MSGFR R1,R2 [RRE] */
+ tmp = load_reg(r1);
+ tmp2 = load_reg(r2);
+ if (op == 0x1c) {
+ tcg_gen_ext32s_i64(tmp2, tmp2);
+ }
+ tcg_gen_mul_i64(tmp, tmp, tmp2);
+ store_reg(r1, tmp);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0xd: /* DSGR R1,R2 [RRE] */
+ case 0x1d: /* DSGFR R1,R2 [RRE] */
+ tmp = load_reg(r1 + 1);
+ if (op == 0xd) {
+ tmp2 = load_reg(r2);
+ } else {
+ tmp32_1 = load_reg32(r2);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_ext_i32_i64(tmp2, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ }
+ tmp3 = tcg_temp_new_i64();
+ tcg_gen_div_i64(tmp3, tmp, tmp2);
+ store_reg(r1 + 1, tmp3);
+ tcg_gen_rem_i64(tmp3, tmp, tmp2);
+ store_reg(r1, tmp3);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x14: /* LGFR R1,R2 [RRE] */
+ tmp32_1 = load_reg32(r2);
+ tmp = tcg_temp_new_i64();
+ tcg_gen_ext_i32_i64(tmp, tmp32_1);
+ store_reg(r1, tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x16: /* LLGFR R1,R2 [RRE] */
+ tmp32_1 = load_reg32(r2);
+ tmp = tcg_temp_new_i64();
+ tcg_gen_extu_i32_i64(tmp, tmp32_1);
+ store_reg(r1, tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x17: /* LLGTR R1,R2 [RRE] */
+ tmp32_1 = load_reg32(r2);
+ tmp = tcg_temp_new_i64();
+ tcg_gen_andi_i32(tmp32_1, tmp32_1, 0x7fffffffUL);
+ tcg_gen_extu_i32_i64(tmp, tmp32_1);
+ store_reg(r1, tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x18: /* AGFR R1,R2 [RRE] */
+ case 0x1a: /* ALGFR R1,R2 [RRE] */
+ tmp32_1 = load_reg32(r2);
+ tmp2 = tcg_temp_new_i64();
+ if (op == 0x18) {
+ tcg_gen_ext_i32_i64(tmp2, tmp32_1);
+ } else {
+ tcg_gen_extu_i32_i64(tmp2, tmp32_1);
+ }
+ tcg_temp_free_i32(tmp32_1);
+ tmp = load_reg(r1);
+ tmp3 = tcg_temp_new_i64();
+ tcg_gen_add_i64(tmp3, tmp, tmp2);
+ store_reg(r1, tmp3);
+ if (op == 0x18) {
+ set_cc_add64(s, tmp, tmp2, tmp3);
+ } else {
+ set_cc_addu64(s, tmp, tmp2, tmp3);
+ }
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x1f: /* LRVR R1,R2 [RRE] */
+ tmp32_1 = load_reg32(r2);
+ tcg_gen_bswap32_i32(tmp32_1, tmp32_1);
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x20: /* CGR R1,R2 [RRE] */
+ case 0x30: /* CGFR R1,R2 [RRE] */
+ tmp2 = load_reg(r2);
+ if (op == 0x30) {
+ tcg_gen_ext32s_i64(tmp2, tmp2);
+ }
+ tmp = load_reg(r1);
+ cmp_s64(s, tmp, tmp2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x21: /* CLGR R1,R2 [RRE] */
+ case 0x31: /* CLGFR R1,R2 [RRE] */
+ tmp2 = load_reg(r2);
+ if (op == 0x31) {
+ tcg_gen_ext32u_i64(tmp2, tmp2);
+ }
+ tmp = load_reg(r1);
+ cmp_u64(s, tmp, tmp2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x26: /* LBR R1,R2 [RRE] */
+ tmp32_1 = load_reg32(r2);
+ tcg_gen_ext8s_i32(tmp32_1, tmp32_1);
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x27: /* LHR R1,R2 [RRE] */
+ tmp32_1 = load_reg32(r2);
+ tcg_gen_ext16s_i32(tmp32_1, tmp32_1);
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x80: /* NGR R1,R2 [RRE] */
+ case 0x81: /* OGR R1,R2 [RRE] */
+ case 0x82: /* XGR R1,R2 [RRE] */
+ tmp = load_reg(r1);
+ tmp2 = load_reg(r2);
+ switch (op) {
+ case 0x80:
+ tcg_gen_and_i64(tmp, tmp, tmp2);
+ break;
+ case 0x81:
+ tcg_gen_or_i64(tmp, tmp, tmp2);
+ break;
+ case 0x82:
+ tcg_gen_xor_i64(tmp, tmp, tmp2);
+ break;
+ default:
+ tcg_abort();
+ }
+ store_reg(r1, tmp);
+ set_cc_nz_u64(s, tmp);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x83: /* FLOGR R1,R2 [RRE] */
+ tmp = load_reg(r2);
+ tmp32_1 = tcg_const_i32(r1);
+ gen_helper_flogr(cc_op, tmp32_1, tmp);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x84: /* LLGCR R1,R2 [RRE] */
+ tmp = load_reg(r2);
+ tcg_gen_andi_i64(tmp, tmp, 0xff);
+ store_reg(r1, tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x85: /* LLGHR R1,R2 [RRE] */
+ tmp = load_reg(r2);
+ tcg_gen_andi_i64(tmp, tmp, 0xffff);
+ store_reg(r1, tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x87: /* DLGR R1,R2 [RRE] */
+ tmp32_1 = tcg_const_i32(r1);
+ tmp = load_reg(r2);
+ gen_helper_dlg(tmp32_1, tmp);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x88: /* ALCGR R1,R2 [RRE] */
+ tmp = load_reg(r1);
+ tmp2 = load_reg(r2);
+ tmp3 = tcg_temp_new_i64();
+ gen_op_calc_cc(s);
+ tcg_gen_extu_i32_i64(tmp3, cc_op);
+ tcg_gen_shri_i64(tmp3, tmp3, 1);
+ tcg_gen_andi_i64(tmp3, tmp3, 1);
+ tcg_gen_add_i64(tmp3, tmp2, tmp3);
+ tcg_gen_add_i64(tmp3, tmp, tmp3);
+ store_reg(r1, tmp3);
+ set_cc_addu64(s, tmp, tmp2, tmp3);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x89: /* SLBGR R1,R2 [RRE] */
+ tmp = load_reg(r1);
+ tmp2 = load_reg(r2);
+ tmp32_1 = tcg_const_i32(r1);
+ gen_op_calc_cc(s);
+ gen_helper_slbg(cc_op, cc_op, tmp32_1, tmp, tmp2);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x94: /* LLCR R1,R2 [RRE] */
+ tmp32_1 = load_reg32(r2);
+ tcg_gen_andi_i32(tmp32_1, tmp32_1, 0xff);
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x95: /* LLHR R1,R2 [RRE] */
+ tmp32_1 = load_reg32(r2);
+ tcg_gen_andi_i32(tmp32_1, tmp32_1, 0xffff);
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x96: /* MLR R1,R2 [RRE] */
+ /* reg(r1, r1+1) = reg(r1+1) * reg(r2) */
+ tmp2 = load_reg(r2);
+ tmp3 = load_reg((r1 + 1) & 15);
+ tcg_gen_ext32u_i64(tmp2, tmp2);
+ tcg_gen_ext32u_i64(tmp3, tmp3);
+ tcg_gen_mul_i64(tmp2, tmp2, tmp3);
+ store_reg32_i64((r1 + 1) & 15, tmp2);
+ tcg_gen_shri_i64(tmp2, tmp2, 32);
+ store_reg32_i64(r1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x97: /* DLR R1,R2 [RRE] */
+ /* reg(r1) = reg(r1, r1+1) % reg(r2) */
+ /* reg(r1+1) = reg(r1, r1+1) / reg(r2) */
+ tmp = load_reg(r1);
+ tmp2 = load_reg(r2);
+ tmp3 = load_reg((r1 + 1) & 15);
+ tcg_gen_ext32u_i64(tmp2, tmp2);
+ tcg_gen_ext32u_i64(tmp3, tmp3);
+ tcg_gen_shli_i64(tmp, tmp, 32);
+ tcg_gen_or_i64(tmp, tmp, tmp3);
+
+ tcg_gen_rem_i64(tmp3, tmp, tmp2);
+ tcg_gen_div_i64(tmp, tmp, tmp2);
+ store_reg32_i64((r1 + 1) & 15, tmp);
+ store_reg32_i64(r1, tmp3);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x98: /* ALCR R1,R2 [RRE] */
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = load_reg32(r2);
+ tmp32_3 = tcg_temp_new_i32();
+ /* XXX possible optimization point */
+ gen_op_calc_cc(s);
+ gen_helper_addc_u32(tmp32_3, cc_op, tmp32_1, tmp32_2);
+ set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3);
+ store_reg32(r1, tmp32_3);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+ case 0x99: /* SLBR R1,R2 [RRE] */
+ tmp32_1 = load_reg32(r2);
+ tmp32_2 = tcg_const_i32(r1);
+ gen_op_calc_cc(s);
+ gen_helper_slb(cc_op, cc_op, tmp32_2, tmp32_1);
+ set_cc_static(s);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ default:
+ LOG_DISAS("illegal b9 operation 0x%x\n", op);
+ gen_illegal_opcode(s, 2);
+ break;
+ }
+}
+
+static void disas_c0(DisasContext *s, int op, int r1, int i2)
+{
+ TCGv_i64 tmp;
+ TCGv_i32 tmp32_1, tmp32_2;
+ uint64_t target = s->pc + i2 * 2LL;
+ int l1;
+
+ LOG_DISAS("disas_c0: op 0x%x r1 %d i2 %d\n", op, r1, i2);
+
+ switch (op) {
+ case 0: /* larl r1, i2 */
+ tmp = tcg_const_i64(target);
+ store_reg(r1, tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x1: /* LGFI R1,I2 [RIL] */
+ tmp = tcg_const_i64((int64_t)i2);
+ store_reg(r1, tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x4: /* BRCL M1,I2 [RIL] */
+ /* m1 & (1 << (3 - cc)) */
+ tmp32_1 = tcg_const_i32(3);
+ tmp32_2 = tcg_const_i32(1);
+ gen_op_calc_cc(s);
+ tcg_gen_sub_i32(tmp32_1, tmp32_1, cc_op);
+ tcg_gen_shl_i32(tmp32_2, tmp32_2, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ tmp32_1 = tcg_const_i32(r1); /* m1 == r1 */
+ tcg_gen_and_i32(tmp32_1, tmp32_1, tmp32_2);
+ l1 = gen_new_label();
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp32_1, 0, l1);
+ gen_goto_tb(s, 0, target);
+ gen_set_label(l1);
+ gen_goto_tb(s, 1, s->pc + 6);
+ s->is_jmp = DISAS_TB_JUMP;
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x5: /* brasl r1, i2 */
+ tmp = tcg_const_i64(pc_to_link_info(s, s->pc + 6));
+ store_reg(r1, tmp);
+ tcg_temp_free_i64(tmp);
+ gen_goto_tb(s, 0, target);
+ s->is_jmp = DISAS_TB_JUMP;
+ break;
+ case 0x7: /* XILF R1,I2 [RIL] */
+ case 0xb: /* NILF R1,I2 [RIL] */
+ case 0xd: /* OILF R1,I2 [RIL] */
+ tmp32_1 = load_reg32(r1);
+ switch (op) {
+ case 0x7:
+ tcg_gen_xori_i32(tmp32_1, tmp32_1, (uint32_t)i2);
+ break;
+ case 0xb:
+ tcg_gen_andi_i32(tmp32_1, tmp32_1, (uint32_t)i2);
+ break;
+ case 0xd:
+ tcg_gen_ori_i32(tmp32_1, tmp32_1, (uint32_t)i2);
+ break;
+ default:
+ tcg_abort();
+ }
+ store_reg32(r1, tmp32_1);
+ set_cc_nz_u32(s, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x9: /* IILF R1,I2 [RIL] */
+ tmp32_1 = tcg_const_i32((uint32_t)i2);
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0xa: /* NIHF R1,I2 [RIL] */
+ tmp = load_reg(r1);
+ tmp32_1 = tcg_temp_new_i32();
+ tcg_gen_andi_i64(tmp, tmp, (((uint64_t)((uint32_t)i2)) << 32)
+ | 0xffffffffULL);
+ store_reg(r1, tmp);
+ tcg_gen_shri_i64(tmp, tmp, 32);
+ tcg_gen_trunc_i64_i32(tmp32_1, tmp);
+ set_cc_nz_u32(s, tmp32_1);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0xe: /* LLIHF R1,I2 [RIL] */
+ tmp = tcg_const_i64(((uint64_t)(uint32_t)i2) << 32);
+ store_reg(r1, tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0xf: /* LLILF R1,I2 [RIL] */
+ tmp = tcg_const_i64((uint32_t)i2);
+ store_reg(r1, tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ default:
+ LOG_DISAS("illegal c0 operation 0x%x\n", op);
+ gen_illegal_opcode(s, 3);
+ break;
+ }
+}
+
+static void disas_c2(DisasContext *s, int op, int r1, int i2)
+{
+ TCGv_i64 tmp, tmp2, tmp3;
+ TCGv_i32 tmp32_1, tmp32_2, tmp32_3;
+
+ switch (op) {
+ case 0x4: /* SLGFI R1,I2 [RIL] */
+ case 0xa: /* ALGFI R1,I2 [RIL] */
+ tmp = load_reg(r1);
+ tmp2 = tcg_const_i64((uint64_t)(uint32_t)i2);
+ tmp3 = tcg_temp_new_i64();
+ switch (op) {
+ case 0x4:
+ tcg_gen_sub_i64(tmp3, tmp, tmp2);
+ set_cc_subu64(s, tmp, tmp2, tmp3);
+ break;
+ case 0xa:
+ tcg_gen_add_i64(tmp3, tmp, tmp2);
+ set_cc_addu64(s, tmp, tmp2, tmp3);
+ break;
+ default:
+ tcg_abort();
+ }
+ store_reg(r1, tmp3);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x5: /* SLFI R1,I2 [RIL] */
+ case 0xb: /* ALFI R1,I2 [RIL] */
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = tcg_const_i32(i2);
+ tmp32_3 = tcg_temp_new_i32();
+ switch (op) {
+ case 0x5:
+ tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2);
+ set_cc_subu32(s, tmp32_1, tmp32_2, tmp32_3);
+ break;
+ case 0xb:
+ tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2);
+ set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3);
+ break;
+ default:
+ tcg_abort();
+ }
+ store_reg32(r1, tmp32_3);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+ case 0xc: /* CGFI R1,I2 [RIL] */
+ tmp = load_reg(r1);
+ cmp_s64c(s, tmp, (int64_t)i2);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0xe: /* CLGFI R1,I2 [RIL] */
+ tmp = load_reg(r1);
+ cmp_u64c(s, tmp, (uint64_t)(uint32_t)i2);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0xd: /* CFI R1,I2 [RIL] */
+ tmp32_1 = load_reg32(r1);
+ cmp_s32c(s, tmp32_1, i2);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0xf: /* CLFI R1,I2 [RIL] */
+ tmp32_1 = load_reg32(r1);
+ cmp_u32c(s, tmp32_1, i2);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ default:
+ LOG_DISAS("illegal c2 operation 0x%x\n", op);
+ gen_illegal_opcode(s, 3);
+ break;
+ }
+}
+
+static void gen_and_or_xor_i32(int opc, TCGv_i32 tmp, TCGv_i32 tmp2)
+{
+ switch (opc & 0xf) {
+ case 0x4:
+ tcg_gen_and_i32(tmp, tmp, tmp2);
+ break;
+ case 0x6:
+ tcg_gen_or_i32(tmp, tmp, tmp2);
+ break;
+ case 0x7:
+ tcg_gen_xor_i32(tmp, tmp, tmp2);
+ break;
+ default:
+ tcg_abort();
+ }
+}
+
+static void disas_s390_insn(DisasContext *s)
+{
+ TCGv_i64 tmp, tmp2, tmp3, tmp4;
+ TCGv_i32 tmp32_1, tmp32_2, tmp32_3, tmp32_4;
+ unsigned char opc;
+ uint64_t insn;
+ int op, r1, r2, r3, d1, d2, x2, b1, b2, i, i2, r1b;
+ TCGv_i32 vl;
+ int ilc;
+ int l1;
+
+ opc = ldub_code(s->pc);
+ LOG_DISAS("opc 0x%x\n", opc);
+
+ ilc = get_ilc(opc);
+
+ switch (opc) {
+#ifndef CONFIG_USER_ONLY
+ case 0x01: /* SAM */
+ insn = ld_code2(s->pc);
+ /* set addressing mode, but we only do 64bit anyways */
+ break;
+#endif
+ case 0x6: /* BCTR R1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp32_1 = load_reg32(r1);
+ tcg_gen_subi_i32(tmp32_1, tmp32_1, 1);
+ store_reg32(r1, tmp32_1);
+
+ if (r2) {
+ gen_update_cc_op(s);
+ l1 = gen_new_label();
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp32_1, 0, l1);
+
+ /* not taking the branch, jump to after the instruction */
+ gen_goto_tb(s, 0, s->pc + 2);
+ gen_set_label(l1);
+
+ /* take the branch, move R2 into psw.addr */
+ tmp32_1 = load_reg32(r2);
+ tmp = tcg_temp_new_i64();
+ tcg_gen_extu_i32_i64(tmp, tmp32_1);
+ tcg_gen_mov_i64(psw_addr, tmp);
+ s->is_jmp = DISAS_JUMP;
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i64(tmp);
+ }
+ break;
+ case 0x7: /* BCR M1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ if (r2) {
+ tmp = load_reg(r2);
+ gen_bcr(s, r1, tmp, s->pc);
+ tcg_temp_free_i64(tmp);
+ s->is_jmp = DISAS_TB_JUMP;
+ } else {
+ /* XXX: "serialization and checkpoint-synchronization function"? */
+ }
+ break;
+ case 0xa: /* SVC I [RR] */
+ insn = ld_code2(s->pc);
+ debug_insn(insn);
+ i = insn & 0xff;
+ update_psw_addr(s);
+ gen_op_calc_cc(s);
+ tmp32_1 = tcg_const_i32(i);
+ tmp32_2 = tcg_const_i32(ilc * 2);
+ tmp32_3 = tcg_const_i32(EXCP_SVC);
+ tcg_gen_st_i32(tmp32_1, cpu_env, offsetof(CPUState, int_svc_code));
+ tcg_gen_st_i32(tmp32_2, cpu_env, offsetof(CPUState, int_svc_ilc));
+ gen_helper_exception(tmp32_3);
+ s->is_jmp = DISAS_EXCP;
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+ case 0xd: /* BASR R1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp = tcg_const_i64(pc_to_link_info(s, s->pc + 2));
+ store_reg(r1, tmp);
+ if (r2) {
+ tmp2 = load_reg(r2);
+ tcg_gen_mov_i64(psw_addr, tmp2);
+ tcg_temp_free_i64(tmp2);
+ s->is_jmp = DISAS_JUMP;
+ }
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0xe: /* MVCL R1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r2);
+ potential_page_fault(s);
+ gen_helper_mvcl(cc_op, tmp32_1, tmp32_2);
+ set_cc_static(s);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x10: /* LPR R1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp32_1 = load_reg32(r2);
+ set_cc_abs32(s, tmp32_1);
+ gen_helper_abs_i32(tmp32_1, tmp32_1);
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x11: /* LNR R1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp32_1 = load_reg32(r2);
+ set_cc_nabs32(s, tmp32_1);
+ gen_helper_nabs_i32(tmp32_1, tmp32_1);
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x12: /* LTR R1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp32_1 = load_reg32(r2);
+ if (r1 != r2) {
+ store_reg32(r1, tmp32_1);
+ }
+ set_cc_s32(s, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x13: /* LCR R1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp32_1 = load_reg32(r2);
+ tcg_gen_neg_i32(tmp32_1, tmp32_1);
+ store_reg32(r1, tmp32_1);
+ set_cc_comp32(s, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x14: /* NR R1,R2 [RR] */
+ case 0x16: /* OR R1,R2 [RR] */
+ case 0x17: /* XR R1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp32_2 = load_reg32(r2);
+ tmp32_1 = load_reg32(r1);
+ gen_and_or_xor_i32(opc, tmp32_1, tmp32_2);
+ store_reg32(r1, tmp32_1);
+ set_cc_nz_u32(s, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x18: /* LR R1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp32_1 = load_reg32(r2);
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x15: /* CLR R1,R2 [RR] */
+ case 0x19: /* CR R1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = load_reg32(r2);
+ if (opc == 0x15) {
+ cmp_u32(s, tmp32_1, tmp32_2);
+ } else {
+ cmp_s32(s, tmp32_1, tmp32_2);
+ }
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x1a: /* AR R1,R2 [RR] */
+ case 0x1e: /* ALR R1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = load_reg32(r2);
+ tmp32_3 = tcg_temp_new_i32();
+ tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2);
+ store_reg32(r1, tmp32_3);
+ if (opc == 0x1a) {
+ set_cc_add32(s, tmp32_1, tmp32_2, tmp32_3);
+ } else {
+ set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3);
+ }
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+ case 0x1b: /* SR R1,R2 [RR] */
+ case 0x1f: /* SLR R1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = load_reg32(r2);
+ tmp32_3 = tcg_temp_new_i32();
+ tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2);
+ store_reg32(r1, tmp32_3);
+ if (opc == 0x1b) {
+ set_cc_sub32(s, tmp32_1, tmp32_2, tmp32_3);
+ } else {
+ set_cc_subu32(s, tmp32_1, tmp32_2, tmp32_3);
+ }
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+ case 0x1c: /* MR R1,R2 [RR] */
+ /* reg(r1, r1+1) = reg(r1+1) * reg(r2) */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp2 = load_reg(r2);
+ tmp3 = load_reg((r1 + 1) & 15);
+ tcg_gen_ext32s_i64(tmp2, tmp2);
+ tcg_gen_ext32s_i64(tmp3, tmp3);
+ tcg_gen_mul_i64(tmp2, tmp2, tmp3);
+ store_reg32_i64((r1 + 1) & 15, tmp2);
+ tcg_gen_shri_i64(tmp2, tmp2, 32);
+ store_reg32_i64(r1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x1d: /* DR R1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = load_reg32(r1 + 1);
+ tmp32_3 = load_reg32(r2);
+
+ tmp = tcg_temp_new_i64(); /* dividend */
+ tmp2 = tcg_temp_new_i64(); /* divisor */
+ tmp3 = tcg_temp_new_i64();
+
+ /* dividend is r(r1 << 32) | r(r1 + 1) */
+ tcg_gen_extu_i32_i64(tmp, tmp32_1);
+ tcg_gen_extu_i32_i64(tmp2, tmp32_2);
+ tcg_gen_shli_i64(tmp, tmp, 32);
+ tcg_gen_or_i64(tmp, tmp, tmp2);
+
+ /* divisor is r(r2) */
+ tcg_gen_ext_i32_i64(tmp2, tmp32_3);
+
+ tcg_gen_div_i64(tmp3, tmp, tmp2);
+ tcg_gen_rem_i64(tmp, tmp, tmp2);
+
+ tcg_gen_trunc_i64_i32(tmp32_1, tmp);
+ tcg_gen_trunc_i64_i32(tmp32_2, tmp3);
+
+ store_reg32(r1, tmp32_1); /* remainder */
+ store_reg32(r1 + 1, tmp32_2); /* quotient */
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x28: /* LDR R1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp = load_freg(r2);
+ store_freg(r1, tmp);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x38: /* LER R1,R2 [RR] */
+ insn = ld_code2(s->pc);
+ decode_rr(s, insn, &r1, &r2);
+ tmp32_1 = load_freg32(r2);
+ store_freg32(r1, tmp32_1);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x40: /* STH R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = load_reg(r1);
+ tcg_gen_qemu_st16(tmp2, tmp, get_mem_index(s));
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x41: /* la */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ store_reg(r1, tmp); /* FIXME: 31/24-bit addressing */
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x42: /* STC R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = load_reg(r1);
+ tcg_gen_qemu_st8(tmp2, tmp, get_mem_index(s));
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x43: /* IC R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld8u(tmp2, tmp, get_mem_index(s));
+ store_reg8(r1, tmp2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x44: /* EX R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = load_reg(r1);
+ tmp3 = tcg_const_i64(s->pc + 4);
+ update_psw_addr(s);
+ gen_op_calc_cc(s);
+ gen_helper_ex(cc_op, cc_op, tmp2, tmp, tmp3);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x46: /* BCT R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tcg_temp_free_i64(tmp);
+
+ tmp32_1 = load_reg32(r1);
+ tcg_gen_subi_i32(tmp32_1, tmp32_1, 1);
+ store_reg32(r1, tmp32_1);
+
+ gen_update_cc_op(s);
+ l1 = gen_new_label();
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp32_1, 0, l1);
+
+ /* not taking the branch, jump to after the instruction */
+ gen_goto_tb(s, 0, s->pc + 4);
+ gen_set_label(l1);
+
+ /* take the branch, move R2 into psw.addr */
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tcg_gen_mov_i64(psw_addr, tmp);
+ s->is_jmp = DISAS_JUMP;
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i64(tmp);
+ break;
+ case 0x47: /* BC M1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ gen_bcr(s, r1, tmp, s->pc + 4);
+ tcg_temp_free_i64(tmp);
+ s->is_jmp = DISAS_TB_JUMP;
+ break;
+ case 0x48: /* LH R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld16s(tmp2, tmp, get_mem_index(s));
+ store_reg32_i64(r1, tmp2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x49: /* CH R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = tcg_temp_new_i32();
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld16s(tmp2, tmp, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_2, tmp2);
+ cmp_s32(s, tmp32_1, tmp32_2);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x4a: /* AH R1,D2(X2,B2) [RX] */
+ case 0x4b: /* SH R1,D2(X2,B2) [RX] */
+ case 0x4c: /* MH R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = tcg_temp_new_i32();
+ tmp32_3 = tcg_temp_new_i32();
+
+ tcg_gen_qemu_ld16s(tmp2, tmp, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_2, tmp2);
+ switch (opc) {
+ case 0x4a:
+ tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2);
+ set_cc_add32(s, tmp32_1, tmp32_2, tmp32_3);
+ break;
+ case 0x4b:
+ tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2);
+ set_cc_sub32(s, tmp32_1, tmp32_2, tmp32_3);
+ break;
+ case 0x4c:
+ tcg_gen_mul_i32(tmp32_3, tmp32_1, tmp32_2);
+ break;
+ default:
+ tcg_abort();
+ }
+ store_reg32(r1, tmp32_3);
+
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x4d: /* BAS R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = tcg_const_i64(pc_to_link_info(s, s->pc + 4));
+ store_reg(r1, tmp2);
+ tcg_gen_mov_i64(psw_addr, tmp);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ s->is_jmp = DISAS_JUMP;
+ break;
+ case 0x4e: /* CVD R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(tmp32_1, regs[r1]);
+ gen_helper_cvd(tmp2, tmp32_1);
+ tcg_gen_qemu_st64(tmp2, tmp, get_mem_index(s));
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x50: /* st r1, d2(x2, b2) */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = load_reg(r1);
+ tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s));
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x55: /* CL R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = tcg_temp_new_i32();
+ tmp32_2 = load_reg32(r1);
+ tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_1, tmp2);
+ cmp_u32(s, tmp32_2, tmp32_1);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x54: /* N R1,D2(X2,B2) [RX] */
+ case 0x56: /* O R1,D2(X2,B2) [RX] */
+ case 0x57: /* X R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_2, tmp2);
+ gen_and_or_xor_i32(opc, tmp32_1, tmp32_2);
+ store_reg32(r1, tmp32_1);
+ set_cc_nz_u32(s, tmp32_1);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x58: /* l r1, d2(x2, b2) */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_1, tmp2);
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x59: /* C R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = tcg_temp_new_i32();
+ tmp32_2 = load_reg32(r1);
+ tcg_gen_qemu_ld32s(tmp2, tmp, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_1, tmp2);
+ cmp_s32(s, tmp32_2, tmp32_1);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x5a: /* A R1,D2(X2,B2) [RX] */
+ case 0x5b: /* S R1,D2(X2,B2) [RX] */
+ case 0x5e: /* AL R1,D2(X2,B2) [RX] */
+ case 0x5f: /* SL R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = tcg_temp_new_i32();
+ tmp32_3 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32s(tmp, tmp, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_2, tmp);
+ switch (opc) {
+ case 0x5a:
+ case 0x5e:
+ tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2);
+ break;
+ case 0x5b:
+ case 0x5f:
+ tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2);
+ break;
+ default:
+ tcg_abort();
+ }
+ store_reg32(r1, tmp32_3);
+ switch (opc) {
+ case 0x5a:
+ set_cc_add32(s, tmp32_1, tmp32_2, tmp32_3);
+ break;
+ case 0x5e:
+ set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3);
+ break;
+ case 0x5b:
+ set_cc_sub32(s, tmp32_1, tmp32_2, tmp32_3);
+ break;
+ case 0x5f:
+ set_cc_subu32(s, tmp32_1, tmp32_2, tmp32_3);
+ break;
+ default:
+ tcg_abort();
+ }
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ break;
+ case 0x5c: /* M R1,D2(X2,B2) [RX] */
+ /* reg(r1, r1+1) = reg(r1+1) * *(s32*)addr */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld32s(tmp2, tmp, get_mem_index(s));
+ tmp3 = load_reg((r1 + 1) & 15);
+ tcg_gen_ext32s_i64(tmp2, tmp2);
+ tcg_gen_ext32s_i64(tmp3, tmp3);
+ tcg_gen_mul_i64(tmp2, tmp2, tmp3);
+ store_reg32_i64((r1 + 1) & 15, tmp2);
+ tcg_gen_shri_i64(tmp2, tmp2, 32);
+ store_reg32_i64(r1, tmp2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x5d: /* D R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp3 = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = load_reg32(r1 + 1);
+
+ tmp = tcg_temp_new_i64();
+ tmp2 = tcg_temp_new_i64();
+
+ /* dividend is r(r1 << 32) | r(r1 + 1) */
+ tcg_gen_extu_i32_i64(tmp, tmp32_1);
+ tcg_gen_extu_i32_i64(tmp2, tmp32_2);
+ tcg_gen_shli_i64(tmp, tmp, 32);
+ tcg_gen_or_i64(tmp, tmp, tmp2);
+
+ /* divisor is in memory */
+ tcg_gen_qemu_ld32s(tmp2, tmp3, get_mem_index(s));
+
+ /* XXX divisor == 0 -> FixP divide exception */
+
+ tcg_gen_div_i64(tmp3, tmp, tmp2);
+ tcg_gen_rem_i64(tmp, tmp, tmp2);
+
+ tcg_gen_trunc_i64_i32(tmp32_1, tmp);
+ tcg_gen_trunc_i64_i32(tmp32_2, tmp3);
+
+ store_reg32(r1, tmp32_1); /* remainder */
+ store_reg32(r1 + 1, tmp32_2); /* quotient */
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x60: /* STD R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = load_freg(r1);
+ tcg_gen_qemu_st64(tmp2, tmp, get_mem_index(s));
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x68: /* LD R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld64(tmp2, tmp, get_mem_index(s));
+ store_freg(r1, tmp2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x70: /* STE R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = load_freg32(r1);
+ tcg_gen_extu_i32_i64(tmp2, tmp32_1);
+ tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s));
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0x71: /* MS R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32s(tmp2, tmp, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_2, tmp2);
+ tcg_gen_mul_i32(tmp32_1, tmp32_1, tmp32_2);
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x78: /* LE R1,D2(X2,B2) [RX] */
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_1, tmp2);
+ store_freg32(r1, tmp32_1);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+#ifndef CONFIG_USER_ONLY
+ case 0x80: /* SSM D2(B2) [S] */
+ /* Set System Mask */
+ check_privileged(s, ilc);
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp3 = tcg_temp_new_i64();
+ tcg_gen_andi_i64(tmp3, psw_mask, ~0xff00000000000000ULL);
+ tcg_gen_qemu_ld8u(tmp2, tmp, get_mem_index(s));
+ tcg_gen_shli_i64(tmp2, tmp2, 56);
+ tcg_gen_or_i64(psw_mask, tmp3, tmp2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+ case 0x82: /* LPSW D2(B2) [S] */
+ /* Load PSW */
+ check_privileged(s, ilc);
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp3 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s));
+ tcg_gen_addi_i64(tmp, tmp, 4);
+ tcg_gen_qemu_ld32u(tmp3, tmp, get_mem_index(s));
+ gen_helper_load_psw(tmp2, tmp3);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ /* we need to keep cc_op intact */
+ s->is_jmp = DISAS_JUMP;
+ break;
+ case 0x83: /* DIAG R1,R3,D2 [RS] */
+ /* Diagnose call (KVM hypercall) */
+ check_privileged(s, ilc);
+ potential_page_fault(s);
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp32_1 = tcg_const_i32(insn & 0xfff);
+ tmp2 = load_reg(2);
+ tmp3 = load_reg(1);
+ gen_helper_diag(tmp2, tmp32_1, tmp2, tmp3);
+ store_reg(2, tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+#endif
+ case 0x88: /* SRL R1,D2(B2) [RS] */
+ case 0x89: /* SLL R1,D2(B2) [RS] */
+ case 0x8a: /* SRA R1,D2(B2) [RS] */
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(tmp32_2, tmp);
+ tcg_gen_andi_i32(tmp32_2, tmp32_2, 0x3f);
+ switch (opc) {
+ case 0x88:
+ tcg_gen_shr_i32(tmp32_1, tmp32_1, tmp32_2);
+ break;
+ case 0x89:
+ tcg_gen_shl_i32(tmp32_1, tmp32_1, tmp32_2);
+ break;
+ case 0x8a:
+ tcg_gen_sar_i32(tmp32_1, tmp32_1, tmp32_2);
+ set_cc_s32(s, tmp32_1);
+ break;
+ default:
+ tcg_abort();
+ }
+ store_reg32(r1, tmp32_1);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x8c: /* SRDL R1,D2(B2) [RS] */
+ case 0x8d: /* SLDL R1,D2(B2) [RS] */
+ case 0x8e: /* SRDA R1,D2(B2) [RS] */
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2); /* shift */
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = load_reg32(r1 + 1);
+ tcg_gen_concat_i32_i64(tmp2, tmp32_2, tmp32_1); /* operand */
+ switch (opc) {
+ case 0x8c:
+ tcg_gen_shr_i64(tmp2, tmp2, tmp);
+ break;
+ case 0x8d:
+ tcg_gen_shl_i64(tmp2, tmp2, tmp);
+ break;
+ case 0x8e:
+ tcg_gen_sar_i64(tmp2, tmp2, tmp);
+ set_cc_s64(s, tmp2);
+ break;
+ }
+ tcg_gen_shri_i64(tmp, tmp2, 32);
+ tcg_gen_trunc_i64_i32(tmp32_1, tmp);
+ store_reg32(r1, tmp32_1);
+ tcg_gen_trunc_i64_i32(tmp32_2, tmp2);
+ store_reg32(r1 + 1, tmp32_2);
+ break;
+ case 0x98: /* LM R1,R3,D2(B2) [RS] */
+ case 0x90: /* STM R1,R3,D2(B2) [RS] */
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+
+ tmp = get_address(s, 0, b2, d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp3 = tcg_const_i64(4);
+ tmp4 = tcg_const_i64(0xffffffff00000000ULL);
+ for (i = r1;; i = (i + 1) % 16) {
+ if (opc == 0x98) {
+ tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s));
+ tcg_gen_and_i64(regs[i], regs[i], tmp4);
+ tcg_gen_or_i64(regs[i], regs[i], tmp2);
+ } else {
+ tcg_gen_qemu_st32(regs[i], tmp, get_mem_index(s));
+ }
+ if (i == r3) {
+ break;
+ }
+ tcg_gen_add_i64(tmp, tmp, tmp3);
+ }
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ tcg_temp_free_i64(tmp4);
+ break;
+ case 0x91: /* TM D1(B1),I2 [SI] */
+ insn = ld_code4(s->pc);
+ tmp = decode_si(s, insn, &i2, &b1, &d1);
+ tmp2 = tcg_const_i64(i2);
+ tcg_gen_qemu_ld8u(tmp, tmp, get_mem_index(s));
+ cmp_64(s, tmp, tmp2, CC_OP_TM_32);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x92: /* MVI D1(B1),I2 [SI] */
+ insn = ld_code4(s->pc);
+ tmp = decode_si(s, insn, &i2, &b1, &d1);
+ tmp2 = tcg_const_i64(i2);
+ tcg_gen_qemu_st8(tmp2, tmp, get_mem_index(s));
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x94: /* NI D1(B1),I2 [SI] */
+ case 0x96: /* OI D1(B1),I2 [SI] */
+ case 0x97: /* XI D1(B1),I2 [SI] */
+ insn = ld_code4(s->pc);
+ tmp = decode_si(s, insn, &i2, &b1, &d1);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld8u(tmp2, tmp, get_mem_index(s));
+ switch (opc) {
+ case 0x94:
+ tcg_gen_andi_i64(tmp2, tmp2, i2);
+ break;
+ case 0x96:
+ tcg_gen_ori_i64(tmp2, tmp2, i2);
+ break;
+ case 0x97:
+ tcg_gen_xori_i64(tmp2, tmp2, i2);
+ break;
+ default:
+ tcg_abort();
+ }
+ tcg_gen_qemu_st8(tmp2, tmp, get_mem_index(s));
+ set_cc_nz_u64(s, tmp2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x95: /* CLI D1(B1),I2 [SI] */
+ insn = ld_code4(s->pc);
+ tmp = decode_si(s, insn, &i2, &b1, &d1);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_qemu_ld8u(tmp2, tmp, get_mem_index(s));
+ cmp_u64c(s, tmp2, i2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0x9a: /* LAM R1,R3,D2(B2) [RS] */
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r3);
+ potential_page_fault(s);
+ gen_helper_lam(tmp32_1, tmp, tmp32_2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0x9b: /* STAM R1,R3,D2(B2) [RS] */
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r3);
+ potential_page_fault(s);
+ gen_helper_stam(tmp32_1, tmp, tmp32_2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0xa5:
+ insn = ld_code4(s->pc);
+ r1 = (insn >> 20) & 0xf;
+ op = (insn >> 16) & 0xf;
+ i2 = insn & 0xffff;
+ disas_a5(s, op, r1, i2);
+ break;
+ case 0xa7:
+ insn = ld_code4(s->pc);
+ r1 = (insn >> 20) & 0xf;
+ op = (insn >> 16) & 0xf;
+ i2 = (short)insn;
+ disas_a7(s, op, r1, i2);
+ break;
+ case 0xa8: /* MVCLE R1,R3,D2(B2) [RS] */
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r3);
+ potential_page_fault(s);
+ gen_helper_mvcle(cc_op, tmp32_1, tmp, tmp32_2);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0xa9: /* CLCLE R1,R3,D2(B2) [RS] */
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r3);
+ potential_page_fault(s);
+ gen_helper_clcle(cc_op, tmp32_1, tmp, tmp32_2);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+#ifndef CONFIG_USER_ONLY
+ case 0xac: /* STNSM D1(B1),I2 [SI] */
+ case 0xad: /* STOSM D1(B1),I2 [SI] */
+ check_privileged(s, ilc);
+ insn = ld_code4(s->pc);
+ tmp = decode_si(s, insn, &i2, &b1, &d1);
+ tmp2 = tcg_temp_new_i64();
+ tcg_gen_shri_i64(tmp2, psw_mask, 56);
+ tcg_gen_qemu_st8(tmp2, tmp, get_mem_index(s));
+ if (opc == 0xac) {
+ tcg_gen_andi_i64(psw_mask, psw_mask,
+ ((uint64_t)i2 << 56) | 0x00ffffffffffffffULL);
+ } else {
+ tcg_gen_ori_i64(psw_mask, psw_mask, (uint64_t)i2 << 56);
+ }
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ case 0xae: /* SIGP R1,R3,D2(B2) [RS] */
+ check_privileged(s, ilc);
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp2 = load_reg(r3);
+ tmp32_1 = tcg_const_i32(r1);
+ potential_page_fault(s);
+ gen_helper_sigp(cc_op, tmp, tmp32_1, tmp2);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+ case 0xb1: /* LRA R1,D2(X2, B2) [RX] */
+ check_privileged(s, ilc);
+ insn = ld_code4(s->pc);
+ tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
+ tmp32_1 = tcg_const_i32(r1);
+ potential_page_fault(s);
+ gen_helper_lra(cc_op, tmp, tmp32_1);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ break;
+#endif
+ case 0xb2:
+ insn = ld_code4(s->pc);
+ op = (insn >> 16) & 0xff;
+ switch (op) {
+ case 0x9c: /* STFPC D2(B2) [S] */
+ d2 = insn & 0xfff;
+ b2 = (insn >> 12) & 0xf;
+ tmp32_1 = tcg_temp_new_i32();
+ tmp = tcg_temp_new_i64();
+ tmp2 = get_address(s, 0, b2, d2);
+ tcg_gen_ld_i32(tmp32_1, cpu_env, offsetof(CPUState, fpc));
+ tcg_gen_extu_i32_i64(tmp, tmp32_1);
+ tcg_gen_qemu_st32(tmp, tmp2, get_mem_index(s));
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+ default:
+ disas_b2(s, op, insn);
+ break;
+ }
+ break;
+ case 0xb3:
+ insn = ld_code4(s->pc);
+ op = (insn >> 16) & 0xff;
+ r3 = (insn >> 12) & 0xf; /* aka m3 */
+ r1 = (insn >> 4) & 0xf;
+ r2 = insn & 0xf;
+ disas_b3(s, op, r3, r1, r2);
+ break;
+#ifndef CONFIG_USER_ONLY
+ case 0xb6: /* STCTL R1,R3,D2(B2) [RS] */
+ /* Store Control */
+ check_privileged(s, ilc);
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r3);
+ potential_page_fault(s);
+ gen_helper_stctl(tmp32_1, tmp, tmp32_2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0xb7: /* LCTL R1,R3,D2(B2) [RS] */
+ /* Load Control */
+ check_privileged(s, ilc);
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r3);
+ potential_page_fault(s);
+ gen_helper_lctl(tmp32_1, tmp, tmp32_2);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+#endif
+ case 0xb9:
+ insn = ld_code4(s->pc);
+ r1 = (insn >> 4) & 0xf;
+ r2 = insn & 0xf;
+ op = (insn >> 16) & 0xff;
+ disas_b9(s, op, r1, r2);
+ break;
+ case 0xba: /* CS R1,R3,D2(B2) [RS] */
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = tcg_const_i32(r1);
+ tmp32_2 = tcg_const_i32(r3);
+ potential_page_fault(s);
+ gen_helper_cs(cc_op, tmp32_1, tmp, tmp32_2);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0xbd: /* CLM R1,M3,D2(B2) [RS] */
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = tcg_const_i32(r3);
+ potential_page_fault(s);
+ gen_helper_clm(cc_op, tmp32_1, tmp32_2, tmp);
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0xbe: /* STCM R1,M3,D2(B2) [RS] */
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = tcg_const_i32(r3);
+ potential_page_fault(s);
+ gen_helper_stcm(tmp32_1, tmp32_2, tmp);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ break;
+ case 0xbf: /* ICM R1,M3,D2(B2) [RS] */
+ insn = ld_code4(s->pc);
+ decode_rs(s, insn, &r1, &r3, &b2, &d2);
+ if (r3 == 15) {
+ /* effectively a 32-bit load */
+ tmp = get_address(s, 0, b2, d2);
+ tmp32_1 = tcg_temp_new_i32();
+ tmp32_2 = tcg_const_i32(r3);
+ tcg_gen_qemu_ld32u(tmp, tmp, get_mem_index(s));
+ store_reg32_i64(r1, tmp);
+ tcg_gen_trunc_i64_i32(tmp32_1, tmp);
+ set_cc_icm(s, tmp32_2, tmp32_1);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ } else if (r3) {
+ uint32_t mask = 0x00ffffffUL;
+ uint32_t shift = 24;
+ int m3 = r3;
+ tmp = get_address(s, 0, b2, d2);
+ tmp2 = tcg_temp_new_i64();
+ tmp32_1 = load_reg32(r1);
+ tmp32_2 = tcg_temp_new_i32();
+ tmp32_3 = tcg_const_i32(r3);
+ tmp32_4 = tcg_const_i32(0);
+ while (m3) {
+ if (m3 & 8) {
+ tcg_gen_qemu_ld8u(tmp2, tmp, get_mem_index(s));
+ tcg_gen_trunc_i64_i32(tmp32_2, tmp2);
+ if (shift) {
+ tcg_gen_shli_i32(tmp32_2, tmp32_2, shift);
+ }
+ tcg_gen_andi_i32(tmp32_1, tmp32_1, mask);
+ tcg_gen_or_i32(tmp32_1, tmp32_1, tmp32_2);
+ tcg_gen_or_i32(tmp32_4, tmp32_4, tmp32_2);
+ tcg_gen_addi_i64(tmp, tmp, 1);
+ }
+ m3 = (m3 << 1) & 0xf;
+ mask = (mask >> 8) | 0xff000000UL;
+ shift -= 8;
+ }
+ store_reg32(r1, tmp32_1);
+ set_cc_icm(s, tmp32_3, tmp32_4);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i32(tmp32_1);
+ tcg_temp_free_i32(tmp32_2);
+ tcg_temp_free_i32(tmp32_3);
+ tcg_temp_free_i32(tmp32_4);
+ } else {
+ /* i.e. env->cc = 0 */
+ gen_op_movi_cc(s, 0);
+ }
+ break;
+ case 0xc0:
+ case 0xc2:
+ insn = ld_code6(s->pc);
+ r1 = (insn >> 36) & 0xf;
+ op = (insn >> 32) & 0xf;
+ i2 = (int)insn;
+ switch (opc) {
+ case 0xc0:
+ disas_c0(s, op, r1, i2);
+ break;
+ case 0xc2:
+ disas_c2(s, op, r1, i2);
+ break;
+ default:
+ tcg_abort();
+ }
+ break;
+ case 0xd2: /* MVC D1(L,B1),D2(B2) [SS] */
+ case 0xd4: /* NC D1(L,B1),D2(B2) [SS] */
+ case 0xd5: /* CLC D1(L,B1),D2(B2) [SS] */
+ case 0xd6: /* OC D1(L,B1),D2(B2) [SS] */
+ case 0xd7: /* XC D1(L,B1),D2(B2) [SS] */
+ case 0xdc: /* TR D1(L,B1),D2(B2) [SS] */
+ case 0xf3: /* UNPK D1(L1,B1),D2(L2,B2) [SS] */
+ insn = ld_code6(s->pc);
+ vl = tcg_const_i32((insn >> 32) & 0xff);
+ b1 = (insn >> 28) & 0xf;
+ b2 = (insn >> 12) & 0xf;
+ d1 = (insn >> 16) & 0xfff;
+ d2 = insn & 0xfff;
+ tmp = get_address(s, 0, b1, d1);
+ tmp2 = get_address(s, 0, b2, d2);
+ switch (opc) {
+ case 0xd2:
+ gen_op_mvc(s, (insn >> 32) & 0xff, tmp, tmp2);
+ break;
+ case 0xd4:
+ potential_page_fault(s);
+ gen_helper_nc(cc_op, vl, tmp, tmp2);
+ set_cc_static(s);
+ break;
+ case 0xd5:
+ gen_op_clc(s, (insn >> 32) & 0xff, tmp, tmp2);
+ break;
+ case 0xd6:
+ potential_page_fault(s);
+ gen_helper_oc(cc_op, vl, tmp, tmp2);
+ set_cc_static(s);
+ break;
+ case 0xd7:
+ potential_page_fault(s);
+ gen_helper_xc(cc_op, vl, tmp, tmp2);
+ set_cc_static(s);
+ break;
+ case 0xdc:
+ potential_page_fault(s);
+ gen_helper_tr(vl, tmp, tmp2);
+ set_cc_static(s);
+ break;
+ case 0xf3:
+ potential_page_fault(s);
+ gen_helper_unpk(vl, tmp, tmp2);
+ break;
+ default:
+ tcg_abort();
+ }
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ break;
+#ifndef CONFIG_USER_ONLY
+ case 0xda: /* MVCP D1(R1,B1),D2(B2),R3 [SS] */
+ case 0xdb: /* MVCS D1(R1,B1),D2(B2),R3 [SS] */
+ check_privileged(s, ilc);
+ potential_page_fault(s);
+ insn = ld_code6(s->pc);
+ r1 = (insn >> 36) & 0xf;
+ r3 = (insn >> 32) & 0xf;
+ b1 = (insn >> 28) & 0xf;
+ d1 = (insn >> 16) & 0xfff;
+ b2 = (insn >> 12) & 0xf;
+ d2 = insn & 0xfff;
+ tmp = load_reg(r1);
+ /* XXX key in r3 */
+ tmp2 = get_address(s, 0, b1, d1);
+ tmp3 = get_address(s, 0, b2, d2);
+ if (opc == 0xda) {
+ gen_helper_mvcp(cc_op, tmp, tmp2, tmp3);
+ } else {
+ gen_helper_mvcs(cc_op, tmp, tmp2, tmp3);
+ }
+ set_cc_static(s);
+ tcg_temp_free_i64(tmp);
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp3);
+ break;
+#endif
+ case 0xe3:
+ insn = ld_code6(s->pc);
+ debug_insn(insn);
+ op = insn & 0xff;
+ r1 = (insn >> 36) & 0xf;
+ x2 = (insn >> 32) & 0xf;
+ b2 = (insn >> 28) & 0xf;
+ d2 = ((int)((((insn >> 16) & 0xfff)
+ | ((insn << 4) & 0xff000)) << 12)) >> 12;
+ disas_e3(s, op, r1, x2, b2, d2 );
+ break;
+#ifndef CONFIG_USER_ONLY
+ case 0xe5:
+ /* Test Protection */
+ check_privileged(s, ilc);
+ insn = ld_code6(s->pc);
+ debug_insn(insn);
+ disas_e5(s, insn);
+ break;
+#endif
+ case 0xeb:
+ insn = ld_code6(s->pc);
+ debug_insn(insn);
+ op = insn & 0xff;
+ r1 = (insn >> 36) & 0xf;
+ r3 = (insn >> 32) & 0xf;
+ b2 = (insn >> 28) & 0xf;
+ d2 = ((int)((((insn >> 16) & 0xfff)
+ | ((insn << 4) & 0xff000)) << 12)) >> 12;
+ disas_eb(s, op, r1, r3, b2, d2);
+ break;
+ case 0xed:
+ insn = ld_code6(s->pc);
+ debug_insn(insn);
+ op = insn & 0xff;
+ r1 = (insn >> 36) & 0xf;
+ x2 = (insn >> 32) & 0xf;
+ b2 = (insn >> 28) & 0xf;
+ d2 = (short)((insn >> 16) & 0xfff);
+ r1b = (insn >> 12) & 0xf;
+ disas_ed(s, op, r1, x2, b2, d2, r1b);
+ break;
+ default:
+ LOG_DISAS("unimplemented opcode 0x%x\n", opc);
+ gen_illegal_opcode(s, ilc);
+ break;
+ }
+
+ /* Instruction length is encoded in the opcode */
+ s->pc += (ilc * 2);
+}
+
+static inline void gen_intermediate_code_internal(CPUState *env,
+ TranslationBlock *tb,
+ int search_pc)
+{
+ DisasContext dc;
+ target_ulong pc_start;
+ uint64_t next_page_start;
+ uint16_t *gen_opc_end;
+ int j, lj = -1;
+ int num_insns, max_insns;
+ CPUBreakpoint *bp;
+
+ pc_start = tb->pc;
+
+ /* 31-bit mode */
+ if (!(tb->flags & FLAG_MASK_64)) {
+ pc_start &= 0x7fffffff;
+ }
+
+ dc.pc = pc_start;
+ dc.is_jmp = DISAS_NEXT;
+ dc.tb = tb;
+ dc.cc_op = CC_OP_DYNAMIC;
+
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+
+ next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
+
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0) {
+ max_insns = CF_COUNT_MASK;
+ }
+
+ gen_icount_start();
+
+ do {
+ if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
+ QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+ if (bp->pc == dc.pc) {
+ gen_debug(&dc);
+ break;
+ }
+ }
+ }
+ if (search_pc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ if (lj < j) {
+ lj++;
+ while (lj < j) {
+ gen_opc_instr_start[lj++] = 0;
+ }
+ }
+ gen_opc_pc[lj] = dc.pc;
+ gen_opc_cc_op[lj] = dc.cc_op;
+ gen_opc_instr_start[lj] = 1;
+ gen_opc_icount[lj] = num_insns;
+ }
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) {
+ gen_io_start();
+ }
+#if defined(S390X_DEBUG_DISAS_VERBOSE)
+ LOG_DISAS("pc " TARGET_FMT_lx "\n",
+ dc.pc);
+#endif
+ disas_s390_insn(&dc);
+
+ num_insns++;
+ if (env->singlestep_enabled) {
+ gen_debug(&dc);
+ }
+ } while (!dc.is_jmp && gen_opc_ptr < gen_opc_end && dc.pc < next_page_start
+ && num_insns < max_insns && !env->singlestep_enabled
+ && !singlestep);
+
+ if (!dc.is_jmp) {
+ update_psw_addr(&dc);
+ }
+
+ if (singlestep && dc.cc_op != CC_OP_DYNAMIC) {
+ gen_op_calc_cc(&dc);
+ } else {
+ /* next TB starts off with CC_OP_DYNAMIC, so make sure the cc op type
+ is in env */
+ gen_op_set_cc_op(&dc);
+ }
+
+ if (tb->cflags & CF_LAST_IO) {
+ gen_io_end();
+ }
+ /* Generate the return instruction */
+ if (dc.is_jmp != DISAS_TB_JUMP) {
+ tcg_gen_exit_tb(0);
+ }
+ gen_icount_end(tb, num_insns);
+ *gen_opc_ptr = INDEX_op_end;
+ if (search_pc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ lj++;
+ while (lj <= j) {
+ gen_opc_instr_start[lj++] = 0;
+ }
+ } else {
+ tb->size = dc.pc - pc_start;
+ tb->icount = num_insns;
+ }
+#if defined(S390X_DEBUG_DISAS)
+ log_cpu_state_mask(CPU_LOG_TB_CPU, env, 0);
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ qemu_log("IN: %s\n", lookup_symbol(pc_start));
+ log_target_disas(pc_start, dc.pc - pc_start, 1);
+ qemu_log("\n");
+ }
+#endif
}
void gen_intermediate_code (CPUState *env, struct TranslationBlock *tb)
{
+ gen_intermediate_code_internal(env, tb, 0);
}
void gen_intermediate_code_pc (CPUState *env, struct TranslationBlock *tb)
{
+ gen_intermediate_code_internal(env, tb, 1);
}
void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos)
{
+ int cc_op;
env->psw.addr = gen_opc_pc[pc_pos];
+ cc_op = gen_opc_cc_op[pc_pos];
+ if ((cc_op != CC_OP_DYNAMIC) && (cc_op != CC_OP_STATIC)) {
+ env->cc_op = cc_op;
+ }
}
commit 81f7c56cb1a27aee3dabf45d6377f3c85a85378f
Author: Alexander Graf <agraf at suse.de>
Date: Wed Mar 23 10:58:07 2011 +0100
s390x: Adjust internal kvm code
We're now finally emulating an s390x CPU, so we can move quite some logic
from the kvm code out into generic CPU code.
This patch does this and adjusts the interfaces according to what the code
around now expects to be able to call.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index 3155693..c927e61 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -49,13 +49,6 @@
#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
@@ -228,9 +221,9 @@ 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)
+static inline void setcc(CPUState *env, uint64_t cc)
{
- env->kvm_run->psw_mask &= ~(3ul << 44);
+ env->kvm_run->psw_mask &= ~(3ull << 44);
env->kvm_run->psw_mask |= (cc & 3) << 44;
env->psw.mask &= ~(3ul << 44);
@@ -248,35 +241,11 @@ static int kvm_sclp_service_call(CPUState *env, struct kvm_run *run,
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) {
+ r = sclp_service_call(env, sccb, code);
+ if (r) {
setcc(env, 3);
}
+
return 0;
}
@@ -449,7 +418,8 @@ static int handle_intercept(CPUState *env)
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);
+ dprintf("intercept: 0x%x (at 0x%lx)\n", icpt_code,
+ (long)env->kvm_run->psw_addr);
switch (icpt_code) {
case ICPT_INSTRUCTION:
r = handle_instruction(env, run);
commit defb0e3157af2934c412f6be69740003b9c8a2b9
Author: Alexander Graf <agraf at suse.de>
Date: Wed Mar 23 10:58:07 2011 +0100
s390x: Implement opcode helpers
There are some instructions that can't (or shouldn't) be expressed by pure
tcg code. For those, we call into externally compiled C functions.
This patch implements those C functions.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/helpers.h b/target-s390x/helpers.h
new file mode 100644
index 0000000..6ca48eb
--- /dev/null
+++ b/target-s390x/helpers.h
@@ -0,0 +1,151 @@
+#include "def-helper.h"
+
+DEF_HELPER_1(exception, void, i32)
+DEF_HELPER_3(nc, i32, i32, i64, i64)
+DEF_HELPER_3(oc, i32, i32, i64, i64)
+DEF_HELPER_3(xc, i32, i32, i64, i64)
+DEF_HELPER_3(mvc, void, i32, i64, i64)
+DEF_HELPER_3(clc, i32, i32, i64, i64)
+DEF_HELPER_2(mvcl, i32, i32, i32)
+DEF_HELPER_FLAGS_1(set_cc_comp_s32, TCG_CALL_PURE|TCG_CALL_CONST, i32, s32)
+DEF_HELPER_FLAGS_1(set_cc_comp_s64, TCG_CALL_PURE|TCG_CALL_CONST, i32, s64)
+DEF_HELPER_FLAGS_2(set_cc_icm, TCG_CALL_PURE|TCG_CALL_CONST, i32, i32, i32)
+DEF_HELPER_3(clm, i32, i32, i32, i64)
+DEF_HELPER_3(stcm, void, i32, i32, i64)
+DEF_HELPER_2(mlg, void, i32, i64)
+DEF_HELPER_2(dlg, void, i32, i64)
+DEF_HELPER_FLAGS_3(set_cc_add64, TCG_CALL_PURE|TCG_CALL_CONST, i32, s64, s64, s64)
+DEF_HELPER_FLAGS_3(set_cc_addu64, TCG_CALL_PURE|TCG_CALL_CONST, i32, i64, i64, i64)
+DEF_HELPER_FLAGS_3(set_cc_add32, TCG_CALL_PURE|TCG_CALL_CONST, i32, s32, s32, s32)
+DEF_HELPER_FLAGS_3(set_cc_addu32, TCG_CALL_PURE|TCG_CALL_CONST, i32, i32, i32, i32)
+DEF_HELPER_FLAGS_3(set_cc_sub64, TCG_CALL_PURE|TCG_CALL_CONST, i32, s64, s64, s64)
+DEF_HELPER_FLAGS_3(set_cc_subu64, TCG_CALL_PURE|TCG_CALL_CONST, i32, i64, i64, i64)
+DEF_HELPER_FLAGS_3(set_cc_sub32, TCG_CALL_PURE|TCG_CALL_CONST, i32, s32, s32, s32)
+DEF_HELPER_FLAGS_3(set_cc_subu32, TCG_CALL_PURE|TCG_CALL_CONST, i32, i32, i32, i32)
+DEF_HELPER_3(srst, i32, i32, i32, i32)
+DEF_HELPER_3(clst, i32, i32, i32, i32)
+DEF_HELPER_3(mvpg, void, i64, i64, i64)
+DEF_HELPER_3(mvst, void, i32, i32, i32)
+DEF_HELPER_3(csg, i32, i32, i64, i32)
+DEF_HELPER_3(cdsg, i32, i32, i64, i32)
+DEF_HELPER_3(cs, i32, i32, i64, i32)
+DEF_HELPER_4(ex, i32, i32, i64, i64, i64)
+DEF_HELPER_FLAGS_1(abs_i32, TCG_CALL_PURE|TCG_CALL_CONST, i32, s32)
+DEF_HELPER_FLAGS_1(nabs_i32, TCG_CALL_PURE|TCG_CALL_CONST, s32, s32)
+DEF_HELPER_FLAGS_1(abs_i64, TCG_CALL_PURE|TCG_CALL_CONST, i64, s64)
+DEF_HELPER_FLAGS_1(nabs_i64, TCG_CALL_PURE|TCG_CALL_CONST, s64, s64)
+DEF_HELPER_3(stcmh, void, i32, i64, i32)
+DEF_HELPER_3(icmh, i32, i32, i64, i32)
+DEF_HELPER_2(ipm, void, i32, i32)
+DEF_HELPER_FLAGS_3(addc_u32, TCG_CALL_PURE|TCG_CALL_CONST, i32, i32, i32, i32)
+DEF_HELPER_FLAGS_3(set_cc_addc_u64, TCG_CALL_PURE|TCG_CALL_CONST, i32, i64, i64, i64)
+DEF_HELPER_3(stam, void, i32, i64, i32)
+DEF_HELPER_3(lam, void, i32, i64, i32)
+DEF_HELPER_3(mvcle, i32, i32, i64, i32)
+DEF_HELPER_3(clcle, i32, i32, i64, i32)
+DEF_HELPER_3(slb, i32, i32, i32, i32)
+DEF_HELPER_4(slbg, i32, i32, i32, i64, i64)
+DEF_HELPER_2(cefbr, void, i32, s32)
+DEF_HELPER_2(cdfbr, void, i32, s32)
+DEF_HELPER_2(cxfbr, void, i32, s32)
+DEF_HELPER_2(cegbr, void, i32, s64)
+DEF_HELPER_2(cdgbr, void, i32, s64)
+DEF_HELPER_2(cxgbr, void, i32, s64)
+DEF_HELPER_2(adbr, i32, i32, i32)
+DEF_HELPER_2(aebr, i32, i32, i32)
+DEF_HELPER_2(sebr, i32, i32, i32)
+DEF_HELPER_2(sdbr, i32, i32, i32)
+DEF_HELPER_2(debr, void, i32, i32)
+DEF_HELPER_2(dxbr, void, i32, i32)
+DEF_HELPER_2(mdbr, void, i32, i32)
+DEF_HELPER_2(mxbr, void, i32, i32)
+DEF_HELPER_2(ldebr, void, i32, i32)
+DEF_HELPER_2(ldxbr, void, i32, i32)
+DEF_HELPER_2(lxdbr, void, i32, i32)
+DEF_HELPER_2(ledbr, void, i32, i32)
+DEF_HELPER_2(lexbr, void, i32, i32)
+DEF_HELPER_2(lpebr, i32, i32, i32)
+DEF_HELPER_2(lpdbr, i32, i32, i32)
+DEF_HELPER_2(lpxbr, i32, i32, i32)
+DEF_HELPER_2(ltebr, i32, i32, i32)
+DEF_HELPER_2(ltdbr, i32, i32, i32)
+DEF_HELPER_2(ltxbr, i32, i32, i32)
+DEF_HELPER_2(lcebr, i32, i32, i32)
+DEF_HELPER_2(lcdbr, i32, i32, i32)
+DEF_HELPER_2(lcxbr, i32, i32, i32)
+DEF_HELPER_2(aeb, void, i32, i32)
+DEF_HELPER_2(deb, void, i32, i32)
+DEF_HELPER_2(meeb, void, i32, i32)
+DEF_HELPER_2(cdb, i32, i32, i64)
+DEF_HELPER_2(adb, i32, i32, i64)
+DEF_HELPER_2(seb, void, i32, i32)
+DEF_HELPER_2(sdb, i32, i32, i64)
+DEF_HELPER_2(mdb, void, i32, i64)
+DEF_HELPER_2(ddb, void, i32, i64)
+DEF_HELPER_FLAGS_2(cebr, TCG_CALL_PURE, i32, i32, i32)
+DEF_HELPER_FLAGS_2(cdbr, TCG_CALL_PURE, i32, i32, i32)
+DEF_HELPER_FLAGS_2(cxbr, TCG_CALL_PURE, i32, i32, i32)
+DEF_HELPER_3(cgebr, i32, i32, i32, i32)
+DEF_HELPER_3(cgdbr, i32, i32, i32, i32)
+DEF_HELPER_3(cgxbr, i32, i32, i32, i32)
+DEF_HELPER_1(lzer, void, i32)
+DEF_HELPER_1(lzdr, void, i32)
+DEF_HELPER_1(lzxr, void, i32)
+DEF_HELPER_3(cfebr, i32, i32, i32, i32)
+DEF_HELPER_3(cfdbr, i32, i32, i32, i32)
+DEF_HELPER_3(cfxbr, i32, i32, i32, i32)
+DEF_HELPER_2(axbr, i32, i32, i32)
+DEF_HELPER_2(sxbr, i32, i32, i32)
+DEF_HELPER_2(meebr, void, i32, i32)
+DEF_HELPER_2(ddbr, void, i32, i32)
+DEF_HELPER_3(madb, void, i32, i64, i32)
+DEF_HELPER_3(maebr, void, i32, i32, i32)
+DEF_HELPER_3(madbr, void, i32, i32, i32)
+DEF_HELPER_3(msdbr, void, i32, i32, i32)
+DEF_HELPER_2(lxdb, void, i32, i64)
+DEF_HELPER_FLAGS_2(tceb, TCG_CALL_PURE, i32, i32, i64)
+DEF_HELPER_FLAGS_2(tcdb, TCG_CALL_PURE, i32, i32, i64)
+DEF_HELPER_FLAGS_2(tcxb, TCG_CALL_PURE, i32, i32, i64)
+DEF_HELPER_2(flogr, i32, i32, i64)
+DEF_HELPER_2(sqdbr, void, i32, i32)
+DEF_HELPER_FLAGS_1(cvd, TCG_CALL_PURE|TCG_CALL_CONST, i64, s32)
+DEF_HELPER_3(unpk, void, i32, i64, i64)
+DEF_HELPER_3(tr, void, i32, i64, i64)
+
+DEF_HELPER_2(servc, i32, i32, i64)
+DEF_HELPER_3(diag, i64, i32, i64, i64)
+DEF_HELPER_2(load_psw, void, i64, i64)
+DEF_HELPER_1(program_interrupt, void, i32)
+DEF_HELPER_FLAGS_1(stidp, TCG_CALL_CONST, void, i64)
+DEF_HELPER_FLAGS_1(spx, TCG_CALL_CONST, void, i64)
+DEF_HELPER_FLAGS_1(sck, TCG_CALL_CONST, i32, i64)
+DEF_HELPER_1(stck, i32, i64)
+DEF_HELPER_1(stcke, i32, i64)
+DEF_HELPER_FLAGS_1(sckc, TCG_CALL_CONST, void, i64)
+DEF_HELPER_FLAGS_1(stckc, TCG_CALL_CONST, void, i64)
+DEF_HELPER_FLAGS_1(spt, TCG_CALL_CONST, void, i64)
+DEF_HELPER_FLAGS_1(stpt, TCG_CALL_CONST, void, i64)
+DEF_HELPER_3(stsi, i32, i64, i32, i32)
+DEF_HELPER_3(lctl, void, i32, i64, i32)
+DEF_HELPER_3(lctlg, void, i32, i64, i32)
+DEF_HELPER_3(stctl, void, i32, i64, i32)
+DEF_HELPER_3(stctg, void, i32, i64, i32)
+DEF_HELPER_FLAGS_2(tprot, TCG_CALL_CONST, i32, i64, i64)
+DEF_HELPER_FLAGS_1(iske, TCG_CALL_PURE|TCG_CALL_CONST, i64, i64)
+DEF_HELPER_FLAGS_2(sske, TCG_CALL_CONST, void, i32, i64)
+DEF_HELPER_FLAGS_2(rrbe, TCG_CALL_CONST, i32, i32, i64)
+DEF_HELPER_2(csp, i32, i32, i32)
+DEF_HELPER_3(mvcs, i32, i64, i64, i64)
+DEF_HELPER_3(mvcp, i32, i64, i64, i64)
+DEF_HELPER_3(sigp, i32, i64, i32, i64)
+DEF_HELPER_1(sacf, void, i64)
+DEF_HELPER_FLAGS_2(ipte, TCG_CALL_CONST, void, i64, i64)
+DEF_HELPER_FLAGS_0(ptlb, TCG_CALL_CONST, void)
+DEF_HELPER_2(lra, i32, i64, i32)
+DEF_HELPER_2(stura, void, i64, i32)
+DEF_HELPER_2(cksm, void, i32, i32)
+
+DEF_HELPER_FLAGS_4(calc_cc, TCG_CALL_PURE|TCG_CALL_CONST,
+ i32, i32, i64, i64, i64)
+
+#include "def-helper.h"
diff --git a/target-s390x/op_helper.c b/target-s390x/op_helper.c
index 7f0adcb..9153940 100644
--- a/target-s390x/op_helper.c
+++ b/target-s390x/op_helper.c
@@ -1,6 +1,7 @@
/*
* S/390 helper routines
*
+ * Copyright (c) 2009 Ulrich Hecht
* Copyright (c) 2009 Alexander Graf
*
* This library is free software; you can redistribute it and/or
@@ -18,6 +19,11 @@
*/
#include "exec.h"
+#include "host-utils.h"
+#include "helpers.h"
+#include <string.h>
+#include "kvm.h"
+#include "qemu-timer.h"
/*****************************************************************************/
/* Softmmu support */
@@ -64,16 +70,2925 @@ void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
cpu_restore_state(tb, env, pc);
}
}
- /* XXX */
- /* helper_raise_exception_err(env->exception_index, env->error_code); */
+ cpu_loop_exit();
}
env = saved_env;
}
#endif
+/* #define DEBUG_HELPER */
+#ifdef DEBUG_HELPER
+#define HELPER_LOG(x...) qemu_log(x)
+#else
+#define HELPER_LOG(x...)
+#endif
+
+/* raise an exception */
+void HELPER(exception)(uint32_t excp)
+{
+ HELPER_LOG("%s: exception %d\n", __FUNCTION__, excp);
+ env->exception_index = excp;
+ cpu_loop_exit();
+}
+
+#ifndef CONFIG_USER_ONLY
+static void mvc_fast_memset(CPUState *env, uint32_t l, uint64_t dest,
+ uint8_t byte)
+{
+ target_phys_addr_t dest_phys;
+ target_phys_addr_t len = l;
+ void *dest_p;
+ uint64_t asc = env->psw.mask & PSW_MASK_ASC;
+ int flags;
+
+ if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) {
+ stb(dest, byte);
+ cpu_abort(env, "should never reach here");
+ }
+ dest_phys |= dest & ~TARGET_PAGE_MASK;
+
+ dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
+
+ memset(dest_p, byte, len);
+
+ cpu_physical_memory_unmap(dest_p, 1, len, len);
+}
+
+static void mvc_fast_memmove(CPUState *env, uint32_t l, uint64_t dest,
+ uint64_t src)
+{
+ target_phys_addr_t dest_phys;
+ target_phys_addr_t src_phys;
+ target_phys_addr_t len = l;
+ void *dest_p;
+ void *src_p;
+ uint64_t asc = env->psw.mask & PSW_MASK_ASC;
+ int flags;
+
+ if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) {
+ stb(dest, 0);
+ cpu_abort(env, "should never reach here");
+ }
+ dest_phys |= dest & ~TARGET_PAGE_MASK;
+
+ if (mmu_translate(env, src, 0, asc, &src_phys, &flags)) {
+ ldub(src);
+ cpu_abort(env, "should never reach here");
+ }
+ src_phys |= src & ~TARGET_PAGE_MASK;
+
+ dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
+ src_p = cpu_physical_memory_map(src_phys, &len, 0);
+
+ memmove(dest_p, src_p, len);
+
+ cpu_physical_memory_unmap(dest_p, 1, len, len);
+ cpu_physical_memory_unmap(src_p, 0, len, len);
+}
+#endif
+
+/* and on array */
+uint32_t HELPER(nc)(uint32_t l, uint64_t dest, uint64_t src)
+{
+ int i;
+ unsigned char x;
+ uint32_t cc = 0;
+
+ HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
+ __FUNCTION__, l, dest, src);
+ for (i = 0; i <= l; i++) {
+ x = ldub(dest + i) & ldub(src + i);
+ if (x) {
+ cc = 1;
+ }
+ stb(dest + i, x);
+ }
+ return cc;
+}
+
+/* xor on array */
+uint32_t HELPER(xc)(uint32_t l, uint64_t dest, uint64_t src)
+{
+ int i;
+ unsigned char x;
+ uint32_t cc = 0;
+
+ HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
+ __FUNCTION__, l, dest, src);
+
+#ifndef CONFIG_USER_ONLY
+ /* xor with itself is the same as memset(0) */
+ if ((l > 32) && (src == dest) &&
+ (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK)) {
+ mvc_fast_memset(env, l + 1, dest, 0);
+ return 0;
+ }
+#else
+ if (src == dest) {
+ memset(g2h(dest), 0, l + 1);
+ return 0;
+ }
+#endif
+
+ for (i = 0; i <= l; i++) {
+ x = ldub(dest + i) ^ ldub(src + i);
+ if (x) {
+ cc = 1;
+ }
+ stb(dest + i, x);
+ }
+ return cc;
+}
+
+/* or on array */
+uint32_t HELPER(oc)(uint32_t l, uint64_t dest, uint64_t src)
+{
+ int i;
+ unsigned char x;
+ uint32_t cc = 0;
+
+ HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
+ __FUNCTION__, l, dest, src);
+ for (i = 0; i <= l; i++) {
+ x = ldub(dest + i) | ldub(src + i);
+ if (x) {
+ cc = 1;
+ }
+ stb(dest + i, x);
+ }
+ return cc;
+}
+
+/* memmove */
+void HELPER(mvc)(uint32_t l, uint64_t dest, uint64_t src)
+{
+ int i = 0;
+ int x = 0;
+ uint32_t l_64 = (l + 1) / 8;
+
+ HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
+ __FUNCTION__, l, dest, src);
+
+#ifndef CONFIG_USER_ONLY
+ if ((l > 32) &&
+ (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK) &&
+ (dest & TARGET_PAGE_MASK) == ((dest + l) & TARGET_PAGE_MASK)) {
+ if (dest == (src + 1)) {
+ mvc_fast_memset(env, l + 1, dest, ldub(src));
+ return;
+ } else if ((src & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) {
+ mvc_fast_memmove(env, l + 1, dest, src);
+ return;
+ }
+ }
+#else
+ if (dest == (src + 1)) {
+ memset(g2h(dest), ldub(src), l + 1);
+ return;
+ } else {
+ memmove(g2h(dest), g2h(src), l + 1);
+ return;
+ }
+#endif
+
+ /* handle the parts that fit into 8-byte loads/stores */
+ if (dest != (src + 1)) {
+ for (i = 0; i < l_64; i++) {
+ stq(dest + x, ldq(src + x));
+ x += 8;
+ }
+ }
+
+ /* slow version crossing pages with byte accesses */
+ for (i = x; i <= l; i++) {
+ stb(dest + i, ldub(src + i));
+ }
+}
+
+/* compare unsigned byte arrays */
+uint32_t HELPER(clc)(uint32_t l, uint64_t s1, uint64_t s2)
+{
+ int i;
+ unsigned char x,y;
+ uint32_t cc;
+ HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n",
+ __FUNCTION__, l, s1, s2);
+ for (i = 0; i <= l; i++) {
+ x = ldub(s1 + i);
+ y = ldub(s2 + i);
+ HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y);
+ if (x < y) {
+ cc = 1;
+ goto done;
+ } else if (x > y) {
+ cc = 2;
+ goto done;
+ }
+ }
+ cc = 0;
+done:
+ HELPER_LOG("\n");
+ return cc;
+}
+
+/* compare logical under mask */
+uint32_t HELPER(clm)(uint32_t r1, uint32_t mask, uint64_t addr)
+{
+ uint8_t r,d;
+ uint32_t cc;
+ HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __FUNCTION__, r1,
+ mask, addr);
+ cc = 0;
+ while (mask) {
+ if (mask & 8) {
+ d = ldub(addr);
+ r = (r1 & 0xff000000UL) >> 24;
+ HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d,
+ addr);
+ if (r < d) {
+ cc = 1;
+ break;
+ } else if (r > d) {
+ cc = 2;
+ break;
+ }
+ addr++;
+ }
+ mask = (mask << 1) & 0xf;
+ r1 <<= 8;
+ }
+ HELPER_LOG("\n");
+ return cc;
+}
+
+/* store character under mask */
+void HELPER(stcm)(uint32_t r1, uint32_t mask, uint64_t addr)
+{
+ uint8_t r;
+ HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%lx\n", __FUNCTION__, r1, mask,
+ addr);
+ while (mask) {
+ if (mask & 8) {
+ r = (r1 & 0xff000000UL) >> 24;
+ stb(addr, r);
+ HELPER_LOG("mask 0x%x %02x (0x%lx) ", mask, r, addr);
+ addr++;
+ }
+ mask = (mask << 1) & 0xf;
+ r1 <<= 8;
+ }
+ HELPER_LOG("\n");
+}
+
+/* 64/64 -> 128 unsigned multiplication */
+void HELPER(mlg)(uint32_t r1, uint64_t v2)
+{
+#if HOST_LONG_BITS == 64 && defined(__GNUC__)
+ /* assuming 64-bit hosts have __uint128_t */
+ __uint128_t res = (__uint128_t)env->regs[r1 + 1];
+ res *= (__uint128_t)v2;
+ env->regs[r1] = (uint64_t)(res >> 64);
+ env->regs[r1 + 1] = (uint64_t)res;
+#else
+ mulu64(&env->regs[r1 + 1], &env->regs[r1], env->regs[r1 + 1], v2);
+#endif
+}
+
+/* 128 -> 64/64 unsigned division */
+void HELPER(dlg)(uint32_t r1, uint64_t v2)
+{
+ uint64_t divisor = v2;
+
+ if (!env->regs[r1]) {
+ /* 64 -> 64/64 case */
+ env->regs[r1] = env->regs[r1+1] % divisor;
+ env->regs[r1+1] = env->regs[r1+1] / divisor;
+ return;
+ } else {
+
+#if HOST_LONG_BITS == 64 && defined(__GNUC__)
+ /* assuming 64-bit hosts have __uint128_t */
+ __uint128_t dividend = (((__uint128_t)env->regs[r1]) << 64) |
+ (env->regs[r1+1]);
+ __uint128_t quotient = dividend / divisor;
+ env->regs[r1+1] = quotient;
+ __uint128_t remainder = dividend % divisor;
+ env->regs[r1] = remainder;
+#else
+ /* 32-bit hosts would need special wrapper functionality - just abort if
+ we encounter such a case; it's very unlikely anyways. */
+ cpu_abort(env, "128 -> 64/64 division not implemented\n");
+#endif
+ }
+}
+
+static inline uint64_t get_address(int x2, int b2, int d2)
+{
+ uint64_t r = d2;
+
+ if (x2) {
+ r += env->regs[x2];
+ }
+
+ if (b2) {
+ r += env->regs[b2];
+ }
+
+ /* 31-Bit mode */
+ if (!(env->psw.mask & PSW_MASK_64)) {
+ r &= 0x7fffffff;
+ }
+
+ return r;
+}
+
+static inline uint64_t get_address_31fix(int reg)
+{
+ uint64_t r = env->regs[reg];
+
+ /* 31-Bit mode */
+ if (!(env->psw.mask & PSW_MASK_64)) {
+ r &= 0x7fffffff;
+ }
+
+ return r;
+}
+
+/* search string (c is byte to search, r2 is string, r1 end of string) */
+uint32_t HELPER(srst)(uint32_t c, uint32_t r1, uint32_t r2)
+{
+ uint64_t i;
+ uint32_t cc = 2;
+ uint64_t str = get_address_31fix(r2);
+ uint64_t end = get_address_31fix(r1);
+
+ HELPER_LOG("%s: c %d *r1 0x%" PRIx64 " *r2 0x%" PRIx64 "\n", __FUNCTION__,
+ c, env->regs[r1], env->regs[r2]);
+
+ for (i = str; i != end; i++) {
+ if (ldub(i) == c) {
+ env->regs[r1] = i;
+ cc = 1;
+ break;
+ }
+ }
+
+ return cc;
+}
+
+/* unsigned string compare (c is string terminator) */
+uint32_t HELPER(clst)(uint32_t c, uint32_t r1, uint32_t r2)
+{
+ uint64_t s1 = get_address_31fix(r1);
+ uint64_t s2 = get_address_31fix(r2);
+ uint8_t v1, v2;
+ uint32_t cc;
+ c = c & 0xff;
+#ifdef CONFIG_USER_ONLY
+ if (!c) {
+ HELPER_LOG("%s: comparing '%s' and '%s'\n",
+ __FUNCTION__, (char*)g2h(s1), (char*)g2h(s2));
+ }
+#endif
+ for (;;) {
+ v1 = ldub(s1);
+ v2 = ldub(s2);
+ if ((v1 == c || v2 == c) || (v1 != v2)) {
+ break;
+ }
+ s1++;
+ s2++;
+ }
+
+ if (v1 == v2) {
+ cc = 0;
+ } else {
+ cc = (v1 < v2) ? 1 : 2;
+ /* FIXME: 31-bit mode! */
+ env->regs[r1] = s1;
+ env->regs[r2] = s2;
+ }
+ return cc;
+}
+
+/* move page */
+void HELPER(mvpg)(uint64_t r0, uint64_t r1, uint64_t r2)
+{
+ /* XXX missing r0 handling */
+#ifdef CONFIG_USER_ONLY
+ int i;
+
+ for (i = 0; i < TARGET_PAGE_SIZE; i++) {
+ stb(r1 + i, ldub(r2 + i));
+ }
+#else
+ mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2);
+#endif
+}
+
+/* string copy (c is string terminator) */
+void HELPER(mvst)(uint32_t c, uint32_t r1, uint32_t r2)
+{
+ uint64_t dest = get_address_31fix(r1);
+ uint64_t src = get_address_31fix(r2);
+ uint8_t v;
+ c = c & 0xff;
+#ifdef CONFIG_USER_ONLY
+ if (!c) {
+ HELPER_LOG("%s: copy '%s' to 0x%lx\n", __FUNCTION__, (char*)g2h(src),
+ dest);
+ }
+#endif
+ for (;;) {
+ v = ldub(src);
+ stb(dest, v);
+ if (v == c) {
+ break;
+ }
+ src++;
+ dest++;
+ }
+ env->regs[r1] = dest; /* FIXME: 31-bit mode! */
+}
+
+/* compare and swap 64-bit */
+uint32_t HELPER(csg)(uint32_t r1, uint64_t a2, uint32_t r3)
+{
+ /* FIXME: locking? */
+ uint32_t cc;
+ uint64_t v2 = ldq(a2);
+ if (env->regs[r1] == v2) {
+ cc = 0;
+ stq(a2, env->regs[r3]);
+ } else {
+ cc = 1;
+ env->regs[r1] = v2;
+ }
+ return cc;
+}
+
+/* compare double and swap 64-bit */
+uint32_t HELPER(cdsg)(uint32_t r1, uint64_t a2, uint32_t r3)
+{
+ /* FIXME: locking? */
+ uint32_t cc;
+ uint64_t v2_hi = ldq(a2);
+ uint64_t v2_lo = ldq(a2 + 8);
+ uint64_t v1_hi = env->regs[r1];
+ uint64_t v1_lo = env->regs[r1 + 1];
+
+ if ((v1_hi == v2_hi) && (v1_lo == v2_lo)) {
+ cc = 0;
+ stq(a2, env->regs[r3]);
+ stq(a2 + 8, env->regs[r3 + 1]);
+ } else {
+ cc = 1;
+ env->regs[r1] = v2_hi;
+ env->regs[r1 + 1] = v2_lo;
+ }
+
+ return cc;
+}
+
+/* compare and swap 32-bit */
+uint32_t HELPER(cs)(uint32_t r1, uint64_t a2, uint32_t r3)
+{
+ /* FIXME: locking? */
+ uint32_t cc;
+ HELPER_LOG("%s: r1 %d a2 0x%lx r3 %d\n", __FUNCTION__, r1, a2, r3);
+ uint32_t v2 = ldl(a2);
+ if (((uint32_t)env->regs[r1]) == v2) {
+ cc = 0;
+ stl(a2, (uint32_t)env->regs[r3]);
+ } else {
+ cc = 1;
+ env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | v2;
+ }
+ return cc;
+}
+
+static uint32_t helper_icm(uint32_t r1, uint64_t address, uint32_t mask)
+{
+ int pos = 24; /* top of the lower half of r1 */
+ uint64_t rmask = 0xff000000ULL;
+ uint8_t val = 0;
+ int ccd = 0;
+ uint32_t cc = 0;
+
+ while (mask) {
+ if (mask & 8) {
+ env->regs[r1] &= ~rmask;
+ val = ldub(address);
+ if ((val & 0x80) && !ccd) {
+ cc = 1;
+ }
+ ccd = 1;
+ if (val && cc == 0) {
+ cc = 2;
+ }
+ env->regs[r1] |= (uint64_t)val << pos;
+ address++;
+ }
+ mask = (mask << 1) & 0xf;
+ pos -= 8;
+ rmask >>= 8;
+ }
+
+ return cc;
+}
+
+/* execute instruction
+ this instruction executes an insn modified with the contents of r1
+ it does not change the executed instruction in memory
+ it does not change the program counter
+ in other words: tricky...
+ currently implemented by interpreting the cases it is most commonly used in
+ */
+uint32_t HELPER(ex)(uint32_t cc, uint64_t v1, uint64_t addr, uint64_t ret)
+{
+ uint16_t insn = lduw_code(addr);
+ HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __FUNCTION__, v1, addr,
+ insn);
+ if ((insn & 0xf0ff) == 0xd000) {
+ uint32_t l, insn2, b1, b2, d1, d2;
+ l = v1 & 0xff;
+ insn2 = ldl_code(addr + 2);
+ b1 = (insn2 >> 28) & 0xf;
+ b2 = (insn2 >> 12) & 0xf;
+ d1 = (insn2 >> 16) & 0xfff;
+ d2 = insn2 & 0xfff;
+ switch (insn & 0xf00) {
+ case 0x200:
+ helper_mvc(l, get_address(0, b1, d1), get_address(0, b2, d2));
+ break;
+ case 0x500:
+ cc = helper_clc(l, get_address(0, b1, d1), get_address(0, b2, d2));
+ break;
+ case 0x700:
+ cc = helper_xc(l, get_address(0, b1, d1), get_address(0, b2, d2));
+ break;
+ default:
+ goto abort;
+ break;
+ }
+ } else if ((insn & 0xff00) == 0x0a00) {
+ /* supervisor call */
+ HELPER_LOG("%s: svc %ld via execute\n", __FUNCTION__, (insn|v1) & 0xff);
+ env->psw.addr = ret - 4;
+ env->int_svc_code = (insn|v1) & 0xff;
+ env->int_svc_ilc = 4;
+ helper_exception(EXCP_SVC);
+ } else if ((insn & 0xff00) == 0xbf00) {
+ uint32_t insn2, r1, r3, b2, d2;
+ insn2 = ldl_code(addr + 2);
+ r1 = (insn2 >> 20) & 0xf;
+ r3 = (insn2 >> 16) & 0xf;
+ b2 = (insn2 >> 12) & 0xf;
+ d2 = insn2 & 0xfff;
+ cc = helper_icm(r1, get_address(0, b2, d2), r3);
+ } else {
+abort:
+ cpu_abort(env, "EXECUTE on instruction prefix 0x%x not implemented\n",
+ insn);
+ }
+ return cc;
+}
+
+/* absolute value 32-bit */
+uint32_t HELPER(abs_i32)(int32_t val)
+{
+ if (val < 0) {
+ return -val;
+ } else {
+ return val;
+ }
+}
+
+/* negative absolute value 32-bit */
+int32_t HELPER(nabs_i32)(int32_t val)
+{
+ if (val < 0) {
+ return val;
+ } else {
+ return -val;
+ }
+}
+
+/* absolute value 64-bit */
+uint64_t HELPER(abs_i64)(int64_t val)
+{
+ HELPER_LOG("%s: val 0x%" PRIx64 "\n", __FUNCTION__, val);
+
+ if (val < 0) {
+ return -val;
+ } else {
+ return val;
+ }
+}
+
+/* negative absolute value 64-bit */
+int64_t HELPER(nabs_i64)(int64_t val)
+{
+ if (val < 0) {
+ return val;
+ } else {
+ return -val;
+ }
+}
+
+/* add with carry 32-bit unsigned */
+uint32_t HELPER(addc_u32)(uint32_t cc, uint32_t v1, uint32_t v2)
+{
+ uint32_t res;
+
+ res = v1 + v2;
+ if (cc & 2) {
+ res++;
+ }
+
+ return res;
+}
+
+/* store character under mask high operates on the upper half of r1 */
+void HELPER(stcmh)(uint32_t r1, uint64_t address, uint32_t mask)
+{
+ int pos = 56; /* top of the upper half of r1 */
+
+ while (mask) {
+ if (mask & 8) {
+ stb(address, (env->regs[r1] >> pos) & 0xff);
+ address++;
+ }
+ mask = (mask << 1) & 0xf;
+ pos -= 8;
+ }
+}
+
+/* insert character under mask high; same as icm, but operates on the
+ upper half of r1 */
+uint32_t HELPER(icmh)(uint32_t r1, uint64_t address, uint32_t mask)
+{
+ int pos = 56; /* top of the upper half of r1 */
+ uint64_t rmask = 0xff00000000000000ULL;
+ uint8_t val = 0;
+ int ccd = 0;
+ uint32_t cc = 0;
+
+ while (mask) {
+ if (mask & 8) {
+ env->regs[r1] &= ~rmask;
+ val = ldub(address);
+ if ((val & 0x80) && !ccd) {
+ cc = 1;
+ }
+ ccd = 1;
+ if (val && cc == 0) {
+ cc = 2;
+ }
+ env->regs[r1] |= (uint64_t)val << pos;
+ address++;
+ }
+ mask = (mask << 1) & 0xf;
+ pos -= 8;
+ rmask >>= 8;
+ }
+
+ return cc;
+}
+
+/* insert psw mask and condition code into r1 */
+void HELPER(ipm)(uint32_t cc, uint32_t r1)
+{
+ uint64_t r = env->regs[r1];
+
+ r &= 0xffffffff00ffffffULL;
+ r |= (cc << 28) | ( (env->psw.mask >> 40) & 0xf );
+ env->regs[r1] = r;
+ HELPER_LOG("%s: cc %d psw.mask 0x%lx r1 0x%lx\n", __FUNCTION__,
+ cc, env->psw.mask, r);
+}
+
+/* load access registers r1 to r3 from memory at a2 */
+void HELPER(lam)(uint32_t r1, uint64_t a2, uint32_t r3)
+{
+ int i;
+
+ for (i = r1;; i = (i + 1) % 16) {
+ env->aregs[i] = ldl(a2);
+ a2 += 4;
+
+ if (i == r3) {
+ break;
+ }
+ }
+}
+
+/* store access registers r1 to r3 in memory at a2 */
+void HELPER(stam)(uint32_t r1, uint64_t a2, uint32_t r3)
+{
+ int i;
+
+ for (i = r1;; i = (i + 1) % 16) {
+ stl(a2, env->aregs[i]);
+ a2 += 4;
+
+ if (i == r3) {
+ break;
+ }
+ }
+}
+
+/* move long */
+uint32_t HELPER(mvcl)(uint32_t r1, uint32_t r2)
+{
+ uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
+ uint64_t dest = get_address_31fix(r1);
+ uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
+ uint64_t src = get_address_31fix(r2);
+ uint8_t pad = src >> 24;
+ uint8_t v;
+ uint32_t cc;
+
+ if (destlen == srclen) {
+ cc = 0;
+ } else if (destlen < srclen) {
+ cc = 1;
+ } else {
+ cc = 2;
+ }
+
+ if (srclen > destlen) {
+ srclen = destlen;
+ }
+
+ for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
+ v = ldub(src);
+ stb(dest, v);
+ }
+
+ for (; destlen; dest++, destlen--) {
+ stb(dest, pad);
+ }
+
+ env->regs[r1 + 1] = destlen;
+ /* can't use srclen here, we trunc'ed it */
+ env->regs[r2 + 1] -= src - env->regs[r2];
+ env->regs[r1] = dest;
+ env->regs[r2] = src;
+
+ return cc;
+}
+
+/* move long extended another memcopy insn with more bells and whistles */
+uint32_t HELPER(mvcle)(uint32_t r1, uint64_t a2, uint32_t r3)
+{
+ uint64_t destlen = env->regs[r1 + 1];
+ uint64_t dest = env->regs[r1];
+ uint64_t srclen = env->regs[r3 + 1];
+ uint64_t src = env->regs[r3];
+ uint8_t pad = a2 & 0xff;
+ uint8_t v;
+ uint32_t cc;
+
+ if (!(env->psw.mask & PSW_MASK_64)) {
+ destlen = (uint32_t)destlen;
+ srclen = (uint32_t)srclen;
+ dest &= 0x7fffffff;
+ src &= 0x7fffffff;
+ }
+
+ if (destlen == srclen) {
+ cc = 0;
+ } else if (destlen < srclen) {
+ cc = 1;
+ } else {
+ cc = 2;
+ }
+
+ if (srclen > destlen) {
+ srclen = destlen;
+ }
+
+ for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
+ v = ldub(src);
+ stb(dest, v);
+ }
+
+ for (; destlen; dest++, destlen--) {
+ stb(dest, pad);
+ }
+
+ env->regs[r1 + 1] = destlen;
+ /* can't use srclen here, we trunc'ed it */
+ /* FIXME: 31-bit mode! */
+ env->regs[r3 + 1] -= src - env->regs[r3];
+ env->regs[r1] = dest;
+ env->regs[r3] = src;
+
+ return cc;
+}
+
+/* compare logical long extended memcompare insn with padding */
+uint32_t HELPER(clcle)(uint32_t r1, uint64_t a2, uint32_t r3)
+{
+ uint64_t destlen = env->regs[r1 + 1];
+ uint64_t dest = get_address_31fix(r1);
+ uint64_t srclen = env->regs[r3 + 1];
+ uint64_t src = get_address_31fix(r3);
+ uint8_t pad = a2 & 0xff;
+ uint8_t v1 = 0,v2 = 0;
+ uint32_t cc = 0;
+
+ if (!(destlen || srclen)) {
+ return cc;
+ }
+
+ if (srclen > destlen) {
+ srclen = destlen;
+ }
+
+ for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
+ v1 = srclen ? ldub(src) : pad;
+ v2 = destlen ? ldub(dest) : pad;
+ if (v1 != v2) {
+ cc = (v1 < v2) ? 1 : 2;
+ break;
+ }
+ }
+
+ env->regs[r1 + 1] = destlen;
+ /* can't use srclen here, we trunc'ed it */
+ env->regs[r3 + 1] -= src - env->regs[r3];
+ env->regs[r1] = dest;
+ env->regs[r3] = src;
+
+ return cc;
+}
+
+/* subtract unsigned v2 from v1 with borrow */
+uint32_t HELPER(slb)(uint32_t cc, uint32_t r1, uint32_t v2)
+{
+ uint32_t v1 = env->regs[r1];
+ uint32_t res = v1 + (~v2) + (cc >> 1);
+
+ env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | res;
+ if (cc & 2) {
+ /* borrow */
+ return v1 ? 1 : 0;
+ } else {
+ return v1 ? 3 : 2;
+ }
+}
+
+/* subtract unsigned v2 from v1 with borrow */
+uint32_t HELPER(slbg)(uint32_t cc, uint32_t r1, uint64_t v1, uint64_t v2)
+{
+ uint64_t res = v1 + (~v2) + (cc >> 1);
+
+ env->regs[r1] = res;
+ if (cc & 2) {
+ /* borrow */
+ return v1 ? 1 : 0;
+ } else {
+ return v1 ? 3 : 2;
+ }
+}
+
+static inline int float_comp_to_cc(int float_compare)
+{
+ switch (float_compare) {
+ case float_relation_equal:
+ return 0;
+ case float_relation_less:
+ return 1;
+ case float_relation_greater:
+ return 2;
+ case float_relation_unordered:
+ return 3;
+ default:
+ cpu_abort(env, "unknown return value for float compare\n");
+ }
+}
+
+/* condition codes for binary FP ops */
+static uint32_t set_cc_f32(float32 v1, float32 v2)
+{
+ return float_comp_to_cc(float32_compare_quiet(v1, v2, &env->fpu_status));
+}
+
+static uint32_t set_cc_f64(float64 v1, float64 v2)
+{
+ return float_comp_to_cc(float64_compare_quiet(v1, v2, &env->fpu_status));
+}
+
+/* condition codes for unary FP ops */
+static uint32_t set_cc_nz_f32(float32 v)
+{
+ if (float32_is_any_nan(v)) {
+ return 3;
+ } else if (float32_is_zero(v)) {
+ return 0;
+ } else if (float32_is_neg(v)) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+
+static uint32_t set_cc_nz_f64(float64 v)
+{
+ if (float64_is_any_nan(v)) {
+ return 3;
+ } else if (float64_is_zero(v)) {
+ return 0;
+ } else if (float64_is_neg(v)) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+
+static uint32_t set_cc_nz_f128(float128 v)
+{
+ if (float128_is_any_nan(v)) {
+ return 3;
+ } else if (float128_is_zero(v)) {
+ return 0;
+ } else if (float128_is_neg(v)) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+
+/* convert 32-bit int to 64-bit float */
+void HELPER(cdfbr)(uint32_t f1, int32_t v2)
+{
+ HELPER_LOG("%s: converting %d to f%d\n", __FUNCTION__, v2, f1);
+ env->fregs[f1].d = int32_to_float64(v2, &env->fpu_status);
+}
+
+/* convert 32-bit int to 128-bit float */
+void HELPER(cxfbr)(uint32_t f1, int32_t v2)
+{
+ CPU_QuadU v1;
+ v1.q = int32_to_float128(v2, &env->fpu_status);
+ env->fregs[f1].ll = v1.ll.upper;
+ env->fregs[f1 + 2].ll = v1.ll.lower;
+}
+
+/* convert 64-bit int to 32-bit float */
+void HELPER(cegbr)(uint32_t f1, int64_t v2)
+{
+ HELPER_LOG("%s: converting %ld to f%d\n", __FUNCTION__, v2, f1);
+ env->fregs[f1].l.upper = int64_to_float32(v2, &env->fpu_status);
+}
+
+/* convert 64-bit int to 64-bit float */
+void HELPER(cdgbr)(uint32_t f1, int64_t v2)
+{
+ HELPER_LOG("%s: converting %ld to f%d\n", __FUNCTION__, v2, f1);
+ env->fregs[f1].d = int64_to_float64(v2, &env->fpu_status);
+}
+
+/* convert 64-bit int to 128-bit float */
+void HELPER(cxgbr)(uint32_t f1, int64_t v2)
+{
+ CPU_QuadU x1;
+ x1.q = int64_to_float128(v2, &env->fpu_status);
+ HELPER_LOG("%s: converted %ld to 0x%lx and 0x%lx\n", __FUNCTION__, v2,
+ x1.ll.upper, x1.ll.lower);
+ env->fregs[f1].ll = x1.ll.upper;
+ env->fregs[f1 + 2].ll = x1.ll.lower;
+}
+
+/* convert 32-bit int to 32-bit float */
+void HELPER(cefbr)(uint32_t f1, int32_t v2)
+{
+ env->fregs[f1].l.upper = int32_to_float32(v2, &env->fpu_status);
+ HELPER_LOG("%s: converting %d to 0x%d in f%d\n", __FUNCTION__, v2,
+ env->fregs[f1].l.upper, f1);
+}
+
+/* 32-bit FP addition RR */
+uint32_t HELPER(aebr)(uint32_t f1, uint32_t f2)
+{
+ env->fregs[f1].l.upper = float32_add(env->fregs[f1].l.upper,
+ env->fregs[f2].l.upper,
+ &env->fpu_status);
+ HELPER_LOG("%s: adding 0x%d resulting in 0x%d in f%d\n", __FUNCTION__,
+ env->fregs[f2].l.upper, env->fregs[f1].l.upper, f1);
+
+ return set_cc_nz_f32(env->fregs[f1].l.upper);
+}
+
+/* 64-bit FP addition RR */
+uint32_t HELPER(adbr)(uint32_t f1, uint32_t f2)
+{
+ env->fregs[f1].d = float64_add(env->fregs[f1].d, env->fregs[f2].d,
+ &env->fpu_status);
+ HELPER_LOG("%s: adding 0x%ld resulting in 0x%ld in f%d\n", __FUNCTION__,
+ env->fregs[f2].d, env->fregs[f1].d, f1);
+
+ return set_cc_nz_f64(env->fregs[f1].d);
+}
+
+/* 32-bit FP subtraction RR */
+uint32_t HELPER(sebr)(uint32_t f1, uint32_t f2)
+{
+ env->fregs[f1].l.upper = float32_sub(env->fregs[f1].l.upper,
+ env->fregs[f2].l.upper,
+ &env->fpu_status);
+ HELPER_LOG("%s: adding 0x%d resulting in 0x%d in f%d\n", __FUNCTION__,
+ env->fregs[f2].l.upper, env->fregs[f1].l.upper, f1);
+
+ return set_cc_nz_f32(env->fregs[f1].l.upper);
+}
+
+/* 64-bit FP subtraction RR */
+uint32_t HELPER(sdbr)(uint32_t f1, uint32_t f2)
+{
+ env->fregs[f1].d = float64_sub(env->fregs[f1].d, env->fregs[f2].d,
+ &env->fpu_status);
+ HELPER_LOG("%s: subtracting 0x%ld resulting in 0x%ld in f%d\n",
+ __FUNCTION__, env->fregs[f2].d, env->fregs[f1].d, f1);
+
+ return set_cc_nz_f64(env->fregs[f1].d);
+}
+
+/* 32-bit FP division RR */
+void HELPER(debr)(uint32_t f1, uint32_t f2)
+{
+ env->fregs[f1].l.upper = float32_div(env->fregs[f1].l.upper,
+ env->fregs[f2].l.upper,
+ &env->fpu_status);
+}
+
+/* 128-bit FP division RR */
+void HELPER(dxbr)(uint32_t f1, uint32_t f2)
+{
+ CPU_QuadU v1;
+ v1.ll.upper = env->fregs[f1].ll;
+ v1.ll.lower = env->fregs[f1 + 2].ll;
+ CPU_QuadU v2;
+ v2.ll.upper = env->fregs[f2].ll;
+ v2.ll.lower = env->fregs[f2 + 2].ll;
+ CPU_QuadU res;
+ res.q = float128_div(v1.q, v2.q, &env->fpu_status);
+ env->fregs[f1].ll = res.ll.upper;
+ env->fregs[f1 + 2].ll = res.ll.lower;
+}
+
+/* 64-bit FP multiplication RR */
+void HELPER(mdbr)(uint32_t f1, uint32_t f2)
+{
+ env->fregs[f1].d = float64_mul(env->fregs[f1].d, env->fregs[f2].d,
+ &env->fpu_status);
+}
+
+/* 128-bit FP multiplication RR */
+void HELPER(mxbr)(uint32_t f1, uint32_t f2)
+{
+ CPU_QuadU v1;
+ v1.ll.upper = env->fregs[f1].ll;
+ v1.ll.lower = env->fregs[f1 + 2].ll;
+ CPU_QuadU v2;
+ v2.ll.upper = env->fregs[f2].ll;
+ v2.ll.lower = env->fregs[f2 + 2].ll;
+ CPU_QuadU res;
+ res.q = float128_mul(v1.q, v2.q, &env->fpu_status);
+ env->fregs[f1].ll = res.ll.upper;
+ env->fregs[f1 + 2].ll = res.ll.lower;
+}
+
+/* convert 32-bit float to 64-bit float */
+void HELPER(ldebr)(uint32_t r1, uint32_t r2)
+{
+ env->fregs[r1].d = float32_to_float64(env->fregs[r2].l.upper,
+ &env->fpu_status);
+}
+
+/* convert 128-bit float to 64-bit float */
+void HELPER(ldxbr)(uint32_t f1, uint32_t f2)
+{
+ CPU_QuadU x2;
+ x2.ll.upper = env->fregs[f2].ll;
+ x2.ll.lower = env->fregs[f2 + 2].ll;
+ env->fregs[f1].d = float128_to_float64(x2.q, &env->fpu_status);
+ HELPER_LOG("%s: to 0x%ld\n", __FUNCTION__, env->fregs[f1].d);
+}
+
+/* convert 64-bit float to 128-bit float */
+void HELPER(lxdbr)(uint32_t f1, uint32_t f2)
+{
+ CPU_QuadU res;
+ res.q = float64_to_float128(env->fregs[f2].d, &env->fpu_status);
+ env->fregs[f1].ll = res.ll.upper;
+ env->fregs[f1 + 2].ll = res.ll.lower;
+}
+
+/* convert 64-bit float to 32-bit float */
+void HELPER(ledbr)(uint32_t f1, uint32_t f2)
+{
+ float64 d2 = env->fregs[f2].d;
+ env->fregs[f1].l.upper = float64_to_float32(d2, &env->fpu_status);
+}
+
+/* convert 128-bit float to 32-bit float */
+void HELPER(lexbr)(uint32_t f1, uint32_t f2)
+{
+ CPU_QuadU x2;
+ x2.ll.upper = env->fregs[f2].ll;
+ x2.ll.lower = env->fregs[f2 + 2].ll;
+ env->fregs[f1].l.upper = float128_to_float32(x2.q, &env->fpu_status);
+ HELPER_LOG("%s: to 0x%d\n", __FUNCTION__, env->fregs[f1].l.upper);
+}
+
+/* absolute value of 32-bit float */
+uint32_t HELPER(lpebr)(uint32_t f1, uint32_t f2)
+{
+ float32 v1;
+ float32 v2 = env->fregs[f2].d;
+ v1 = float32_abs(v2);
+ env->fregs[f1].d = v1;
+ return set_cc_nz_f32(v1);
+}
+
+/* absolute value of 64-bit float */
+uint32_t HELPER(lpdbr)(uint32_t f1, uint32_t f2)
+{
+ float64 v1;
+ float64 v2 = env->fregs[f2].d;
+ v1 = float64_abs(v2);
+ env->fregs[f1].d = v1;
+ return set_cc_nz_f64(v1);
+}
+
+/* absolute value of 128-bit float */
+uint32_t HELPER(lpxbr)(uint32_t f1, uint32_t f2)
+{
+ CPU_QuadU v1;
+ CPU_QuadU v2;
+ v2.ll.upper = env->fregs[f2].ll;
+ v2.ll.lower = env->fregs[f2 + 2].ll;
+ v1.q = float128_abs(v2.q);
+ env->fregs[f1].ll = v1.ll.upper;
+ env->fregs[f1 + 2].ll = v1.ll.lower;
+ return set_cc_nz_f128(v1.q);
+}
+
+/* load and test 64-bit float */
+uint32_t HELPER(ltdbr)(uint32_t f1, uint32_t f2)
+{
+ env->fregs[f1].d = env->fregs[f2].d;
+ return set_cc_nz_f64(env->fregs[f1].d);
+}
+
+/* load and test 32-bit float */
+uint32_t HELPER(ltebr)(uint32_t f1, uint32_t f2)
+{
+ env->fregs[f1].l.upper = env->fregs[f2].l.upper;
+ return set_cc_nz_f32(env->fregs[f1].l.upper);
+}
+
+/* load and test 128-bit float */
+uint32_t HELPER(ltxbr)(uint32_t f1, uint32_t f2)
+{
+ CPU_QuadU x;
+ x.ll.upper = env->fregs[f2].ll;
+ x.ll.lower = env->fregs[f2 + 2].ll;
+ env->fregs[f1].ll = x.ll.upper;
+ env->fregs[f1 + 2].ll = x.ll.lower;
+ return set_cc_nz_f128(x.q);
+}
+
+/* load complement of 32-bit float */
+uint32_t HELPER(lcebr)(uint32_t f1, uint32_t f2)
+{
+ env->fregs[f1].l.upper = float32_chs(env->fregs[f2].l.upper);
+
+ return set_cc_nz_f32(env->fregs[f1].l.upper);
+}
+
+/* load complement of 64-bit float */
+uint32_t HELPER(lcdbr)(uint32_t f1, uint32_t f2)
+{
+ env->fregs[f1].d = float64_chs(env->fregs[f2].d);
+
+ return set_cc_nz_f64(env->fregs[f1].d);
+}
+
+/* load complement of 128-bit float */
+uint32_t HELPER(lcxbr)(uint32_t f1, uint32_t f2)
+{
+ CPU_QuadU x1, x2;
+ x2.ll.upper = env->fregs[f2].ll;
+ x2.ll.lower = env->fregs[f2 + 2].ll;
+ x1.q = float128_chs(x2.q);
+ env->fregs[f1].ll = x1.ll.upper;
+ env->fregs[f1 + 2].ll = x1.ll.lower;
+ return set_cc_nz_f128(x1.q);
+}
+
+/* 32-bit FP addition RM */
+void HELPER(aeb)(uint32_t f1, uint32_t val)
+{
+ float32 v1 = env->fregs[f1].l.upper;
+ CPU_FloatU v2;
+ v2.l = val;
+ HELPER_LOG("%s: adding 0x%d from f%d and 0x%d\n", __FUNCTION__,
+ v1, f1, v2.f);
+ env->fregs[f1].l.upper = float32_add(v1, v2.f, &env->fpu_status);
+}
+
+/* 32-bit FP division RM */
+void HELPER(deb)(uint32_t f1, uint32_t val)
+{
+ float32 v1 = env->fregs[f1].l.upper;
+ CPU_FloatU v2;
+ v2.l = val;
+ HELPER_LOG("%s: dividing 0x%d from f%d by 0x%d\n", __FUNCTION__,
+ v1, f1, v2.f);
+ env->fregs[f1].l.upper = float32_div(v1, v2.f, &env->fpu_status);
+}
+
+/* 32-bit FP multiplication RM */
+void HELPER(meeb)(uint32_t f1, uint32_t val)
+{
+ float32 v1 = env->fregs[f1].l.upper;
+ CPU_FloatU v2;
+ v2.l = val;
+ HELPER_LOG("%s: multiplying 0x%d from f%d and 0x%d\n", __FUNCTION__,
+ v1, f1, v2.f);
+ env->fregs[f1].l.upper = float32_mul(v1, v2.f, &env->fpu_status);
+}
+
+/* 32-bit FP compare RR */
+uint32_t HELPER(cebr)(uint32_t f1, uint32_t f2)
+{
+ float32 v1 = env->fregs[f1].l.upper;
+ float32 v2 = env->fregs[f2].l.upper;;
+ HELPER_LOG("%s: comparing 0x%d from f%d and 0x%d\n", __FUNCTION__,
+ v1, f1, v2);
+ return set_cc_f32(v1, v2);
+}
+
+/* 64-bit FP compare RR */
+uint32_t HELPER(cdbr)(uint32_t f1, uint32_t f2)
+{
+ float64 v1 = env->fregs[f1].d;
+ float64 v2 = env->fregs[f2].d;;
+ HELPER_LOG("%s: comparing 0x%ld from f%d and 0x%ld\n", __FUNCTION__,
+ v1, f1, v2);
+ return set_cc_f64(v1, v2);
+}
+
+/* 128-bit FP compare RR */
+uint32_t HELPER(cxbr)(uint32_t f1, uint32_t f2)
+{
+ CPU_QuadU v1;
+ v1.ll.upper = env->fregs[f1].ll;
+ v1.ll.lower = env->fregs[f1 + 2].ll;
+ CPU_QuadU v2;
+ v2.ll.upper = env->fregs[f2].ll;
+ v2.ll.lower = env->fregs[f2 + 2].ll;
+
+ return float_comp_to_cc(float128_compare_quiet(v1.q, v2.q,
+ &env->fpu_status));
+}
+
+/* 64-bit FP compare RM */
+uint32_t HELPER(cdb)(uint32_t f1, uint64_t a2)
+{
+ float64 v1 = env->fregs[f1].d;
+ CPU_DoubleU v2;
+ v2.ll = ldq(a2);
+ HELPER_LOG("%s: comparing 0x%ld from f%d and 0x%lx\n", __FUNCTION__, v1,
+ f1, v2.d);
+ return set_cc_f64(v1, v2.d);
+}
+
+/* 64-bit FP addition RM */
+uint32_t HELPER(adb)(uint32_t f1, uint64_t a2)
+{
+ float64 v1 = env->fregs[f1].d;
+ CPU_DoubleU v2;
+ v2.ll = ldq(a2);
+ HELPER_LOG("%s: adding 0x%lx from f%d and 0x%lx\n", __FUNCTION__,
+ v1, f1, v2.d);
+ env->fregs[f1].d = v1 = float64_add(v1, v2.d, &env->fpu_status);
+ return set_cc_nz_f64(v1);
+}
+
+/* 32-bit FP subtraction RM */
+void HELPER(seb)(uint32_t f1, uint32_t val)
+{
+ float32 v1 = env->fregs[f1].l.upper;
+ CPU_FloatU v2;
+ v2.l = val;
+ env->fregs[f1].l.upper = float32_sub(v1, v2.f, &env->fpu_status);
+}
+
+/* 64-bit FP subtraction RM */
+uint32_t HELPER(sdb)(uint32_t f1, uint64_t a2)
+{
+ float64 v1 = env->fregs[f1].d;
+ CPU_DoubleU v2;
+ v2.ll = ldq(a2);
+ env->fregs[f1].d = v1 = float64_sub(v1, v2.d, &env->fpu_status);
+ return set_cc_nz_f64(v1);
+}
+
+/* 64-bit FP multiplication RM */
+void HELPER(mdb)(uint32_t f1, uint64_t a2)
+{
+ float64 v1 = env->fregs[f1].d;
+ CPU_DoubleU v2;
+ v2.ll = ldq(a2);
+ HELPER_LOG("%s: multiplying 0x%lx from f%d and 0x%ld\n", __FUNCTION__,
+ v1, f1, v2.d);
+ env->fregs[f1].d = float64_mul(v1, v2.d, &env->fpu_status);
+}
+
+/* 64-bit FP division RM */
+void HELPER(ddb)(uint32_t f1, uint64_t a2)
+{
+ float64 v1 = env->fregs[f1].d;
+ CPU_DoubleU v2;
+ v2.ll = ldq(a2);
+ HELPER_LOG("%s: dividing 0x%lx from f%d by 0x%ld\n", __FUNCTION__,
+ v1, f1, v2.d);
+ env->fregs[f1].d = float64_div(v1, v2.d, &env->fpu_status);
+}
+
+static void set_round_mode(int m3)
+{
+ switch (m3) {
+ case 0:
+ /* current mode */
+ break;
+ case 1:
+ /* biased round no nearest */
+ case 4:
+ /* round to nearest */
+ set_float_rounding_mode(float_round_nearest_even, &env->fpu_status);
+ break;
+ case 5:
+ /* round to zero */
+ set_float_rounding_mode(float_round_to_zero, &env->fpu_status);
+ break;
+ case 6:
+ /* round to +inf */
+ set_float_rounding_mode(float_round_up, &env->fpu_status);
+ break;
+ case 7:
+ /* round to -inf */
+ set_float_rounding_mode(float_round_down, &env->fpu_status);
+ break;
+ }
+}
+
+/* convert 32-bit float to 64-bit int */
+uint32_t HELPER(cgebr)(uint32_t r1, uint32_t f2, uint32_t m3)
+{
+ float32 v2 = env->fregs[f2].l.upper;
+ set_round_mode(m3);
+ env->regs[r1] = float32_to_int64(v2, &env->fpu_status);
+ return set_cc_nz_f32(v2);
+}
+
+/* convert 64-bit float to 64-bit int */
+uint32_t HELPER(cgdbr)(uint32_t r1, uint32_t f2, uint32_t m3)
+{
+ float64 v2 = env->fregs[f2].d;
+ set_round_mode(m3);
+ env->regs[r1] = float64_to_int64(v2, &env->fpu_status);
+ return set_cc_nz_f64(v2);
+}
+
+/* convert 128-bit float to 64-bit int */
+uint32_t HELPER(cgxbr)(uint32_t r1, uint32_t f2, uint32_t m3)
+{
+ CPU_QuadU v2;
+ v2.ll.upper = env->fregs[f2].ll;
+ v2.ll.lower = env->fregs[f2 + 2].ll;
+ set_round_mode(m3);
+ env->regs[r1] = float128_to_int64(v2.q, &env->fpu_status);
+ if (float128_is_any_nan(v2.q)) {
+ return 3;
+ } else if (float128_is_zero(v2.q)) {
+ return 0;
+ } else if (float128_is_neg(v2.q)) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+
+/* convert 32-bit float to 32-bit int */
+uint32_t HELPER(cfebr)(uint32_t r1, uint32_t f2, uint32_t m3)
+{
+ float32 v2 = env->fregs[f2].l.upper;
+ set_round_mode(m3);
+ env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) |
+ float32_to_int32(v2, &env->fpu_status);
+ return set_cc_nz_f32(v2);
+}
+
+/* convert 64-bit float to 32-bit int */
+uint32_t HELPER(cfdbr)(uint32_t r1, uint32_t f2, uint32_t m3)
+{
+ float64 v2 = env->fregs[f2].d;
+ set_round_mode(m3);
+ env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) |
+ float64_to_int32(v2, &env->fpu_status);
+ return set_cc_nz_f64(v2);
+}
+
+/* convert 128-bit float to 32-bit int */
+uint32_t HELPER(cfxbr)(uint32_t r1, uint32_t f2, uint32_t m3)
+{
+ CPU_QuadU v2;
+ v2.ll.upper = env->fregs[f2].ll;
+ v2.ll.lower = env->fregs[f2 + 2].ll;
+ env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) |
+ float128_to_int32(v2.q, &env->fpu_status);
+ return set_cc_nz_f128(v2.q);
+}
+
+/* load 32-bit FP zero */
+void HELPER(lzer)(uint32_t f1)
+{
+ env->fregs[f1].l.upper = float32_zero;
+}
+
+/* load 64-bit FP zero */
+void HELPER(lzdr)(uint32_t f1)
+{
+ env->fregs[f1].d = float64_zero;
+}
+
+/* load 128-bit FP zero */
+void HELPER(lzxr)(uint32_t f1)
+{
+ CPU_QuadU x;
+ x.q = float64_to_float128(float64_zero, &env->fpu_status);
+ env->fregs[f1].ll = x.ll.upper;
+ env->fregs[f1 + 1].ll = x.ll.lower;
+}
+
+/* 128-bit FP subtraction RR */
+uint32_t HELPER(sxbr)(uint32_t f1, uint32_t f2)
+{
+ CPU_QuadU v1;
+ v1.ll.upper = env->fregs[f1].ll;
+ v1.ll.lower = env->fregs[f1 + 2].ll;
+ CPU_QuadU v2;
+ v2.ll.upper = env->fregs[f2].ll;
+ v2.ll.lower = env->fregs[f2 + 2].ll;
+ CPU_QuadU res;
+ res.q = float128_sub(v1.q, v2.q, &env->fpu_status);
+ env->fregs[f1].ll = res.ll.upper;
+ env->fregs[f1 + 2].ll = res.ll.lower;
+ return set_cc_nz_f128(res.q);
+}
+
+/* 128-bit FP addition RR */
+uint32_t HELPER(axbr)(uint32_t f1, uint32_t f2)
+{
+ CPU_QuadU v1;
+ v1.ll.upper = env->fregs[f1].ll;
+ v1.ll.lower = env->fregs[f1 + 2].ll;
+ CPU_QuadU v2;
+ v2.ll.upper = env->fregs[f2].ll;
+ v2.ll.lower = env->fregs[f2 + 2].ll;
+ CPU_QuadU res;
+ res.q = float128_add(v1.q, v2.q, &env->fpu_status);
+ env->fregs[f1].ll = res.ll.upper;
+ env->fregs[f1 + 2].ll = res.ll.lower;
+ return set_cc_nz_f128(res.q);
+}
+
+/* 32-bit FP multiplication RR */
+void HELPER(meebr)(uint32_t f1, uint32_t f2)
+{
+ env->fregs[f1].l.upper = float32_mul(env->fregs[f1].l.upper,
+ env->fregs[f2].l.upper,
+ &env->fpu_status);
+}
+
+/* 64-bit FP division RR */
+void HELPER(ddbr)(uint32_t f1, uint32_t f2)
+{
+ env->fregs[f1].d = float64_div(env->fregs[f1].d, env->fregs[f2].d,
+ &env->fpu_status);
+}
+
+/* 64-bit FP multiply and add RM */
+void HELPER(madb)(uint32_t f1, uint64_t a2, uint32_t f3)
+{
+ HELPER_LOG("%s: f1 %d a2 0x%lx f3 %d\n", __FUNCTION__, f1, a2, f3);
+ CPU_DoubleU v2;
+ v2.ll = ldq(a2);
+ env->fregs[f1].d = float64_add(env->fregs[f1].d,
+ float64_mul(v2.d, env->fregs[f3].d,
+ &env->fpu_status),
+ &env->fpu_status);
+}
+
+/* 64-bit FP multiply and add RR */
+void HELPER(madbr)(uint32_t f1, uint32_t f3, uint32_t f2)
+{
+ HELPER_LOG("%s: f1 %d f2 %d f3 %d\n", __FUNCTION__, f1, f2, f3);
+ env->fregs[f1].d = float64_add(float64_mul(env->fregs[f2].d,
+ env->fregs[f3].d,
+ &env->fpu_status),
+ env->fregs[f1].d, &env->fpu_status);
+}
+
+/* 64-bit FP multiply and subtract RR */
+void HELPER(msdbr)(uint32_t f1, uint32_t f3, uint32_t f2)
+{
+ HELPER_LOG("%s: f1 %d f2 %d f3 %d\n", __FUNCTION__, f1, f2, f3);
+ env->fregs[f1].d = float64_sub(float64_mul(env->fregs[f2].d,
+ env->fregs[f3].d,
+ &env->fpu_status),
+ env->fregs[f1].d, &env->fpu_status);
+}
+
+/* 32-bit FP multiply and add RR */
+void HELPER(maebr)(uint32_t f1, uint32_t f3, uint32_t f2)
+{
+ env->fregs[f1].l.upper = float32_add(env->fregs[f1].l.upper,
+ float32_mul(env->fregs[f2].l.upper,
+ env->fregs[f3].l.upper,
+ &env->fpu_status),
+ &env->fpu_status);
+}
+
+/* convert 64-bit float to 128-bit float */
+void HELPER(lxdb)(uint32_t f1, uint64_t a2)
+{
+ CPU_DoubleU v2;
+ v2.ll = ldq(a2);
+ CPU_QuadU v1;
+ v1.q = float64_to_float128(v2.d, &env->fpu_status);
+ env->fregs[f1].ll = v1.ll.upper;
+ env->fregs[f1 + 2].ll = v1.ll.lower;
+}
+
+/* test data class 32-bit */
+uint32_t HELPER(tceb)(uint32_t f1, uint64_t m2)
+{
+ float32 v1 = env->fregs[f1].l.upper;
+ int neg = float32_is_neg(v1);
+ uint32_t cc = 0;
+
+ HELPER_LOG("%s: v1 0x%lx m2 0x%lx neg %d\n", __FUNCTION__, (long)v1, m2, neg);
+ if ((float32_is_zero(v1) && (m2 & (1 << (11-neg)))) ||
+ (float32_is_infinity(v1) && (m2 & (1 << (5-neg)))) ||
+ (float32_is_any_nan(v1) && (m2 & (1 << (3-neg)))) ||
+ (float32_is_signaling_nan(v1) && (m2 & (1 << (1-neg))))) {
+ cc = 1;
+ } else if (m2 & (1 << (9-neg))) {
+ /* assume normalized number */
+ cc = 1;
+ }
+
+ /* FIXME: denormalized? */
+ return cc;
+}
+
+/* test data class 64-bit */
+uint32_t HELPER(tcdb)(uint32_t f1, uint64_t m2)
+{
+ float64 v1 = env->fregs[f1].d;
+ int neg = float64_is_neg(v1);
+ uint32_t cc = 0;
+
+ HELPER_LOG("%s: v1 0x%lx m2 0x%lx neg %d\n", __FUNCTION__, v1, m2, neg);
+ if ((float64_is_zero(v1) && (m2 & (1 << (11-neg)))) ||
+ (float64_is_infinity(v1) && (m2 & (1 << (5-neg)))) ||
+ (float64_is_any_nan(v1) && (m2 & (1 << (3-neg)))) ||
+ (float64_is_signaling_nan(v1) && (m2 & (1 << (1-neg))))) {
+ cc = 1;
+ } else if (m2 & (1 << (9-neg))) {
+ /* assume normalized number */
+ cc = 1;
+ }
+ /* FIXME: denormalized? */
+ return cc;
+}
+
+/* test data class 128-bit */
+uint32_t HELPER(tcxb)(uint32_t f1, uint64_t m2)
+{
+ CPU_QuadU v1;
+ uint32_t cc = 0;
+ v1.ll.upper = env->fregs[f1].ll;
+ v1.ll.lower = env->fregs[f1 + 2].ll;
+
+ int neg = float128_is_neg(v1.q);
+ if ((float128_is_zero(v1.q) && (m2 & (1 << (11-neg)))) ||
+ (float128_is_infinity(v1.q) && (m2 & (1 << (5-neg)))) ||
+ (float128_is_any_nan(v1.q) && (m2 & (1 << (3-neg)))) ||
+ (float128_is_signaling_nan(v1.q) && (m2 & (1 << (1-neg))))) {
+ cc = 1;
+ } else if (m2 & (1 << (9-neg))) {
+ /* assume normalized number */
+ cc = 1;
+ }
+ /* FIXME: denormalized? */
+ return cc;
+}
+
+/* find leftmost one */
+uint32_t HELPER(flogr)(uint32_t r1, uint64_t v2)
+{
+ uint64_t res = 0;
+ uint64_t ov2 = v2;
+
+ while (!(v2 & 0x8000000000000000ULL) && v2) {
+ v2 <<= 1;
+ res++;
+ }
+
+ if (!v2) {
+ env->regs[r1] = 64;
+ env->regs[r1 + 1] = 0;
+ return 0;
+ } else {
+ env->regs[r1] = res;
+ env->regs[r1 + 1] = ov2 & ~(0x8000000000000000ULL >> res);
+ return 2;
+ }
+}
+
+/* square root 64-bit RR */
+void HELPER(sqdbr)(uint32_t f1, uint32_t f2)
+{
+ env->fregs[f1].d = float64_sqrt(env->fregs[f2].d, &env->fpu_status);
+}
+
+static inline uint64_t cksm_overflow(uint64_t cksm)
+{
+ if (cksm > 0xffffffffULL) {
+ cksm &= 0xffffffffULL;
+ cksm++;
+ }
+ return cksm;
+}
+
+/* checksum */
+void HELPER(cksm)(uint32_t r1, uint32_t r2)
+{
+ uint64_t src = get_address_31fix(r2);
+ uint64_t src_len = env->regs[(r2 + 1) & 15];
+ uint64_t cksm = 0;
+
+ while (src_len >= 4) {
+ cksm += ldl(src);
+ cksm = cksm_overflow(cksm);
+
+ /* move to next word */
+ src_len -= 4;
+ src += 4;
+ }
+
+ switch (src_len) {
+ case 0:
+ break;
+ case 1:
+ cksm += ldub(src);
+ cksm = cksm_overflow(cksm);
+ break;
+ case 2:
+ cksm += lduw(src);
+ cksm = cksm_overflow(cksm);
+ break;
+ case 3:
+ /* XXX check if this really is correct */
+ cksm += lduw(src) << 8;
+ cksm += ldub(src + 2);
+ cksm = cksm_overflow(cksm);
+ break;
+ }
+
+ /* indicate we've processed everything */
+ env->regs[(r2 + 1) & 15] = 0;
+
+ /* store result */
+ env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | (uint32_t)cksm;
+}
+
+static inline uint32_t cc_calc_ltgt_32(CPUState *env, int32_t src,
+ int32_t dst)
+{
+ if (src == dst) {
+ return 0;
+ } else if (src < dst) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+
+static inline uint32_t cc_calc_ltgt0_32(CPUState *env, int32_t dst)
+{
+ return cc_calc_ltgt_32(env, dst, 0);
+}
+
+static inline uint32_t cc_calc_ltgt_64(CPUState *env, int64_t src,
+ int64_t dst)
+{
+ if (src == dst) {
+ return 0;
+ } else if (src < dst) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+
+static inline uint32_t cc_calc_ltgt0_64(CPUState *env, int64_t dst)
+{
+ return cc_calc_ltgt_64(env, dst, 0);
+}
+
+static inline uint32_t cc_calc_ltugtu_32(CPUState *env, uint32_t src,
+ uint32_t dst)
+{
+ if (src == dst) {
+ return 0;
+ } else if (src < dst) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+
+static inline uint32_t cc_calc_ltugtu_64(CPUState *env, uint64_t src,
+ uint64_t dst)
+{
+ if (src == dst) {
+ return 0;
+ } else if (src < dst) {
+ return 1;
+ } else {
+ return 2;
+ }
+}
+
+static inline uint32_t cc_calc_tm_32(CPUState *env, uint32_t val, uint32_t mask)
+{
+ HELPER_LOG("%s: val 0x%x mask 0x%x\n", __FUNCTION__, val, mask);
+ uint16_t r = val & mask;
+ if (r == 0 || mask == 0) {
+ return 0;
+ } else if (r == mask) {
+ return 3;
+ } else {
+ return 1;
+ }
+}
+
+/* set condition code for test under mask */
+static inline uint32_t cc_calc_tm_64(CPUState *env, uint64_t val, uint32_t mask)
+{
+ uint16_t r = val & mask;
+ HELPER_LOG("%s: val 0x%lx mask 0x%x r 0x%x\n", __FUNCTION__, val, mask, r);
+ if (r == 0 || mask == 0) {
+ return 0;
+ } else if (r == mask) {
+ return 3;
+ } else {
+ while (!(mask & 0x8000)) {
+ mask <<= 1;
+ val <<= 1;
+ }
+ if (val & 0x8000) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+}
+
+static inline uint32_t cc_calc_nz(CPUState *env, uint64_t dst)
+{
+ return !!dst;
+}
+
+static inline uint32_t cc_calc_add_64(CPUState *env, int64_t a1, int64_t a2,
+ int64_t ar)
+{
+ if ((a1 > 0 && a2 > 0 && ar < 0) || (a1 < 0 && a2 < 0 && ar > 0)) {
+ return 3; /* overflow */
+ } else {
+ if (ar < 0) {
+ return 1;
+ } else if (ar > 0) {
+ return 2;
+ } else {
+ return 0;
+ }
+ }
+}
+
+static inline uint32_t cc_calc_addu_64(CPUState *env, uint64_t a1, uint64_t a2,
+ uint64_t ar)
+{
+ if (ar == 0) {
+ if (a1) {
+ return 2;
+ } else {
+ return 0;
+ }
+ } else {
+ if (ar < a1 || ar < a2) {
+ return 3;
+ } else {
+ return 1;
+ }
+ }
+}
+
+static inline uint32_t cc_calc_sub_64(CPUState *env, int64_t a1, int64_t a2,
+ int64_t ar)
+{
+ if ((a1 > 0 && a2 < 0 && ar < 0) || (a1 < 0 && a2 > 0 && ar > 0)) {
+ return 3; /* overflow */
+ } else {
+ if (ar < 0) {
+ return 1;
+ } else if (ar > 0) {
+ return 2;
+ } else {
+ return 0;
+ }
+ }
+}
+
+static inline uint32_t cc_calc_subu_64(CPUState *env, uint64_t a1, uint64_t a2,
+ uint64_t ar)
+{
+ if (ar == 0) {
+ return 2;
+ } else {
+ if (a2 > a1) {
+ return 1;
+ } else {
+ return 3;
+ }
+ }
+}
+
+static inline uint32_t cc_calc_abs_64(CPUState *env, int64_t dst)
+{
+ if ((uint64_t)dst == 0x8000000000000000ULL) {
+ return 3;
+ } else if (dst) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static inline uint32_t cc_calc_nabs_64(CPUState *env, int64_t dst)
+{
+ return !!dst;
+}
+
+static inline uint32_t cc_calc_comp_64(CPUState *env, int64_t dst)
+{
+ if ((uint64_t)dst == 0x8000000000000000ULL) {
+ return 3;
+ } else if (dst < 0) {
+ return 1;
+ } else if (dst > 0) {
+ return 2;
+ } else {
+ return 0;
+ }
+}
+
+
+static inline uint32_t cc_calc_add_32(CPUState *env, int32_t a1, int32_t a2,
+ int32_t ar)
+{
+ if ((a1 > 0 && a2 > 0 && ar < 0) || (a1 < 0 && a2 < 0 && ar > 0)) {
+ return 3; /* overflow */
+ } else {
+ if (ar < 0) {
+ return 1;
+ } else if (ar > 0) {
+ return 2;
+ } else {
+ return 0;
+ }
+ }
+}
+
+static inline uint32_t cc_calc_addu_32(CPUState *env, uint32_t a1, uint32_t a2,
+ uint32_t ar)
+{
+ if (ar == 0) {
+ if (a1) {
+ return 2;
+ } else {
+ return 0;
+ }
+ } else {
+ if (ar < a1 || ar < a2) {
+ return 3;
+ } else {
+ return 1;
+ }
+ }
+}
+
+static inline uint32_t cc_calc_sub_32(CPUState *env, int32_t a1, int32_t a2,
+ int32_t ar)
+{
+ if ((a1 > 0 && a2 < 0 && ar < 0) || (a1 < 0 && a2 > 0 && ar > 0)) {
+ return 3; /* overflow */
+ } else {
+ if (ar < 0) {
+ return 1;
+ } else if (ar > 0) {
+ return 2;
+ } else {
+ return 0;
+ }
+ }
+}
+
+static inline uint32_t cc_calc_subu_32(CPUState *env, uint32_t a1, uint32_t a2,
+ uint32_t ar)
+{
+ if (ar == 0) {
+ return 2;
+ } else {
+ if (a2 > a1) {
+ return 1;
+ } else {
+ return 3;
+ }
+ }
+}
+
+static inline uint32_t cc_calc_abs_32(CPUState *env, int32_t dst)
+{
+ if ((uint32_t)dst == 0x80000000UL) {
+ return 3;
+ } else if (dst) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static inline uint32_t cc_calc_nabs_32(CPUState *env, int32_t dst)
+{
+ return !!dst;
+}
+
+static inline uint32_t cc_calc_comp_32(CPUState *env, int32_t dst)
+{
+ if ((uint32_t)dst == 0x80000000UL) {
+ return 3;
+ } else if (dst < 0) {
+ return 1;
+ } else if (dst > 0) {
+ return 2;
+ } else {
+ return 0;
+ }
+}
+
+/* calculate condition code for insert character under mask insn */
+static inline uint32_t cc_calc_icm_32(CPUState *env, uint32_t mask, uint32_t val)
+{
+ HELPER_LOG("%s: mask 0x%x val %d\n", __FUNCTION__, mask, val);
+ uint32_t cc;
+
+ if (mask == 0xf) {
+ if (!val) {
+ return 0;
+ } else if (val & 0x80000000) {
+ return 1;
+ } else {
+ return 2;
+ }
+ }
+
+ if (!val || !mask) {
+ cc = 0;
+ } else {
+ while (mask != 1) {
+ mask >>= 1;
+ val >>= 8;
+ }
+ if (val & 0x80) {
+ cc = 1;
+ } else {
+ cc = 2;
+ }
+ }
+ return cc;
+}
+
+static inline uint32_t cc_calc_slag(CPUState *env, uint64_t src, uint64_t shift)
+{
+ uint64_t mask = ((1ULL << shift) - 1ULL) << (64 - shift);
+ uint64_t match, r;
+
+ /* check if the sign bit stays the same */
+ if (src & (1ULL << 63)) {
+ match = mask;
+ } else {
+ match = 0;
+ }
+
+ if ((src & mask) != match) {
+ /* overflow */
+ return 3;
+ }
+
+ r = ((src << shift) & ((1ULL << 63) - 1)) | (src & (1ULL << 63));
+
+ if ((int64_t)r == 0) {
+ return 0;
+ } else if ((int64_t)r < 0) {
+ return 1;
+ }
+
+ return 2;
+}
+
+
+static inline uint32_t do_calc_cc(CPUState *env, uint32_t cc_op, uint64_t src,
+ uint64_t dst, uint64_t vr)
+{
+ uint32_t r = 0;
+
+ switch (cc_op) {
+ case CC_OP_CONST0:
+ case CC_OP_CONST1:
+ case CC_OP_CONST2:
+ case CC_OP_CONST3:
+ /* cc_op value _is_ cc */
+ r = cc_op;
+ break;
+ case CC_OP_LTGT0_32:
+ r = cc_calc_ltgt0_32(env, dst);
+ break;
+ case CC_OP_LTGT0_64:
+ r = cc_calc_ltgt0_64(env, dst);
+ break;
+ case CC_OP_LTGT_32:
+ r = cc_calc_ltgt_32(env, src, dst);
+ break;
+ case CC_OP_LTGT_64:
+ r = cc_calc_ltgt_64(env, src, dst);
+ break;
+ case CC_OP_LTUGTU_32:
+ r = cc_calc_ltugtu_32(env, src, dst);
+ break;
+ case CC_OP_LTUGTU_64:
+ r = cc_calc_ltugtu_64(env, src, dst);
+ break;
+ case CC_OP_TM_32:
+ r = cc_calc_tm_32(env, src, dst);
+ break;
+ case CC_OP_TM_64:
+ r = cc_calc_tm_64(env, src, dst);
+ break;
+ case CC_OP_NZ:
+ r = cc_calc_nz(env, dst);
+ break;
+ case CC_OP_ADD_64:
+ r = cc_calc_add_64(env, src, dst, vr);
+ break;
+ case CC_OP_ADDU_64:
+ r = cc_calc_addu_64(env, src, dst, vr);
+ break;
+ case CC_OP_SUB_64:
+ r = cc_calc_sub_64(env, src, dst, vr);
+ break;
+ case CC_OP_SUBU_64:
+ r = cc_calc_subu_64(env, src, dst, vr);
+ break;
+ case CC_OP_ABS_64:
+ r = cc_calc_abs_64(env, dst);
+ break;
+ case CC_OP_NABS_64:
+ r = cc_calc_nabs_64(env, dst);
+ break;
+ case CC_OP_COMP_64:
+ r = cc_calc_comp_64(env, dst);
+ break;
+
+ case CC_OP_ADD_32:
+ r = cc_calc_add_32(env, src, dst, vr);
+ break;
+ case CC_OP_ADDU_32:
+ r = cc_calc_addu_32(env, src, dst, vr);
+ break;
+ case CC_OP_SUB_32:
+ r = cc_calc_sub_32(env, src, dst, vr);
+ break;
+ case CC_OP_SUBU_32:
+ r = cc_calc_subu_32(env, src, dst, vr);
+ break;
+ case CC_OP_ABS_32:
+ r = cc_calc_abs_64(env, dst);
+ break;
+ case CC_OP_NABS_32:
+ r = cc_calc_nabs_64(env, dst);
+ break;
+ case CC_OP_COMP_32:
+ r = cc_calc_comp_32(env, dst);
+ break;
+
+ case CC_OP_ICM:
+ r = cc_calc_icm_32(env, src, dst);
+ break;
+ case CC_OP_SLAG:
+ r = cc_calc_slag(env, src, dst);
+ break;
+
+ case CC_OP_LTGT_F32:
+ r = set_cc_f32(src, dst);
+ break;
+ case CC_OP_LTGT_F64:
+ r = set_cc_f64(src, dst);
+ break;
+ case CC_OP_NZ_F32:
+ r = set_cc_nz_f32(dst);
+ break;
+ case CC_OP_NZ_F64:
+ r = set_cc_nz_f64(dst);
+ break;
+
+ default:
+ cpu_abort(env, "Unknown CC operation: %s\n", cc_name(cc_op));
+ }
+
+ HELPER_LOG("%s: %15s 0x%016lx 0x%016lx 0x%016lx = %d\n", __FUNCTION__,
+ cc_name(cc_op), src, dst, vr, r);
+ return r;
+}
+
uint32_t calc_cc(CPUState *env, uint32_t cc_op, uint64_t src, uint64_t dst,
uint64_t vr)
{
+ return do_calc_cc(env, cc_op, src, dst, vr);
+}
+
+uint32_t HELPER(calc_cc)(uint32_t cc_op, uint64_t src, uint64_t dst,
+ uint64_t vr)
+{
+ return do_calc_cc(env, cc_op, src, dst, vr);
+}
+
+uint64_t HELPER(cvd)(int32_t bin)
+{
+ /* positive 0 */
+ uint64_t dec = 0x0c;
+ int shift = 4;
+
+ if (bin < 0) {
+ bin = -bin;
+ dec = 0x0d;
+ }
+
+ for (shift = 4; (shift < 64) && bin; shift += 4) {
+ int current_number = bin % 10;
+
+ dec |= (current_number) << shift;
+ bin /= 10;
+ }
+
+ return dec;
+}
+
+void HELPER(unpk)(uint32_t len, uint64_t dest, uint64_t src)
+{
+ int len_dest = len >> 4;
+ int len_src = len & 0xf;
+ uint8_t b;
+ int second_nibble = 0;
+
+ dest += len_dest;
+ src += len_src;
+
+ /* last byte is special, it only flips the nibbles */
+ b = ldub(src);
+ stb(dest, (b << 4) | (b >> 4));
+ src--;
+ len_src--;
+
+ /* now pad every nibble with 0xf0 */
+
+ while (len_dest > 0) {
+ uint8_t cur_byte = 0;
+
+ if (len_src > 0) {
+ cur_byte = ldub(src);
+ }
+
+ len_dest--;
+ dest--;
+
+ /* only advance one nibble at a time */
+ if (second_nibble) {
+ cur_byte >>= 4;
+ len_src--;
+ src--;
+ }
+ second_nibble = !second_nibble;
+
+ /* digit */
+ cur_byte = (cur_byte & 0xf);
+ /* zone bits */
+ cur_byte |= 0xf0;
+
+ stb(dest, cur_byte);
+ }
+}
+
+void HELPER(tr)(uint32_t len, uint64_t array, uint64_t trans)
+{
+ int i;
+
+ for (i = 0; i <= len; i++) {
+ uint8_t byte = ldub(array + i);
+ uint8_t new_byte = ldub(trans + byte);
+ stb(array + i, new_byte);
+ }
+}
+
+#ifndef CONFIG_USER_ONLY
+
+void HELPER(load_psw)(uint64_t mask, uint64_t addr)
+{
+ load_psw(env, mask, addr);
+ cpu_loop_exit();
+}
+
+static void program_interrupt(CPUState *env, uint32_t code, int ilc)
+{
+ qemu_log("program interrupt at %#" PRIx64 "\n", env->psw.addr);
+
+ if (kvm_enabled()) {
+ kvm_s390_interrupt(env, KVM_S390_PROGRAM_INT, code);
+ } else {
+ env->int_pgm_code = code;
+ env->int_pgm_ilc = ilc;
+ env->exception_index = EXCP_PGM;
+ cpu_loop_exit();
+ }
+}
+
+static void ext_interrupt(CPUState *env, int type, uint32_t param,
+ uint64_t param64)
+{
+ cpu_inject_ext(env, type, param, param64);
+}
+
+int sclp_service_call(CPUState *env, uint32_t sccb, uint64_t code)
+{
+ int r = 0;
+
+#ifdef DEBUG_HELPER
+ printf("sclp(0x%x, 0x%" PRIx64 ")\n", sccb, code);
+#endif
+
+ 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);
+
+ if (kvm_enabled()) {
+#ifdef CONFIG_KVM
+ kvm_s390_interrupt_internal(env, KVM_S390_INT_SERVICE,
+ sccb & ~3, 0, 1);
+#endif
+ } else {
+ env->psw.addr += 4;
+ ext_interrupt(env, EXT_SERVICE, sccb & ~3, 0);
+ }
+ break;
+ default:
+#ifdef DEBUG_HELPER
+ printf("KVM: invalid sclp call 0x%x / 0x%" PRIx64 "x\n", sccb, code);
+#endif
+ r = -1;
+ break;
+ }
+
+out:
+ return r;
+}
+
+/* SCLP service call */
+uint32_t HELPER(servc)(uint32_t r1, uint64_t r2)
+{
+ if (sclp_service_call(env, r1, r2)) {
+ return 3;
+ }
+
+ return 0;
+}
+
+/* DIAG */
+uint64_t HELPER(diag)(uint32_t num, uint64_t mem, uint64_t code)
+{
+ uint64_t r;
+
+ switch (num) {
+ case 0x500:
+ /* KVM hypercall */
+ r = s390_virtio_hypercall(env, mem, code);
+ break;
+ case 0x44:
+ /* yield */
+ r = 0;
+ break;
+ case 0x308:
+ /* ipl */
+ r = 0;
+ break;
+ default:
+ r = -1;
+ break;
+ }
+
+ if (r) {
+ program_interrupt(env, PGM_OPERATION, ILC_LATER_INC);
+ }
+
+ return r;
+}
+
+/* Store CPU ID */
+void HELPER(stidp)(uint64_t a1)
+{
+ stq(a1, env->cpu_num);
+}
+
+/* Set Prefix */
+void HELPER(spx)(uint64_t a1)
+{
+ uint32_t prefix;
+
+ prefix = ldl(a1);
+ env->psa = prefix & 0xfffff000;
+ qemu_log("prefix: %#x\n", prefix);
+ tlb_flush_page(env, 0);
+ tlb_flush_page(env, TARGET_PAGE_SIZE);
+}
+
+/* Set Clock */
+uint32_t HELPER(sck)(uint64_t a1)
+{
+ /* XXX not implemented - is it necessary? */
+
+ return 0;
+}
+
+static inline uint64_t clock_value(CPUState *env)
+{
+ uint64_t time;
+
+ time = env->tod_offset +
+ time2tod(qemu_get_clock_ns(vm_clock) - env->tod_basetime);
+
+ return time;
+}
+
+/* Store Clock */
+uint32_t HELPER(stck)(uint64_t a1)
+{
+ stq(a1, clock_value(env));
+
+ return 0;
+}
+
+/* Store Clock Extended */
+uint32_t HELPER(stcke)(uint64_t a1)
+{
+ stb(a1, 0);
+ /* basically the same value as stck */
+ stq(a1 + 1, clock_value(env) | env->cpu_num);
+ /* more fine grained than stck */
+ stq(a1 + 9, 0);
+ /* XXX programmable fields */
+ stw(a1 + 17, 0);
+
+
return 0;
}
+
+/* Set Clock Comparator */
+void HELPER(sckc)(uint64_t a1)
+{
+ uint64_t time = ldq(a1);
+
+ if (time == -1ULL) {
+ return;
+ }
+
+ /* difference between now and then */
+ time -= clock_value(env);
+ /* nanoseconds */
+ time = (time * 125) >> 9;
+
+ qemu_mod_timer(env->tod_timer, qemu_get_clock_ns(vm_clock) + time);
+}
+
+/* Store Clock Comparator */
+void HELPER(stckc)(uint64_t a1)
+{
+ /* XXX implement */
+ stq(a1, 0);
+}
+
+/* Set CPU Timer */
+void HELPER(spt)(uint64_t a1)
+{
+ uint64_t time = ldq(a1);
+
+ if (time == -1ULL) {
+ return;
+ }
+
+ /* nanoseconds */
+ time = (time * 125) >> 9;
+
+ qemu_mod_timer(env->cpu_timer, qemu_get_clock_ns(vm_clock) + time);
+}
+
+/* Store CPU Timer */
+void HELPER(stpt)(uint64_t a1)
+{
+ /* XXX implement */
+ stq(a1, 0);
+}
+
+/* Store System Information */
+uint32_t HELPER(stsi)(uint64_t a0, uint32_t r0, uint32_t r1)
+{
+ int cc = 0;
+ int sel1, sel2;
+
+ if ((r0 & STSI_LEVEL_MASK) <= STSI_LEVEL_3 &&
+ ((r0 & STSI_R0_RESERVED_MASK) || (r1 & STSI_R1_RESERVED_MASK))) {
+ /* valid function code, invalid reserved bits */
+ program_interrupt(env, PGM_SPECIFICATION, 2);
+ }
+
+ sel1 = r0 & STSI_R0_SEL1_MASK;
+ sel2 = r1 & STSI_R1_SEL2_MASK;
+
+ /* XXX: spec exception if sysib is not 4k-aligned */
+
+ switch (r0 & STSI_LEVEL_MASK) {
+ case STSI_LEVEL_1:
+ if ((sel1 == 1) && (sel2 == 1)) {
+ /* Basic Machine Configuration */
+ struct sysib_111 sysib;
+
+ memset(&sysib, 0, sizeof(sysib));
+ ebcdic_put(sysib.manuf, "QEMU ", 16);
+ /* same as machine type number in STORE CPU ID */
+ ebcdic_put(sysib.type, "QEMU", 4);
+ /* same as model number in STORE CPU ID */
+ ebcdic_put(sysib.model, "QEMU ", 16);
+ ebcdic_put(sysib.sequence, "QEMU ", 16);
+ ebcdic_put(sysib.plant, "QEMU", 4);
+ cpu_physical_memory_rw(a0, (uint8_t*)&sysib, sizeof(sysib), 1);
+ } else if ((sel1 == 2) && (sel2 == 1)) {
+ /* Basic Machine CPU */
+ struct sysib_121 sysib;
+
+ memset(&sysib, 0, sizeof(sysib));
+ /* XXX make different for different CPUs? */
+ ebcdic_put(sysib.sequence, "QEMUQEMUQEMUQEMU", 16);
+ ebcdic_put(sysib.plant, "QEMU", 4);
+ stw_p(&sysib.cpu_addr, env->cpu_num);
+ cpu_physical_memory_rw(a0, (uint8_t*)&sysib, sizeof(sysib), 1);
+ } else if ((sel1 == 2) && (sel2 == 2)) {
+ /* Basic Machine CPUs */
+ struct sysib_122 sysib;
+
+ memset(&sysib, 0, sizeof(sysib));
+ stl_p(&sysib.capability, 0x443afc29);
+ /* XXX change when SMP comes */
+ stw_p(&sysib.total_cpus, 1);
+ stw_p(&sysib.active_cpus, 1);
+ stw_p(&sysib.standby_cpus, 0);
+ stw_p(&sysib.reserved_cpus, 0);
+ cpu_physical_memory_rw(a0, (uint8_t*)&sysib, sizeof(sysib), 1);
+ } else {
+ cc = 3;
+ }
+ break;
+ case STSI_LEVEL_2:
+ {
+ if ((sel1 == 2) && (sel2 == 1)) {
+ /* LPAR CPU */
+ struct sysib_221 sysib;
+
+ memset(&sysib, 0, sizeof(sysib));
+ /* XXX make different for different CPUs? */
+ ebcdic_put(sysib.sequence, "QEMUQEMUQEMUQEMU", 16);
+ ebcdic_put(sysib.plant, "QEMU", 4);
+ stw_p(&sysib.cpu_addr, env->cpu_num);
+ stw_p(&sysib.cpu_id, 0);
+ cpu_physical_memory_rw(a0, (uint8_t*)&sysib, sizeof(sysib), 1);
+ } else if ((sel1 == 2) && (sel2 == 2)) {
+ /* LPAR CPUs */
+ struct sysib_222 sysib;
+
+ memset(&sysib, 0, sizeof(sysib));
+ stw_p(&sysib.lpar_num, 0);
+ sysib.lcpuc = 0;
+ /* XXX change when SMP comes */
+ stw_p(&sysib.total_cpus, 1);
+ stw_p(&sysib.conf_cpus, 1);
+ stw_p(&sysib.standby_cpus, 0);
+ stw_p(&sysib.reserved_cpus, 0);
+ ebcdic_put(sysib.name, "QEMU ", 8);
+ stl_p(&sysib.caf, 1000);
+ stw_p(&sysib.dedicated_cpus, 0);
+ stw_p(&sysib.shared_cpus, 0);
+ cpu_physical_memory_rw(a0, (uint8_t*)&sysib, sizeof(sysib), 1);
+ } else {
+ cc = 3;
+ }
+ break;
+ }
+ case STSI_LEVEL_3:
+ {
+ if ((sel1 == 2) && (sel2 == 2)) {
+ /* VM CPUs */
+ struct sysib_322 sysib;
+
+ memset(&sysib, 0, sizeof(sysib));
+ sysib.count = 1;
+ /* XXX change when SMP comes */
+ stw_p(&sysib.vm[0].total_cpus, 1);
+ stw_p(&sysib.vm[0].conf_cpus, 1);
+ stw_p(&sysib.vm[0].standby_cpus, 0);
+ stw_p(&sysib.vm[0].reserved_cpus, 0);
+ ebcdic_put(sysib.vm[0].name, "KVMguest", 8);
+ stl_p(&sysib.vm[0].caf, 1000);
+ ebcdic_put(sysib.vm[0].cpi, "KVM/Linux ", 16);
+ cpu_physical_memory_rw(a0, (uint8_t*)&sysib, sizeof(sysib), 1);
+ } else {
+ cc = 3;
+ }
+ break;
+ }
+ case STSI_LEVEL_CURRENT:
+ env->regs[0] = STSI_LEVEL_3;
+ break;
+ default:
+ cc = 3;
+ break;
+ }
+
+ return cc;
+}
+
+void HELPER(lctlg)(uint32_t r1, uint64_t a2, uint32_t r3)
+{
+ int i;
+ uint64_t src = a2;
+
+ for (i = r1;; i = (i + 1) % 16) {
+ env->cregs[i] = ldq(src);
+ HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
+ i, src, env->cregs[i]);
+ src += sizeof(uint64_t);
+
+ if (i == r3) {
+ break;
+ }
+ }
+
+ tlb_flush(env, 1);
+}
+
+void HELPER(lctl)(uint32_t r1, uint64_t a2, uint32_t r3)
+{
+ int i;
+ uint64_t src = a2;
+
+ for (i = r1;; i = (i + 1) % 16) {
+ env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) | ldl(src);
+ src += sizeof(uint32_t);
+
+ if (i == r3) {
+ break;
+ }
+ }
+
+ tlb_flush(env, 1);
+}
+
+void HELPER(stctg)(uint32_t r1, uint64_t a2, uint32_t r3)
+{
+ int i;
+ uint64_t dest = a2;
+
+ for (i = r1;; i = (i + 1) % 16) {
+ stq(dest, env->cregs[i]);
+ dest += sizeof(uint64_t);
+
+ if (i == r3) {
+ break;
+ }
+ }
+}
+
+void HELPER(stctl)(uint32_t r1, uint64_t a2, uint32_t r3)
+{
+ int i;
+ uint64_t dest = a2;
+
+ for (i = r1;; i = (i + 1) % 16) {
+ stl(dest, env->cregs[i]);
+ dest += sizeof(uint32_t);
+
+ if (i == r3) {
+ break;
+ }
+ }
+}
+
+uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
+{
+ /* XXX implement */
+
+ return 0;
+}
+
+/* insert storage key extended */
+uint64_t HELPER(iske)(uint64_t r2)
+{
+ uint64_t addr = get_address(0, 0, r2);
+
+ if (addr > ram_size) {
+ return 0;
+ }
+
+ /* XXX maybe use qemu's internal keys? */
+ return env->storage_keys[addr / TARGET_PAGE_SIZE];
+}
+
+/* set storage key extended */
+void HELPER(sske)(uint32_t r1, uint64_t r2)
+{
+ uint64_t addr = get_address(0, 0, r2);
+
+ if (addr > ram_size) {
+ return;
+ }
+
+ env->storage_keys[addr / TARGET_PAGE_SIZE] = r1;
+}
+
+/* reset reference bit extended */
+uint32_t HELPER(rrbe)(uint32_t r1, uint64_t r2)
+{
+ if (r2 > ram_size) {
+ return 0;
+ }
+
+ /* XXX implement */
+#if 0
+ env->storage_keys[r2 / TARGET_PAGE_SIZE] &= ~SK_REFERENCED;
+#endif
+
+ /*
+ * cc
+ *
+ * 0 Reference bit zero; change bit zero
+ * 1 Reference bit zero; change bit one
+ * 2 Reference bit one; change bit zero
+ * 3 Reference bit one; change bit one
+ */
+ return 0;
+}
+
+/* compare and swap and purge */
+uint32_t HELPER(csp)(uint32_t r1, uint32_t r2)
+{
+ uint32_t cc;
+ uint32_t o1 = env->regs[r1];
+ uint64_t a2 = get_address_31fix(r2) & ~3ULL;
+ uint32_t o2 = ldl(a2);
+
+ if (o1 == o2) {
+ stl(a2, env->regs[(r1 + 1) & 15]);
+ if (env->regs[r2] & 0x3) {
+ /* flush TLB / ALB */
+ tlb_flush(env, 1);
+ }
+ cc = 0;
+ } else {
+ env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
+ cc = 1;
+ }
+
+ return cc;
+}
+
+static uint32_t mvc_asc(int64_t l, uint64_t a1, uint64_t mode1, uint64_t a2,
+ uint64_t mode2)
+{
+ target_ulong src, dest;
+ int flags, cc = 0, i;
+
+ if (!l) {
+ return 0;
+ } else if (l > 256) {
+ /* max 256 */
+ l = 256;
+ cc = 3;
+ }
+
+ if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) {
+ cpu_loop_exit();
+ }
+ dest |= a1 & ~TARGET_PAGE_MASK;
+
+ if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) {
+ cpu_loop_exit();
+ }
+ src |= a2 & ~TARGET_PAGE_MASK;
+
+ /* XXX replace w/ memcpy */
+ for (i = 0; i < l; i++) {
+ /* XXX be more clever */
+ if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) ||
+ (((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) {
+ mvc_asc(l - i, a1 + i, mode1, a2 + i, mode2);
+ break;
+ }
+ stb_phys(dest + i, ldub_phys(src + i));
+ }
+
+ return cc;
+}
+
+uint32_t HELPER(mvcs)(uint64_t l, uint64_t a1, uint64_t a2)
+{
+ HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
+ __FUNCTION__, l, a1, a2);
+
+ return mvc_asc(l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY);
+}
+
+uint32_t HELPER(mvcp)(uint64_t l, uint64_t a1, uint64_t a2)
+{
+ HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
+ __FUNCTION__, l, a1, a2);
+
+ return mvc_asc(l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY);
+}
+
+uint32_t HELPER(sigp)(uint64_t order_code, uint32_t r1, uint64_t cpu_addr)
+{
+ int cc = 0;
+
+ HELPER_LOG("%s: %016" PRIx64 " %08x %016" PRIx64 "\n",
+ __FUNCTION__, order_code, r1, cpu_addr);
+
+ /* Remember: Use "R1 or R1+1, whichever is the odd-numbered register"
+ as parameter (input). Status (output) is always R1. */
+
+ switch (order_code) {
+ case SIGP_SET_ARCH:
+ /* switch arch */
+ break;
+ case SIGP_SENSE:
+ /* enumerate CPU status */
+ if (cpu_addr) {
+ /* XXX implement when SMP comes */
+ return 3;
+ }
+ env->regs[r1] &= 0xffffffff00000000ULL;
+ cc = 1;
+ break;
+ default:
+ /* unknown sigp */
+ fprintf(stderr, "XXX unknown sigp: 0x%" PRIx64 "\n", order_code);
+ cc = 3;
+ }
+
+ return cc;
+}
+
+void HELPER(sacf)(uint64_t a1)
+{
+ HELPER_LOG("%s: %16" PRIx64 "\n", __FUNCTION__, a1);
+
+ switch (a1 & 0xf00) {
+ case 0x000:
+ env->psw.mask &= ~PSW_MASK_ASC;
+ env->psw.mask |= PSW_ASC_PRIMARY;
+ break;
+ case 0x100:
+ env->psw.mask &= ~PSW_MASK_ASC;
+ env->psw.mask |= PSW_ASC_SECONDARY;
+ break;
+ case 0x300:
+ env->psw.mask &= ~PSW_MASK_ASC;
+ env->psw.mask |= PSW_ASC_HOME;
+ break;
+ default:
+ qemu_log("unknown sacf mode: %" PRIx64 "\n", a1);
+ program_interrupt(env, PGM_SPECIFICATION, 2);
+ break;
+ }
+}
+
+/* invalidate pte */
+void HELPER(ipte)(uint64_t pte_addr, uint64_t vaddr)
+{
+ uint64_t page = vaddr & TARGET_PAGE_MASK;
+ uint64_t pte = 0;
+
+ /* XXX broadcast to other CPUs */
+
+ /* XXX Linux is nice enough to give us the exact pte address.
+ According to spec we'd have to find it out ourselves */
+ /* XXX Linux is fine with overwriting the pte, the spec requires
+ us to only set the invalid bit */
+ stq_phys(pte_addr, pte | _PAGE_INVALID);
+
+ /* XXX we exploit the fact that Linux passes the exact virtual
+ address here - it's not obliged to! */
+ tlb_flush_page(env, page);
+}
+
+/* flush local tlb */
+void HELPER(ptlb)(void)
+{
+ tlb_flush(env, 1);
+}
+
+/* store using real address */
+void HELPER(stura)(uint64_t addr, uint32_t v1)
+{
+ stw_phys(get_address(0, 0, addr), v1);
+}
+
+/* load real address */
+uint32_t HELPER(lra)(uint64_t addr, uint32_t r1)
+{
+ uint32_t cc = 0;
+ int old_exc = env->exception_index;
+ uint64_t asc = env->psw.mask & PSW_MASK_ASC;
+ uint64_t ret;
+ int flags;
+
+ /* XXX incomplete - has more corner cases */
+ if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
+ program_interrupt(env, PGM_SPECIAL_OP, 2);
+ }
+
+ env->exception_index = old_exc;
+ if (mmu_translate(env, addr, 0, asc, &ret, &flags)) {
+ cc = 3;
+ }
+ if (env->exception_index == EXCP_PGM) {
+ ret = env->int_pgm_code | 0x80000000;
+ } else {
+ ret |= addr & ~TARGET_PAGE_MASK;
+ }
+ env->exception_index = old_exc;
+
+ if (!(env->psw.mask & PSW_MASK_64)) {
+ env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | (ret & 0xffffffffULL);
+ } else {
+ env->regs[r1] = ret;
+ }
+
+ return cc;
+}
+
+#endif
commit d5a439645a5a70fed5431318c3bce9dc2caa950f
Author: Alexander Graf <agraf at suse.de>
Date: Wed Mar 23 10:58:07 2011 +0100
s390x: helper functions for system emulation
When running system emulation, we need to transverse through the MMU and
deliver interrupts according to the specification.
This patch implements those two pieces and in addition adjusts the CPU
initialization code to account for the new fields in CPUState.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/helper.c b/target-s390x/helper.c
index 629dfd9..c79af46 100644
--- a/target-s390x/helper.c
+++ b/target-s390x/helper.c
@@ -2,6 +2,7 @@
* S/390 helpers
*
* Copyright (c) 2009 Ulrich Hecht
+ * Copyright (c) 2011 Alexander Graf
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -25,27 +26,107 @@
#include "exec-all.h"
#include "gdbstub.h"
#include "qemu-common.h"
+#include "qemu-timer.h"
+#if !defined(CONFIG_USER_ONLY)
#include <linux/kvm.h>
#include "kvm.h"
+#endif
+
+//#define DEBUG_S390
+//#define DEBUG_S390_PTE
+//#define DEBUG_S390_STDOUT
+
+#ifdef DEBUG_S390
+#ifdef DEBUG_S390_STDOUT
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); \
+ qemu_log(fmt, ##__VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { qemu_log(fmt, ## __VA_ARGS__); } while (0)
+#endif
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+#ifdef DEBUG_S390_PTE
+#define PTE_DPRINTF DPRINTF
+#else
+#define PTE_DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+#ifndef CONFIG_USER_ONLY
+static void s390x_tod_timer(void *opaque)
+{
+ CPUState *env = opaque;
+
+ env->pending_int |= INTERRUPT_TOD;
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+}
+
+static void s390x_cpu_timer(void *opaque)
+{
+ CPUState *env = opaque;
+
+ env->pending_int |= INTERRUPT_CPUTIMER;
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+}
+#endif
CPUS390XState *cpu_s390x_init(const char *cpu_model)
{
CPUS390XState *env;
+#if !defined (CONFIG_USER_ONLY)
+ struct tm tm;
+#endif
static int inited = 0;
+ static int cpu_num = 0;
env = qemu_mallocz(sizeof(CPUS390XState));
cpu_exec_init(env);
if (!inited) {
inited = 1;
+ s390x_translate_init();
}
+#if !defined(CONFIG_USER_ONLY)
+ qemu_get_timedate(&tm, 0);
+ env->tod_offset = TOD_UNIX_EPOCH +
+ (time2tod(mktimegm(&tm)) * 1000000000ULL);
+ env->tod_basetime = 0;
+ env->tod_timer = qemu_new_timer_ns(vm_clock, s390x_tod_timer, env);
+ env->cpu_timer = qemu_new_timer_ns(vm_clock, s390x_cpu_timer, env);
+#endif
env->cpu_model_str = cpu_model;
+ env->cpu_num = cpu_num++;
+ env->ext_index = -1;
cpu_reset(env);
qemu_init_vcpu(env);
return env;
}
+#if defined(CONFIG_USER_ONLY)
+
+void do_interrupt (CPUState *env)
+{
+ env->exception_index = -1;
+}
+
+int cpu_s390x_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ /* fprintf(stderr,"%s: address 0x%lx rw %d mmu_idx %d is_softmmu %d\n",
+ __FUNCTION__, address, rw, mmu_idx, is_softmmu); */
+ env->exception_index = EXCP_ADDR;
+ env->__excp_addr = address; /* FIXME: find out how this works on a real machine */
+ return 1;
+}
+
+#endif /* CONFIG_USER_ONLY */
+
void cpu_reset(CPUS390XState *env)
{
if (qemu_loglevel_mask(CPU_LOG_RESET)) {
@@ -58,31 +139,495 @@ void cpu_reset(CPUS390XState *env)
tlb_flush(env, 1);
}
-target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+#ifndef CONFIG_USER_ONLY
+
+/* Ensure to exit the TB after this call! */
+static void trigger_pgm_exception(CPUState *env, uint32_t code, uint32_t ilc)
+{
+ env->exception_index = EXCP_PGM;
+ env->int_pgm_code = code;
+ env->int_pgm_ilc = ilc;
+}
+
+static int trans_bits(CPUState *env, uint64_t mode)
+{
+ int bits = 0;
+
+ switch (mode) {
+ case PSW_ASC_PRIMARY:
+ bits = 1;
+ break;
+ case PSW_ASC_SECONDARY:
+ bits = 2;
+ break;
+ case PSW_ASC_HOME:
+ bits = 3;
+ break;
+ default:
+ cpu_abort(env, "unknown asc mode\n");
+ break;
+ }
+
+ return bits;
+}
+
+static void trigger_prot_fault(CPUState *env, target_ulong vaddr, uint64_t mode)
+{
+ int ilc = ILC_LATER_INC_2;
+ int bits = trans_bits(env, mode) | 4;
+
+ DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __FUNCTION__, vaddr, bits);
+
+ stq_phys(env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits);
+ trigger_pgm_exception(env, PGM_PROTECTION, ilc);
+}
+
+static void trigger_page_fault(CPUState *env, target_ulong vaddr, uint32_t type,
+ uint64_t asc, int rw)
+{
+ int ilc = ILC_LATER;
+ int bits = trans_bits(env, asc);
+
+ if (rw == 2) {
+ /* code has is undefined ilc */
+ ilc = 2;
+ }
+
+ DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __FUNCTION__, vaddr, bits);
+
+ stq_phys(env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits);
+ trigger_pgm_exception(env, type, ilc);
+}
+
+static int mmu_translate_asce(CPUState *env, target_ulong vaddr, uint64_t asc,
+ uint64_t asce, int level, target_ulong *raddr,
+ int *flags, int rw)
{
+ uint64_t offs = 0;
+ uint64_t origin;
+ uint64_t new_asce;
+
+ PTE_DPRINTF("%s: 0x%" PRIx64 "\n", __FUNCTION__, asce);
+
+ if (((level != _ASCE_TYPE_SEGMENT) && (asce & _REGION_ENTRY_INV)) ||
+ ((level == _ASCE_TYPE_SEGMENT) && (asce & _SEGMENT_ENTRY_INV))) {
+ /* XXX different regions have different faults */
+ DPRINTF("%s: invalid region\n", __FUNCTION__);
+ trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw);
+ return -1;
+ }
+
+ if ((level <= _ASCE_TYPE_MASK) && ((asce & _ASCE_TYPE_MASK) != level)) {
+ trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
+ return -1;
+ }
+
+ if (asce & _ASCE_REAL_SPACE) {
+ /* direct mapping */
+
+ *raddr = vaddr;
+ return 0;
+ }
+
+ origin = asce & _ASCE_ORIGIN;
+
+ switch (level) {
+ case _ASCE_TYPE_REGION1 + 4:
+ offs = (vaddr >> 50) & 0x3ff8;
+ break;
+ case _ASCE_TYPE_REGION1:
+ offs = (vaddr >> 39) & 0x3ff8;
+ break;
+ case _ASCE_TYPE_REGION2:
+ offs = (vaddr >> 28) & 0x3ff8;
+ break;
+ case _ASCE_TYPE_REGION3:
+ offs = (vaddr >> 17) & 0x3ff8;
+ break;
+ case _ASCE_TYPE_SEGMENT:
+ offs = (vaddr >> 9) & 0x07f8;
+ origin = asce & _SEGMENT_ENTRY_ORIGIN;
+ break;
+ }
+
+ /* XXX region protection flags */
+ /* *flags &= ~PAGE_WRITE */
+
+ new_asce = ldq_phys(origin + offs);
+ PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n",
+ __FUNCTION__, origin, offs, new_asce);
+
+ if (level != _ASCE_TYPE_SEGMENT) {
+ /* yet another region */
+ return mmu_translate_asce(env, vaddr, asc, new_asce, level - 4, raddr,
+ flags, rw);
+ }
+
+ /* PTE */
+ if (new_asce & _PAGE_INVALID) {
+ DPRINTF("%s: PTE=0x%" PRIx64 " invalid\n", __FUNCTION__, new_asce);
+ trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw);
+ return -1;
+ }
+
+ if (new_asce & _PAGE_RO) {
+ *flags &= ~PAGE_WRITE;
+ }
+
+ *raddr = new_asce & _ASCE_ORIGIN;
+
+ PTE_DPRINTF("%s: PTE=0x%" PRIx64 "\n", __FUNCTION__, new_asce);
+
return 0;
}
-#ifndef CONFIG_USER_ONLY
+static int mmu_translate_asc(CPUState *env, target_ulong vaddr, uint64_t asc,
+ target_ulong *raddr, int *flags, int rw)
+{
+ uint64_t asce = 0;
+ int level, new_level;
+ int r;
-int cpu_s390x_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ switch (asc) {
+ case PSW_ASC_PRIMARY:
+ PTE_DPRINTF("%s: asc=primary\n", __FUNCTION__);
+ asce = env->cregs[1];
+ break;
+ case PSW_ASC_SECONDARY:
+ PTE_DPRINTF("%s: asc=secondary\n", __FUNCTION__);
+ asce = env->cregs[7];
+ break;
+ case PSW_ASC_HOME:
+ PTE_DPRINTF("%s: asc=home\n", __FUNCTION__);
+ asce = env->cregs[13];
+ break;
+ }
+
+ switch (asce & _ASCE_TYPE_MASK) {
+ case _ASCE_TYPE_REGION1:
+ break;
+ case _ASCE_TYPE_REGION2:
+ if (vaddr & 0xffe0000000000000ULL) {
+ DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
+ " 0xffe0000000000000ULL\n", __FUNCTION__,
+ vaddr);
+ trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
+ return -1;
+ }
+ break;
+ case _ASCE_TYPE_REGION3:
+ if (vaddr & 0xfffffc0000000000ULL) {
+ DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
+ " 0xfffffc0000000000ULL\n", __FUNCTION__,
+ vaddr);
+ trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
+ return -1;
+ }
+ break;
+ case _ASCE_TYPE_SEGMENT:
+ if (vaddr & 0xffffffff80000000ULL) {
+ DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
+ " 0xffffffff80000000ULL\n", __FUNCTION__,
+ vaddr);
+ trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw);
+ return -1;
+ }
+ break;
+ }
+
+ /* fake level above current */
+ level = asce & _ASCE_TYPE_MASK;
+ new_level = level + 4;
+ asce = (asce & ~_ASCE_TYPE_MASK) | (new_level & _ASCE_TYPE_MASK);
+
+ r = mmu_translate_asce(env, vaddr, asc, asce, new_level, raddr, flags, rw);
+
+ if ((rw == 1) && !(*flags & PAGE_WRITE)) {
+ trigger_prot_fault(env, vaddr, asc);
+ return -1;
+ }
+
+ return r;
+}
+
+int mmu_translate(CPUState *env, target_ulong vaddr, int rw, uint64_t asc,
+ target_ulong *raddr, int *flags)
+{
+ int r = -1;
+
+ *flags = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ vaddr &= TARGET_PAGE_MASK;
+
+ if (!(env->psw.mask & PSW_MASK_DAT)) {
+ *raddr = vaddr;
+ r = 0;
+ goto out;
+ }
+
+ switch (asc) {
+ case PSW_ASC_PRIMARY:
+ case PSW_ASC_HOME:
+ r = mmu_translate_asc(env, vaddr, asc, raddr, flags, rw);
+ break;
+ case PSW_ASC_SECONDARY:
+ /*
+ * Instruction: Primary
+ * Data: Secondary
+ */
+ if (rw == 2) {
+ r = mmu_translate_asc(env, vaddr, PSW_ASC_PRIMARY, raddr, flags,
+ rw);
+ *flags &= ~(PAGE_READ | PAGE_WRITE);
+ } else {
+ r = mmu_translate_asc(env, vaddr, PSW_ASC_SECONDARY, raddr, flags,
+ rw);
+ *flags &= ~(PAGE_EXEC);
+ }
+ break;
+ case PSW_ASC_ACCREG:
+ default:
+ hw_error("guest switched to unknown asc mode\n");
+ break;
+ }
+
+out:
+ /* Convert real address -> absolute address */
+ if (*raddr < 0x2000) {
+ *raddr = *raddr + env->psa;
+ }
+
+ return r;
+}
+
+int cpu_s390x_handle_mmu_fault (CPUState *env, target_ulong _vaddr, int rw,
int mmu_idx, int is_softmmu)
{
- target_ulong phys;
+ uint64_t asc = env->psw.mask & PSW_MASK_ASC;
+ target_ulong vaddr, raddr;
int prot;
- /* XXX: implement mmu */
+ DPRINTF("%s: address 0x%" PRIx64 " rw %d mmu_idx %d is_softmmu %d\n",
+ __FUNCTION__, _vaddr, rw, mmu_idx, is_softmmu);
+
+ _vaddr &= TARGET_PAGE_MASK;
+ vaddr = _vaddr;
+
+ /* 31-Bit mode */
+ if (!(env->psw.mask & PSW_MASK_64)) {
+ vaddr &= 0x7fffffff;
+ }
+
+ if (mmu_translate(env, vaddr, rw, asc, &raddr, &prot)) {
+ /* Translation ended in exception */
+ return 1;
+ }
- phys = address;
- prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ /* check out of RAM access */
+ if (raddr > (ram_size + virtio_size)) {
+ DPRINTF("%s: aaddr %" PRIx64 " > ram_size %" PRIx64 "\n", __FUNCTION__,
+ (uint64_t)aaddr, (uint64_t)ram_size);
+ trigger_pgm_exception(env, PGM_ADDRESSING, ILC_LATER);
+ return 1;
+ }
- tlb_set_page(env, address & TARGET_PAGE_MASK,
- phys & TARGET_PAGE_MASK, prot,
+ DPRINTF("%s: set tlb %" PRIx64 " -> %" PRIx64 " (%x)\n", __FUNCTION__,
+ (uint64_t)vaddr, (uint64_t)raddr, prot);
+
+ tlb_set_page(env, _vaddr, raddr, prot,
mmu_idx, TARGET_PAGE_SIZE);
+
return 0;
}
-#endif /* CONFIG_USER_ONLY */
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong vaddr)
+{
+ target_ulong raddr;
+ int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ int old_exc = env->exception_index;
+ uint64_t asc = env->psw.mask & PSW_MASK_ASC;
+
+ /* 31-Bit mode */
+ if (!(env->psw.mask & PSW_MASK_64)) {
+ vaddr &= 0x7fffffff;
+ }
+
+ mmu_translate(env, vaddr, 2, asc, &raddr, &prot);
+ env->exception_index = old_exc;
+
+ return raddr;
+}
+
+void load_psw(CPUState *env, uint64_t mask, uint64_t addr)
+{
+ if (mask & PSW_MASK_WAIT) {
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ if (!(mask & (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK))) {
+ /* XXX disabled wait state - CPU is dead */
+ }
+ }
+
+ env->psw.addr = addr;
+ env->psw.mask = mask;
+ env->cc_op = (mask >> 13) & 3;
+}
+
+static uint64_t get_psw_mask(CPUState *env)
+{
+ uint64_t r = env->psw.mask;
+
+ env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, env->cc_vr);
+
+ r &= ~(3ULL << 13);
+ assert(!(env->cc_op & ~3));
+ r |= env->cc_op << 13;
+
+ return r;
+}
+
+static void do_svc_interrupt(CPUState *env)
+{
+ uint64_t mask, addr;
+ LowCore *lowcore;
+ target_phys_addr_t len = TARGET_PAGE_SIZE;
+
+ lowcore = cpu_physical_memory_map(env->psa, &len, 1);
+
+ lowcore->svc_code = cpu_to_be16(env->int_svc_code);
+ lowcore->svc_ilc = cpu_to_be16(env->int_svc_ilc);
+ lowcore->svc_old_psw.mask = cpu_to_be64(get_psw_mask(env));
+ lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + (env->int_svc_ilc));
+ mask = be64_to_cpu(lowcore->svc_new_psw.mask);
+ addr = be64_to_cpu(lowcore->svc_new_psw.addr);
+
+ cpu_physical_memory_unmap(lowcore, len, 1, len);
+
+ load_psw(env, mask, addr);
+}
+
+static void do_program_interrupt(CPUState *env)
+{
+ uint64_t mask, addr;
+ LowCore *lowcore;
+ target_phys_addr_t len = TARGET_PAGE_SIZE;
+ int ilc = env->int_pgm_ilc;
+
+ switch (ilc) {
+ case ILC_LATER:
+ ilc = get_ilc(ldub_code(env->psw.addr));
+ break;
+ case ILC_LATER_INC:
+ ilc = get_ilc(ldub_code(env->psw.addr));
+ env->psw.addr += ilc * 2;
+ break;
+ case ILC_LATER_INC_2:
+ ilc = get_ilc(ldub_code(env->psw.addr)) * 2;
+ env->psw.addr += ilc;
+ break;
+ }
+
+ qemu_log("%s: code=0x%x ilc=%d\n", __FUNCTION__, env->int_pgm_code, ilc);
+
+ lowcore = cpu_physical_memory_map(env->psa, &len, 1);
+
+ lowcore->pgm_ilc = cpu_to_be16(ilc);
+ lowcore->pgm_code = cpu_to_be16(env->int_pgm_code);
+ lowcore->program_old_psw.mask = cpu_to_be64(get_psw_mask(env));
+ lowcore->program_old_psw.addr = cpu_to_be64(env->psw.addr);
+ mask = be64_to_cpu(lowcore->program_new_psw.mask);
+ addr = be64_to_cpu(lowcore->program_new_psw.addr);
+
+ cpu_physical_memory_unmap(lowcore, len, 1, len);
+
+ DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __FUNCTION__,
+ env->int_pgm_code, ilc, env->psw.mask,
+ env->psw.addr);
+
+ load_psw(env, mask, addr);
+}
+
+#define VIRTIO_SUBCODE_64 0x0D00
+
+static void do_ext_interrupt(CPUState *env)
+{
+ uint64_t mask, addr;
+ LowCore *lowcore;
+ target_phys_addr_t len = TARGET_PAGE_SIZE;
+ ExtQueue *q;
+
+ if (!(env->psw.mask & PSW_MASK_EXT)) {
+ cpu_abort(env, "Ext int w/o ext mask\n");
+ }
+
+ if (env->ext_index < 0 || env->ext_index > MAX_EXT_QUEUE) {
+ cpu_abort(env, "Ext queue overrun: %d\n", env->ext_index);
+ }
+
+ q = &env->ext_queue[env->ext_index];
+ lowcore = cpu_physical_memory_map(env->psa, &len, 1);
+
+ lowcore->ext_int_code = cpu_to_be16(q->code);
+ lowcore->ext_params = cpu_to_be32(q->param);
+ lowcore->ext_params2 = cpu_to_be64(q->param64);
+ lowcore->external_old_psw.mask = cpu_to_be64(get_psw_mask(env));
+ lowcore->external_old_psw.addr = cpu_to_be64(env->psw.addr);
+ lowcore->cpu_addr = cpu_to_be16(env->cpu_num | VIRTIO_SUBCODE_64);
+ mask = be64_to_cpu(lowcore->external_new_psw.mask);
+ addr = be64_to_cpu(lowcore->external_new_psw.addr);
+
+ cpu_physical_memory_unmap(lowcore, len, 1, len);
+
+ env->ext_index--;
+ if (env->ext_index == -1) {
+ env->pending_int &= ~INTERRUPT_EXT;
+ }
+
+ DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __FUNCTION__,
+ env->psw.mask, env->psw.addr);
+
+ load_psw(env, mask, addr);
+}
void do_interrupt (CPUState *env)
{
+ qemu_log("%s: %d at pc=%" PRIx64 "\n", __FUNCTION__, env->exception_index,
+ env->psw.addr);
+
+ /* handle external interrupts */
+ if ((env->psw.mask & PSW_MASK_EXT) &&
+ env->exception_index == -1) {
+ if (env->pending_int & INTERRUPT_EXT) {
+ /* code is already in env */
+ env->exception_index = EXCP_EXT;
+ } else if (env->pending_int & INTERRUPT_TOD) {
+ cpu_inject_ext(env, 0x1004, 0, 0);
+ env->exception_index = EXCP_EXT;
+ env->pending_int &= ~INTERRUPT_EXT;
+ env->pending_int &= ~INTERRUPT_TOD;
+ } else if (env->pending_int & INTERRUPT_CPUTIMER) {
+ cpu_inject_ext(env, 0x1005, 0, 0);
+ env->exception_index = EXCP_EXT;
+ env->pending_int &= ~INTERRUPT_EXT;
+ env->pending_int &= ~INTERRUPT_TOD;
+ }
+ }
+
+ switch (env->exception_index) {
+ case EXCP_PGM:
+ do_program_interrupt(env);
+ break;
+ case EXCP_SVC:
+ do_svc_interrupt(env);
+ break;
+ case EXCP_EXT:
+ do_ext_interrupt(env);
+ break;
+ }
+ env->exception_index = -1;
+
+ if (!env->pending_int) {
+ env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+ }
}
+
+#endif /* CONFIG_USER_ONLY */
diff --git a/target-s390x/op_helper.c b/target-s390x/op_helper.c
index be455b9..7f0adcb 100644
--- a/target-s390x/op_helper.c
+++ b/target-s390x/op_helper.c
@@ -71,3 +71,9 @@ void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
}
#endif
+
+uint32_t calc_cc(CPUState *env, uint32_t cc_op, uint64_t src, uint64_t dst,
+ uint64_t vr)
+{
+ return 0;
+}
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index 4d45e32..f995384 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -46,6 +46,10 @@ void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
cpu_fprintf(f, "PSW=mask %016lx addr %016lx cc %02x\n", env->psw.mask, env->psw.addr, env->cc);
}
+void s390x_translate_init(void)
+{
+}
+
void gen_intermediate_code (CPUState *env, struct TranslationBlock *tb)
{
}
commit 4e8367812a053bf169fc82bcddd68c1406751dea
Author: Alexander Graf <agraf at suse.de>
Date: Mon May 2 10:11:40 2011 +0200
s390x: Shift variables in CPUState for memset(0)
The default reset handler does a memset(0) until right in between CPU_COMMON.
I incorrectly changed that behavior on the s390x port, so let's move the fields
in CPUState around to reflect the correct split up to which point memset(0)
zeros out everything.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 00939a3..125b939 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -87,9 +87,12 @@ typedef struct CPUS390XState {
int pending_int;
ExtQueue ext_queue[MAX_EXT_QUEUE];
+ int ext_index;
+
+ CPU_COMMON
+
/* reset does memset(0) up to here */
- int ext_index;
int cpu_num;
uint8_t *storage_keys;
@@ -98,8 +101,6 @@ typedef struct CPUS390XState {
QEMUTimer *tod_timer;
QEMUTimer *cpu_timer;
-
- CPU_COMMON
} CPUS390XState;
#if defined(CONFIG_USER_ONLY)
commit d1ff903ca592420b196ce261428e035b69aa470f
Author: Alexander Graf <agraf at suse.de>
Date: Wed Apr 13 10:55:11 2011 +0200
s390x: keep hint on virtio managing size
The s390x virtio bus keeps management information on virtio after the top
of the guest's RAM. We need to be able to tell the guest the size of its
RAM (without virtio stuff), but also be able to trap when the guest accesses
RAM outside of its scope (including virtio stuff).
So we need a variable telling us the size of the virtio stuff, so we can
calculate the highest available RAM address from that.
While at it, also increase the maximum number of virtio pages, so we play
along well with more recent kernels that spawn a ridiculous number of virtio
console adapters.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c
index bb49e39..d4a12f7 100644
--- a/hw/s390-virtio-bus.c
+++ b/hw/s390-virtio-bus.c
@@ -60,6 +60,9 @@ static const VirtIOBindings virtio_s390_bindings;
static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev);
+/* length of VirtIO device pages */
+const target_phys_addr_t virtio_size = S390_DEVICE_PAGES * TARGET_PAGE_SIZE;
+
VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size)
{
VirtIOS390Bus *bus;
diff --git a/hw/s390-virtio-bus.h b/hw/s390-virtio-bus.h
index edf6d04..0c412d0 100644
--- a/hw/s390-virtio-bus.h
+++ b/hw/s390-virtio-bus.h
@@ -33,7 +33,7 @@
#define VIRTIO_VQCONFIG_LEN 24
#define VIRTIO_RING_LEN (TARGET_PAGE_SIZE * 3)
-#define S390_DEVICE_PAGES 256
+#define S390_DEVICE_PAGES 512
typedef struct VirtIOS390Device {
DeviceState qdev;
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 79aa6c9..00939a3 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -310,6 +310,9 @@ static inline void kvm_s390_interrupt_internal(CPUState *env, int type,
#endif
CPUState *s390_cpu_addr2state(uint16_t cpu_addr);
+/* from s390-virtio-bus */
+extern const target_phys_addr_t virtio_size;
+
#ifndef KVM_S390_SIGP_STOP
#define KVM_S390_SIGP_STOP 0
#define KVM_S390_PROGRAM_INT 0
commit 1f20626679964730f991a9faeb5d9438c2827465
Author: Alexander Graf <agraf at suse.de>
Date: Fri Apr 15 15:16:40 2011 +0200
s390x: make kvm exported functions conditional on kvm
We have some helper functions we use to directly invoke KVM
functionality from device emulation code.
This patch replaces those exported functions with static inline
stubs when not building with KVM enabled.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index a84b3ee..79aa6c9 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -287,10 +287,27 @@ int cpu_s390x_handle_mmu_fault (CPUS390XState *env, target_ulong address, int rw
#ifndef CONFIG_USER_ONLY
int s390_virtio_hypercall(CPUState *env, uint64_t mem, uint64_t hypercall);
+#ifdef CONFIG_KVM
void kvm_s390_interrupt(CPUState *env, int type, uint32_t code);
void kvm_s390_virtio_irq(CPUState *env, int config_change, uint64_t token);
void kvm_s390_interrupt_internal(CPUState *env, int type, uint32_t parm,
uint64_t parm64, int vm);
+#else
+static inline void kvm_s390_interrupt(CPUState *env, int type, uint32_t code)
+{
+}
+
+static inline void kvm_s390_virtio_irq(CPUState *env, int config_change,
+ uint64_t token)
+{
+}
+
+static inline void kvm_s390_interrupt_internal(CPUState *env, int type,
+ uint32_t parm, uint64_t parm64,
+ int vm)
+{
+}
+#endif
CPUState *s390_cpu_addr2state(uint16_t cpu_addr);
#ifndef KVM_S390_SIGP_STOP
commit a4c075f178a3a2c976667389f19ce7dbabaf9712
Author: Ulrich Hecht <uli at suse.de>
Date: Fri Jul 24 16:57:31 2009 +0200
s390x: s390x-linux-user support
This patch adds support for running s390x binaries in the linux-user emulation
code.
Signed-off-by: Ulrich Hecht <uli at suse.de>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 4c399f8..dcfeb7a 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -867,6 +867,25 @@ static inline void init_thread(struct target_pt_regs *regs,
#endif /* TARGET_ALPHA */
+#ifdef TARGET_S390X
+
+#define ELF_START_MMAP (0x20000000000ULL)
+
+#define elf_check_arch(x) ( (x) == ELF_ARCH )
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_DATA ELFDATA2MSB
+#define ELF_ARCH EM_S390
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ regs->psw.addr = infop->entry;
+ regs->psw.mask = PSW_MASK_64 | PSW_MASK_32;
+ regs->gprs[15] = infop->start_stack;
+}
+
+#endif /* TARGET_S390X */
+
#ifndef ELF_PLATFORM
#define ELF_PLATFORM (NULL)
#endif
diff --git a/linux-user/main.c b/linux-user/main.c
index a4996e7..98010a1 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -2701,6 +2701,80 @@ void cpu_loop (CPUState *env)
}
#endif /* TARGET_ALPHA */
+#ifdef TARGET_S390X
+void cpu_loop(CPUS390XState *env)
+{
+ int trapnr;
+ target_siginfo_t info;
+
+ while (1) {
+ trapnr = cpu_s390x_exec (env);
+
+ switch (trapnr) {
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ case EXCP_DEBUG:
+ {
+ int sig;
+
+ sig = gdb_handlesig (env, TARGET_SIGTRAP);
+ if (sig) {
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ queue_signal(env, info.si_signo, &info);
+ }
+ }
+ break;
+ case EXCP_SVC:
+ {
+ int n = env->int_svc_code;
+ if (!n) {
+ /* syscalls > 255 */
+ n = env->regs[1];
+ }
+ env->psw.addr += env->int_svc_ilc;
+ env->regs[2] = do_syscall(env, n,
+ env->regs[2],
+ env->regs[3],
+ env->regs[4],
+ env->regs[5],
+ env->regs[6],
+ env->regs[7]);
+ }
+ break;
+ case EXCP_ADDR:
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->__excp_addr;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ case EXCP_SPEC:
+ {
+ fprintf(stderr,"specification exception insn 0x%08x%04x\n", ldl(env->psw.addr), lduw(env->psw.addr + 4));
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_ILLOPC;
+ info._sifields._sigfault._addr = env->__excp_addr;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ default:
+ printf ("Unhandled trap: 0x%x\n", trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ exit (1);
+ }
+ process_pending_signals (env);
+ }
+}
+
+#endif /* TARGET_S390X */
+
static void version(void)
{
printf("qemu-" TARGET_ARCH " version " QEMU_VERSION QEMU_PKGVERSION
@@ -3450,6 +3524,15 @@ int main(int argc, char **argv, char **envp)
env->regs[15] = regs->acr;
env->pc = regs->erp;
}
+#elif defined(TARGET_S390X)
+ {
+ int i;
+ for (i = 0; i < 16; i++) {
+ env->regs[i] = regs->gprs[i];
+ }
+ env->psw.mask = regs->psw.mask;
+ env->psw.addr = regs->psw.addr;
+ }
#else
#error unsupported target CPU
#endif
diff --git a/linux-user/s390x/syscall.h b/linux-user/s390x/syscall.h
new file mode 100644
index 0000000..c2ea151
--- /dev/null
+++ b/linux-user/s390x/syscall.h
@@ -0,0 +1,23 @@
+/* this typedef defines how a Program Status Word looks like */
+typedef struct {
+ abi_ulong mask;
+ abi_ulong addr;
+} __attribute__ ((aligned(8))) target_psw_t;
+
+/*
+ * The pt_regs struct defines the way the registers are stored on
+ * the stack during a system call.
+ */
+
+#define TARGET_NUM_GPRS 16
+
+struct target_pt_regs {
+ abi_ulong args[1];
+ target_psw_t psw;
+ abi_ulong gprs[TARGET_NUM_GPRS];
+ abi_ulong orig_gpr2;
+ unsigned short ilc;
+ unsigned short trap;
+};
+
+#define UNAME_MACHINE "s390x"
diff --git a/linux-user/s390x/syscall_nr.h b/linux-user/s390x/syscall_nr.h
new file mode 100644
index 0000000..7cc6db2
--- /dev/null
+++ b/linux-user/s390x/syscall_nr.h
@@ -0,0 +1,349 @@
+/*
+ * This file contains the system call numbers.
+ */
+
+#define TARGET_NR_exit 1
+#define TARGET_NR_fork 2
+#define TARGET_NR_read 3
+#define TARGET_NR_write 4
+#define TARGET_NR_open 5
+#define TARGET_NR_close 6
+#define TARGET_NR_restart_syscall 7
+#define TARGET_NR_creat 8
+#define TARGET_NR_link 9
+#define TARGET_NR_unlink 10
+#define TARGET_NR_execve 11
+#define TARGET_NR_chdir 12
+#define TARGET_NR_mknod 14
+#define TARGET_NR_chmod 15
+#define TARGET_NR_lseek 19
+#define TARGET_NR_getpid 20
+#define TARGET_NR_mount 21
+#define TARGET_NR_umount 22
+#define TARGET_NR_ptrace 26
+#define TARGET_NR_alarm 27
+#define TARGET_NR_pause 29
+#define TARGET_NR_utime 30
+#define TARGET_NR_access 33
+#define TARGET_NR_nice 34
+#define TARGET_NR_sync 36
+#define TARGET_NR_kill 37
+#define TARGET_NR_rename 38
+#define TARGET_NR_mkdir 39
+#define TARGET_NR_rmdir 40
+#define TARGET_NR_dup 41
+#define TARGET_NR_pipe 42
+#define TARGET_NR_times 43
+#define TARGET_NR_brk 45
+#define TARGET_NR_signal 48
+#define TARGET_NR_acct 51
+#define TARGET_NR_umount2 52
+#define TARGET_NR_ioctl 54
+#define TARGET_NR_fcntl 55
+#define TARGET_NR_setpgid 57
+#define TARGET_NR_umask 60
+#define TARGET_NR_chroot 61
+#define TARGET_NR_ustat 62
+#define TARGET_NR_dup2 63
+#define TARGET_NR_getppid 64
+#define TARGET_NR_getpgrp 65
+#define TARGET_NR_setsid 66
+#define TARGET_NR_sigaction 67
+#define TARGET_NR_sigsuspend 72
+#define TARGET_NR_sigpending 73
+#define TARGET_NR_sethostname 74
+#define TARGET_NR_setrlimit 75
+#define TARGET_NR_getrusage 77
+#define TARGET_NR_gettimeofday 78
+#define TARGET_NR_settimeofday 79
+#define TARGET_NR_symlink 83
+#define TARGET_NR_readlink 85
+#define TARGET_NR_uselib 86
+#define TARGET_NR_swapon 87
+#define TARGET_NR_reboot 88
+#define TARGET_NR_readdir 89
+#define TARGET_NR_mmap 90
+#define TARGET_NR_munmap 91
+#define TARGET_NR_truncate 92
+#define TARGET_NR_ftruncate 93
+#define TARGET_NR_fchmod 94
+#define TARGET_NR_getpriority 96
+#define TARGET_NR_setpriority 97
+#define TARGET_NR_statfs 99
+#define TARGET_NR_fstatfs 100
+#define TARGET_NR_socketcall 102
+#define TARGET_NR_syslog 103
+#define TARGET_NR_setitimer 104
+#define TARGET_NR_getitimer 105
+#define TARGET_NR_stat 106
+#define TARGET_NR_lstat 107
+#define TARGET_NR_fstat 108
+#define TARGET_NR_lookup_dcookie 110
+#define TARGET_NR_vhangup 111
+#define TARGET_NR_idle 112
+#define TARGET_NR_wait4 114
+#define TARGET_NR_swapoff 115
+#define TARGET_NR_sysinfo 116
+#define TARGET_NR_ipc 117
+#define TARGET_NR_fsync 118
+#define TARGET_NR_sigreturn 119
+#define TARGET_NR_clone 120
+#define TARGET_NR_setdomainname 121
+#define TARGET_NR_uname 122
+#define TARGET_NR_adjtimex 124
+#define TARGET_NR_mprotect 125
+#define TARGET_NR_sigprocmask 126
+#define TARGET_NR_create_module 127
+#define TARGET_NR_init_module 128
+#define TARGET_NR_delete_module 129
+#define TARGET_NR_get_kernel_syms 130
+#define TARGET_NR_quotactl 131
+#define TARGET_NR_getpgid 132
+#define TARGET_NR_fchdir 133
+#define TARGET_NR_bdflush 134
+#define TARGET_NR_sysfs 135
+#define TARGET_NR_personality 136
+#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */
+#define TARGET_NR_getdents 141
+#define TARGET_NR_flock 143
+#define TARGET_NR_msync 144
+#define TARGET_NR_readv 145
+#define TARGET_NR_writev 146
+#define TARGET_NR_getsid 147
+#define TARGET_NR_fdatasync 148
+#define TARGET_NR__sysctl 149
+#define TARGET_NR_mlock 150
+#define TARGET_NR_munlock 151
+#define TARGET_NR_mlockall 152
+#define TARGET_NR_munlockall 153
+#define TARGET_NR_sched_setparam 154
+#define TARGET_NR_sched_getparam 155
+#define TARGET_NR_sched_setscheduler 156
+#define TARGET_NR_sched_getscheduler 157
+#define TARGET_NR_sched_yield 158
+#define TARGET_NR_sched_get_priority_max 159
+#define TARGET_NR_sched_get_priority_min 160
+#define TARGET_NR_sched_rr_get_interval 161
+#define TARGET_NR_nanosleep 162
+#define TARGET_NR_mremap 163
+#define TARGET_NR_query_module 167
+#define TARGET_NR_poll 168
+#define TARGET_NR_nfsservctl 169
+#define TARGET_NR_prctl 172
+#define TARGET_NR_rt_sigreturn 173
+#define TARGET_NR_rt_sigaction 174
+#define TARGET_NR_rt_sigprocmask 175
+#define TARGET_NR_rt_sigpending 176
+#define TARGET_NR_rt_sigtimedwait 177
+#define TARGET_NR_rt_sigqueueinfo 178
+#define TARGET_NR_rt_sigsuspend 179
+#define TARGET_NR_pread64 180
+#define TARGET_NR_pwrite64 181
+#define TARGET_NR_getcwd 183
+#define TARGET_NR_capget 184
+#define TARGET_NR_capset 185
+#define TARGET_NR_sigaltstack 186
+#define TARGET_NR_sendfile 187
+#define TARGET_NR_getpmsg 188
+#define TARGET_NR_putpmsg 189
+#define TARGET_NR_vfork 190
+#define TARGET_NR_pivot_root 217
+#define TARGET_NR_mincore 218
+#define TARGET_NR_madvise 219
+#define TARGET_NR_getdents64 220
+#define TARGET_NR_readahead 222
+#define TARGET_NR_setxattr 224
+#define TARGET_NR_lsetxattr 225
+#define TARGET_NR_fsetxattr 226
+#define TARGET_NR_getxattr 227
+#define TARGET_NR_lgetxattr 228
+#define TARGET_NR_fgetxattr 229
+#define TARGET_NR_listxattr 230
+#define TARGET_NR_llistxattr 231
+#define TARGET_NR_flistxattr 232
+#define TARGET_NR_removexattr 233
+#define TARGET_NR_lremovexattr 234
+#define TARGET_NR_fremovexattr 235
+#define TARGET_NR_gettid 236
+#define TARGET_NR_tkill 237
+#define TARGET_NR_futex 238
+#define TARGET_NR_sched_setaffinity 239
+#define TARGET_NR_sched_getaffinity 240
+#define TARGET_NR_tgkill 241
+/* Number 242 is reserved for tux */
+#define TARGET_NR_io_setup 243
+#define TARGET_NR_io_destroy 244
+#define TARGET_NR_io_getevents 245
+#define TARGET_NR_io_submit 246
+#define TARGET_NR_io_cancel 247
+#define TARGET_NR_exit_group 248
+#define TARGET_NR_epoll_create 249
+#define TARGET_NR_epoll_ctl 250
+#define TARGET_NR_epoll_wait 251
+#define TARGET_NR_set_tid_address 252
+#define TARGET_NR_fadvise64 253
+#define TARGET_NR_timer_create 254
+#define TARGET_NR_timer_settime (TARGET_NR_timer_create+1)
+#define TARGET_NR_timer_gettime (TARGET_NR_timer_create+2)
+#define TARGET_NR_timer_getoverrun (TARGET_NR_timer_create+3)
+#define TARGET_NR_timer_delete (TARGET_NR_timer_create+4)
+#define TARGET_NR_clock_settime (TARGET_NR_timer_create+5)
+#define TARGET_NR_clock_gettime (TARGET_NR_timer_create+6)
+#define TARGET_NR_clock_getres (TARGET_NR_timer_create+7)
+#define TARGET_NR_clock_nanosleep (TARGET_NR_timer_create+8)
+/* Number 263 is reserved for vserver */
+#define TARGET_NR_statfs64 265
+#define TARGET_NR_fstatfs64 266
+#define TARGET_NR_remap_file_pages 267
+/* Number 268 is reserved for new sys_mbind */
+/* Number 269 is reserved for new sys_get_mempolicy */
+/* Number 270 is reserved for new sys_set_mempolicy */
+#define TARGET_NR_mq_open 271
+#define TARGET_NR_mq_unlink 272
+#define TARGET_NR_mq_timedsend 273
+#define TARGET_NR_mq_timedreceive 274
+#define TARGET_NR_mq_notify 275
+#define TARGET_NR_mq_getsetattr 276
+#define TARGET_NR_kexec_load 277
+#define TARGET_NR_add_key 278
+#define TARGET_NR_request_key 279
+#define TARGET_NR_keyctl 280
+#define TARGET_NR_waitid 281
+#define TARGET_NR_ioprio_set 282
+#define TARGET_NR_ioprio_get 283
+#define TARGET_NR_inotify_init 284
+#define TARGET_NR_inotify_add_watch 285
+#define TARGET_NR_inotify_rm_watch 286
+/* Number 287 is reserved for new sys_migrate_pages */
+#define TARGET_NR_openat 288
+#define TARGET_NR_mkdirat 289
+#define TARGET_NR_mknodat 290
+#define TARGET_NR_fchownat 291
+#define TARGET_NR_futimesat 292
+#define TARGET_NR_unlinkat 294
+#define TARGET_NR_renameat 295
+#define TARGET_NR_linkat 296
+#define TARGET_NR_symlinkat 297
+#define TARGET_NR_readlinkat 298
+#define TARGET_NR_fchmodat 299
+#define TARGET_NR_faccessat 300
+#define TARGET_NR_pselect6 301
+#define TARGET_NR_ppoll 302
+#define TARGET_NR_unshare 303
+#define TARGET_NR_set_robust_list 304
+#define TARGET_NR_get_robust_list 305
+#define TARGET_NR_splice 306
+#define TARGET_NR_sync_file_range 307
+#define TARGET_NR_tee 308
+#define TARGET_NR_vmsplice 309
+/* Number 310 is reserved for new sys_move_pages */
+#define TARGET_NR_getcpu 311
+#define TARGET_NR_epoll_pwait 312
+#define TARGET_NR_utimes 313
+#define TARGET_NR_fallocate 314
+#define TARGET_NR_utimensat 315
+#define TARGET_NR_signalfd 316
+#define TARGET_NR_timerfd 317
+#define TARGET_NR_eventfd 318
+#define TARGET_NR_timerfd_create 319
+#define TARGET_NR_timerfd_settime 320
+#define TARGET_NR_timerfd_gettime 321
+#define TARGET_NR_signalfd4 322
+#define TARGET_NR_eventfd2 323
+#define TARGET_NR_inotify_init1 324
+#define TARGET_NR_pipe2 325
+#define TARGET_NR_dup3 326
+#define TARGET_NR_epoll_create1 327
+#undef NR_syscalls
+#define NR_syscalls 328
+
+/*
+ * There are some system calls that are not present on 64 bit, some
+ * have a different name although they do the same (e.g. TARGET_NR_chown32
+ * is TARGET_NR_chown on 64 bit).
+ */
+#ifndef TARGET_S390X
+
+#define TARGET_NR_time 13
+#define TARGET_NR_lchown 16
+#define TARGET_NR_setuid 23
+#define TARGET_NR_getuid 24
+#define TARGET_NR_stime 25
+#define TARGET_NR_setgid 46
+#define TARGET_NR_getgid 47
+#define TARGET_NR_geteuid 49
+#define TARGET_NR_getegid 50
+#define TARGET_NR_setreuid 70
+#define TARGET_NR_setregid 71
+#define TARGET_NR_getrlimit 76
+#define TARGET_NR_getgroups 80
+#define TARGET_NR_setgroups 81
+#define TARGET_NR_fchown 95
+#define TARGET_NR_ioperm 101
+#define TARGET_NR_setfsuid 138
+#define TARGET_NR_setfsgid 139
+#define TARGET_NR__llseek 140
+#define TARGET_NR__newselect 142
+#define TARGET_NR_setresuid 164
+#define TARGET_NR_getresuid 165
+#define TARGET_NR_setresgid 170
+#define TARGET_NR_getresgid 171
+#define TARGET_NR_chown 182
+#define TARGET_NR_ugetrlimit 191 /* SuS compliant getrlimit */
+#define TARGET_NR_mmap2 192
+#define TARGET_NR_truncate64 193
+#define TARGET_NR_ftruncate64 194
+#define TARGET_NR_stat64 195
+#define TARGET_NR_lstat64 196
+#define TARGET_NR_fstat64 197
+#define TARGET_NR_lchown32 198
+#define TARGET_NR_getuid32 199
+#define TARGET_NR_getgid32 200
+#define TARGET_NR_geteuid32 201
+#define TARGET_NR_getegid32 202
+#define TARGET_NR_setreuid32 203
+#define TARGET_NR_setregid32 204
+#define TARGET_NR_getgroups32 205
+#define TARGET_NR_setgroups32 206
+#define TARGET_NR_fchown32 207
+#define TARGET_NR_setresuid32 208
+#define TARGET_NR_getresuid32 209
+#define TARGET_NR_setresgid32 210
+#define TARGET_NR_getresgid32 211
+#define TARGET_NR_chown32 212
+#define TARGET_NR_setuid32 213
+#define TARGET_NR_setgid32 214
+#define TARGET_NR_setfsuid32 215
+#define TARGET_NR_setfsgid32 216
+#define TARGET_NR_fcntl64 221
+#define TARGET_NR_sendfile64 223
+#define TARGET_NR_fadvise64_64 264
+#define TARGET_NR_fstatat64 293
+
+#else
+
+#define TARGET_NR_select 142
+#define TARGET_NR_getrlimit 191 /* SuS compliant getrlimit */
+#define TARGET_NR_lchown 198
+#define TARGET_NR_getuid 199
+#define TARGET_NR_getgid 200
+#define TARGET_NR_geteuid 201
+#define TARGET_NR_getegid 202
+#define TARGET_NR_setreuid 203
+#define TARGET_NR_setregid 204
+#define TARGET_NR_getgroups 205
+#define TARGET_NR_setgroups 206
+#define TARGET_NR_fchown 207
+#define TARGET_NR_setresuid 208
+#define TARGET_NR_getresuid 209
+#define TARGET_NR_setresgid 210
+#define TARGET_NR_getresgid 211
+#define TARGET_NR_chown 212
+#define TARGET_NR_setuid 213
+#define TARGET_NR_setgid 214
+#define TARGET_NR_setfsuid 215
+#define TARGET_NR_setfsgid 216
+#define TARGET_NR_newfstatat 293
+
+#endif
+
diff --git a/linux-user/s390x/target_signal.h b/linux-user/s390x/target_signal.h
new file mode 100644
index 0000000..b4816b0
--- /dev/null
+++ b/linux-user/s390x/target_signal.h
@@ -0,0 +1,26 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ int ss_flags;
+ abi_ulong ss_size;
+} target_stack_t;
+
+/*
+ * sigaltstack controls
+ */
+#define TARGET_SS_ONSTACK 1
+#define TARGET_SS_DISABLE 2
+
+#define TARGET_MINSIGSTKSZ 2048
+#define TARGET_SIGSTKSZ 8192
+
+static inline abi_ulong get_sp_from_cpustate(CPUS390XState *state)
+{
+ return state->regs[15];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/s390x/termbits.h b/linux-user/s390x/termbits.h
new file mode 100644
index 0000000..2a78a05
--- /dev/null
+++ b/linux-user/s390x/termbits.h
@@ -0,0 +1,283 @@
+/*
+ * include/asm-s390/termbits.h
+ *
+ * S390 version
+ *
+ * Derived from "include/asm-i386/termbits.h"
+ */
+
+#define TARGET_NCCS 19
+struct target_termios {
+ unsigned int c_iflag; /* input mode flags */
+ unsigned int c_oflag; /* output mode flags */
+ unsigned int c_cflag; /* control mode flags */
+ unsigned int c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[TARGET_NCCS]; /* control characters */
+};
+
+struct target_termios2 {
+ unsigned int c_iflag; /* input mode flags */
+ unsigned int c_oflag; /* output mode flags */
+ unsigned int c_cflag; /* control mode flags */
+ unsigned int c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[TARGET_NCCS]; /* control characters */
+ unsigned int c_ispeed; /* input speed */
+ unsigned int c_ospeed; /* output speed */
+};
+
+struct target_ktermios {
+ unsigned int c_iflag; /* input mode flags */
+ unsigned int c_oflag; /* output mode flags */
+ unsigned int c_cflag; /* control mode flags */
+ unsigned int c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[TARGET_NCCS]; /* control characters */
+ unsigned int c_ispeed; /* input speed */
+ unsigned int c_ospeed; /* output speed */
+};
+
+/* c_cc characters */
+#define TARGET_VINTR 0
+#define TARGET_VQUIT 1
+#define TARGET_VERASE 2
+#define TARGET_VKILL 3
+#define TARGET_VEOF 4
+#define TARGET_VTIME 5
+#define TARGET_VMIN 6
+#define TARGET_VSWTC 7
+#define TARGET_VSTART 8
+#define TARGET_VSTOP 9
+#define TARGET_VSUSP 10
+#define TARGET_VEOL 11
+#define TARGET_VREPRINT 12
+#define TARGET_VDISCARD 13
+#define TARGET_VWERASE 14
+#define TARGET_VLNEXT 15
+#define TARGET_VEOL2 16
+
+/* c_iflag bits */
+#define TARGET_IGNBRK 0000001
+#define TARGET_BRKINT 0000002
+#define TARGET_IGNPAR 0000004
+#define TARGET_PARMRK 0000010
+#define TARGET_INPCK 0000020
+#define TARGET_ISTRIP 0000040
+#define TARGET_INLCR 0000100
+#define TARGET_IGNCR 0000200
+#define TARGET_ICRNL 0000400
+#define TARGET_IUCLC 0001000
+#define TARGET_IXON 0002000
+#define TARGET_IXANY 0004000
+#define TARGET_IXOFF 0010000
+#define TARGET_IMAXBEL 0020000
+#define TARGET_IUTF8 0040000
+
+/* c_oflag bits */
+#define TARGET_OPOST 0000001
+#define TARGET_OLCUC 0000002
+#define TARGET_ONLCR 0000004
+#define TARGET_OCRNL 0000010
+#define TARGET_ONOCR 0000020
+#define TARGET_ONLRET 0000040
+#define TARGET_OFILL 0000100
+#define TARGET_OFDEL 0000200
+#define TARGET_NLDLY 0000400
+#define TARGET_NL0 0000000
+#define TARGET_NL1 0000400
+#define TARGET_CRDLY 0003000
+#define TARGET_CR0 0000000
+#define TARGET_CR1 0001000
+#define TARGET_CR2 0002000
+#define TARGET_CR3 0003000
+#define TARGET_TABDLY 0014000
+#define TARGET_TAB0 0000000
+#define TARGET_TAB1 0004000
+#define TARGET_TAB2 0010000
+#define TARGET_TAB3 0014000
+#define TARGET_XTABS 0014000
+#define TARGET_BSDLY 0020000
+#define TARGET_BS0 0000000
+#define TARGET_BS1 0020000
+#define TARGET_VTDLY 0040000
+#define TARGET_VT0 0000000
+#define TARGET_VT1 0040000
+#define TARGET_FFDLY 0100000
+#define TARGET_FF0 0000000
+#define TARGET_FF1 0100000
+
+/* c_cflag bit meaning */
+#define TARGET_CBAUD 0010017
+#define TARGET_B0 0000000 /* hang up */
+#define TARGET_B50 0000001
+#define TARGET_B75 0000002
+#define TARGET_B110 0000003
+#define TARGET_B134 0000004
+#define TARGET_B150 0000005
+#define TARGET_B200 0000006
+#define TARGET_B300 0000007
+#define TARGET_B600 0000010
+#define TARGET_B1200 0000011
+#define TARGET_B1800 0000012
+#define TARGET_B2400 0000013
+#define TARGET_B4800 0000014
+#define TARGET_B9600 0000015
+#define TARGET_B19200 0000016
+#define TARGET_B38400 0000017
+#define TARGET_EXTA B19200
+#define TARGET_EXTB B38400
+#define TARGET_CSIZE 0000060
+#define TARGET_CS5 0000000
+#define TARGET_CS6 0000020
+#define TARGET_CS7 0000040
+#define TARGET_CS8 0000060
+#define TARGET_CSTOPB 0000100
+#define TARGET_CREAD 0000200
+#define TARGET_PARENB 0000400
+#define TARGET_PARODD 0001000
+#define TARGET_HUPCL 0002000
+#define TARGET_CLOCAL 0004000
+#define TARGET_CBAUDEX 0010000
+#define TARGET_BOTHER 0010000
+#define TARGET_B57600 0010001
+#define TARGET_B115200 0010002
+#define TARGET_B230400 0010003
+#define TARGET_B460800 0010004
+#define TARGET_B500000 0010005
+#define TARGET_B576000 0010006
+#define TARGET_B921600 0010007
+#define TARGET_B1000000 0010010
+#define TARGET_B1152000 0010011
+#define TARGET_B1500000 0010012
+#define TARGET_B2000000 0010013
+#define TARGET_B2500000 0010014
+#define TARGET_B3000000 0010015
+#define TARGET_B3500000 0010016
+#define TARGET_B4000000 0010017
+#define TARGET_CIBAUD 002003600000 /* input baud rate */
+#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+#define TARGET_IBSHIFT 16 /* Shift from CBAUD to CIBAUD */
+
+/* c_lflag bits */
+#define TARGET_ISIG 0000001
+#define TARGET_ICANON 0000002
+#define TARGET_XCASE 0000004
+#define TARGET_ECHO 0000010
+#define TARGET_ECHOE 0000020
+#define TARGET_ECHOK 0000040
+#define TARGET_ECHONL 0000100
+#define TARGET_NOFLSH 0000200
+#define TARGET_TOSTOP 0000400
+#define TARGET_ECHOCTL 0001000
+#define TARGET_ECHOPRT 0002000
+#define TARGET_ECHOKE 0004000
+#define TARGET_FLUSHO 0010000
+#define TARGET_PENDIN 0040000
+#define TARGET_IEXTEN 0100000
+
+/* tcflow() and TCXONC use these */
+#define TARGET_TCOOFF 0
+#define TARGET_TCOON 1
+#define TARGET_TCIOFF 2
+#define TARGET_TCION 3
+
+/* tcflush() and TCFLSH use these */
+#define TARGET_TCIFLUSH 0
+#define TARGET_TCOFLUSH 1
+#define TARGET_TCIOFLUSH 2
+
+/* tcsetattr uses these */
+#define TARGET_TCSANOW 0
+#define TARGET_TCSADRAIN 1
+#define TARGET_TCSAFLUSH 2
+
+/*
+ * include/asm-s390/ioctls.h
+ *
+ * S390 version
+ *
+ * Derived from "include/asm-i386/ioctls.h"
+ */
+
+/* 0x54 is just a magic number to make these relatively unique ('T') */
+
+#define TARGET_TCGETS 0x5401
+#define TARGET_TCSETS 0x5402
+#define TARGET_TCSETSW 0x5403
+#define TARGET_TCSETSF 0x5404
+#define TARGET_TCGETA 0x5405
+#define TARGET_TCSETA 0x5406
+#define TARGET_TCSETAW 0x5407
+#define TARGET_TCSETAF 0x5408
+#define TARGET_TCSBRK 0x5409
+#define TARGET_TCXONC 0x540A
+#define TARGET_TCFLSH 0x540B
+#define TARGET_TIOCEXCL 0x540C
+#define TARGET_TIOCNXCL 0x540D
+#define TARGET_TIOCSCTTY 0x540E
+#define TARGET_TIOCGPGRP 0x540F
+#define TARGET_TIOCSPGRP 0x5410
+#define TARGET_TIOCOUTQ 0x5411
+#define TARGET_TIOCSTI 0x5412
+#define TARGET_TIOCGWINSZ 0x5413
+#define TARGET_TIOCSWINSZ 0x5414
+#define TARGET_TIOCMGET 0x5415
+#define TARGET_TIOCMBIS 0x5416
+#define TARGET_TIOCMBIC 0x5417
+#define TARGET_TIOCMSET 0x5418
+#define TARGET_TIOCGSOFTCAR 0x5419
+#define TARGET_TIOCSSOFTCAR 0x541A
+#define TARGET_FIONREAD 0x541B
+#define TARGET_TIOCINQ FIONREAD
+#define TARGET_TIOCLINUX 0x541C
+#define TARGET_TIOCCONS 0x541D
+#define TARGET_TIOCGSERIAL 0x541E
+#define TARGET_TIOCSSERIAL 0x541F
+#define TARGET_TIOCPKT 0x5420
+#define TARGET_FIONBIO 0x5421
+#define TARGET_TIOCNOTTY 0x5422
+#define TARGET_TIOCSETD 0x5423
+#define TARGET_TIOCGETD 0x5424
+#define TARGET_TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */
+#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */
+#define TARGET_TCGETS2 _IOR('T',0x2A, struct termios2)
+#define TARGET_TCSETS2 _IOW('T',0x2B, struct termios2)
+#define TARGET_TCSETSW2 _IOW('T',0x2C, struct termios2)
+#define TARGET_TCSETSF2 _IOW('T',0x2D, struct termios2)
+#define TARGET_TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */
+#define TARGET_TIOCGDEV _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */
+
+#define TARGET_FIONCLEX 0x5450 /* these numbers need to be adjusted. */
+#define TARGET_FIOCLEX 0x5451
+#define TARGET_FIOASYNC 0x5452
+#define TARGET_TIOCSERCONFIG 0x5453
+#define TARGET_TIOCSERGWILD 0x5454
+#define TARGET_TIOCSERSWILD 0x5455
+#define TARGET_TIOCGLCKTRMIOS 0x5456
+#define TARGET_TIOCSLCKTRMIOS 0x5457
+#define TARGET_TIOCSERGSTRUCT 0x5458 /* For debugging only */
+#define TARGET_TIOCSERGETLSR 0x5459 /* Get line status register */
+#define TARGET_TIOCSERGETMULTI 0x545A /* Get multiport config */
+#define TARGET_TIOCSERSETMULTI 0x545B /* Set multiport config */
+
+#define TARGET_TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
+#define TARGET_TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
+#define TARGET_FIOQSIZE 0x545E
+
+/* Used for packet mode */
+#define TARGET_TIOCPKT_DATA 0
+#define TARGET_TIOCPKT_FLUSHREAD 1
+#define TARGET_TIOCPKT_FLUSHWRITE 2
+#define TARGET_TIOCPKT_STOP 4
+#define TARGET_TIOCPKT_START 8
+#define TARGET_TIOCPKT_NOSTOP 16
+#define TARGET_TIOCPKT_DOSTOP 32
+
+#define TARGET_TIOCSER_TEMT 0x01 /* Transmitter physically empty */
+
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 6fe086b..c7a375f 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -3614,6 +3614,339 @@ long do_rt_sigreturn(CPUState *env)
return -TARGET_ENOSYS;
}
+#elif defined(TARGET_S390X)
+
+#define __NUM_GPRS 16
+#define __NUM_FPRS 16
+#define __NUM_ACRS 16
+
+#define S390_SYSCALL_SIZE 2
+#define __SIGNAL_FRAMESIZE 160 /* FIXME: 31-bit mode -> 96 */
+
+#define _SIGCONTEXT_NSIG 64
+#define _SIGCONTEXT_NSIG_BPW 64 /* FIXME: 31-bit mode -> 32 */
+#define _SIGCONTEXT_NSIG_WORDS (_SIGCONTEXT_NSIG / _SIGCONTEXT_NSIG_BPW)
+#define _SIGMASK_COPY_SIZE (sizeof(unsigned long)*_SIGCONTEXT_NSIG_WORDS)
+#define PSW_ADDR_AMODE 0x0000000000000000UL /* 0x80000000UL for 31-bit */
+#define S390_SYSCALL_OPCODE ((uint16_t)0x0a00)
+
+typedef struct {
+ target_psw_t psw;
+ target_ulong gprs[__NUM_GPRS];
+ unsigned int acrs[__NUM_ACRS];
+} target_s390_regs_common;
+
+typedef struct {
+ unsigned int fpc;
+ double fprs[__NUM_FPRS];
+} target_s390_fp_regs;
+
+typedef struct {
+ target_s390_regs_common regs;
+ target_s390_fp_regs fpregs;
+} target_sigregs;
+
+struct target_sigcontext {
+ target_ulong oldmask[_SIGCONTEXT_NSIG_WORDS];
+ target_sigregs *sregs;
+};
+
+typedef struct {
+ uint8_t callee_used_stack[__SIGNAL_FRAMESIZE];
+ struct target_sigcontext sc;
+ target_sigregs sregs;
+ int signo;
+ uint8_t retcode[S390_SYSCALL_SIZE];
+} sigframe;
+
+struct target_ucontext {
+ target_ulong uc_flags;
+ struct target_ucontext *uc_link;
+ target_stack_t uc_stack;
+ target_sigregs uc_mcontext;
+ target_sigset_t uc_sigmask; /* mask last for extensibility */
+};
+
+typedef struct {
+ uint8_t callee_used_stack[__SIGNAL_FRAMESIZE];
+ uint8_t retcode[S390_SYSCALL_SIZE];
+ struct target_siginfo info;
+ struct target_ucontext uc;
+} rt_sigframe;
+
+static inline abi_ulong
+get_sigframe(struct target_sigaction *ka, CPUState *env, size_t frame_size)
+{
+ abi_ulong sp;
+
+ /* Default to using normal stack */
+ sp = env->regs[15];
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if (ka->sa_flags & TARGET_SA_ONSTACK) {
+ if (!sas_ss_flags(sp)) {
+ sp = target_sigaltstack_used.ss_sp +
+ target_sigaltstack_used.ss_size;
+ }
+ }
+
+ /* This is the legacy signal stack switching. */
+ else if (/* FIXME !user_mode(regs) */ 0 &&
+ !(ka->sa_flags & TARGET_SA_RESTORER) &&
+ ka->sa_restorer) {
+ sp = (abi_ulong) ka->sa_restorer;
+ }
+
+ return (sp - frame_size) & -8ul;
+}
+
+static void save_sigregs(CPUState *env, target_sigregs *sregs)
+{
+ int i;
+ //save_access_regs(current->thread.acrs); FIXME
+
+ /* Copy a 'clean' PSW mask to the user to avoid leaking
+ information about whether PER is currently on. */
+ __put_user(env->psw.mask, &sregs->regs.psw.mask);
+ __put_user(env->psw.addr, &sregs->regs.psw.addr);
+ for (i = 0; i < 16; i++) {
+ __put_user(env->regs[i], &sregs->regs.gprs[i]);
+ }
+ for (i = 0; i < 16; i++) {
+ __put_user(env->aregs[i], &sregs->regs.acrs[i]);
+ }
+ /*
+ * We have to store the fp registers to current->thread.fp_regs
+ * to merge them with the emulated registers.
+ */
+ //save_fp_regs(¤t->thread.fp_regs); FIXME
+ for (i = 0; i < 16; i++) {
+ __put_user(env->fregs[i].ll, &sregs->fpregs.fprs[i]);
+ }
+}
+
+static void setup_frame(int sig, struct target_sigaction *ka,
+ target_sigset_t *set, CPUState *env)
+{
+ sigframe *frame;
+ abi_ulong frame_addr;
+
+ frame_addr = get_sigframe(ka, env, sizeof(*frame));
+ qemu_log("%s: frame_addr 0x%llx\n", __FUNCTION__,
+ (unsigned long long)frame_addr);
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
+ goto give_sigsegv;
+ }
+
+ qemu_log("%s: 1\n", __FUNCTION__);
+ if (__put_user(set->sig[0], &frame->sc.oldmask[0])) {
+ goto give_sigsegv;
+ }
+
+ save_sigregs(env, &frame->sregs);
+
+ __put_user((abi_ulong)(unsigned long)&frame->sregs,
+ (abi_ulong *)&frame->sc.sregs);
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa_flags & TARGET_SA_RESTORER) {
+ env->regs[14] = (unsigned long)
+ ka->sa_restorer | PSW_ADDR_AMODE;
+ } else {
+ env->regs[14] = (unsigned long)
+ frame->retcode | PSW_ADDR_AMODE;
+ if (__put_user(S390_SYSCALL_OPCODE | TARGET_NR_sigreturn,
+ (uint16_t *)(frame->retcode)))
+ goto give_sigsegv;
+ }
+
+ /* Set up backchain. */
+ if (__put_user(env->regs[15], (abi_ulong *) frame)) {
+ goto give_sigsegv;
+ }
+
+ /* Set up registers for signal handler */
+ env->regs[15] = (target_ulong)(unsigned long) frame;
+ env->psw.addr = (target_ulong) ka->_sa_handler | PSW_ADDR_AMODE;
+
+ env->regs[2] = sig; //map_signal(sig);
+ env->regs[3] = (target_ulong)(unsigned long) &frame->sc;
+
+ /* We forgot to include these in the sigcontext.
+ To avoid breaking binary compatibility, they are passed as args. */
+ env->regs[4] = 0; // FIXME: no clue... current->thread.trap_no;
+ env->regs[5] = 0; // FIXME: no clue... current->thread.prot_addr;
+
+ /* Place signal number on stack to allow backtrace from handler. */
+ if (__put_user(env->regs[2], (int *) &frame->signo)) {
+ goto give_sigsegv;
+ }
+ unlock_user_struct(frame, frame_addr, 1);
+ return;
+
+give_sigsegv:
+ qemu_log("%s: give_sigsegv\n", __FUNCTION__);
+ unlock_user_struct(frame, frame_addr, 1);
+ force_sig(TARGET_SIGSEGV);
+}
+
+static void setup_rt_frame(int sig, struct target_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUState *env)
+{
+ int i;
+ rt_sigframe *frame;
+ abi_ulong frame_addr;
+
+ frame_addr = get_sigframe(ka, env, sizeof *frame);
+ qemu_log("%s: frame_addr 0x%llx\n", __FUNCTION__,
+ (unsigned long long)frame_addr);
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
+ goto give_sigsegv;
+ }
+
+ qemu_log("%s: 1\n", __FUNCTION__);
+ if (copy_siginfo_to_user(&frame->info, info)) {
+ goto give_sigsegv;
+ }
+
+ /* Create the ucontext. */
+ __put_user(0, &frame->uc.uc_flags);
+ __put_user((abi_ulong)0, (abi_ulong *)&frame->uc.uc_link);
+ __put_user(target_sigaltstack_used.ss_sp, &frame->uc.uc_stack.ss_sp);
+ __put_user(sas_ss_flags(get_sp_from_cpustate(env)),
+ &frame->uc.uc_stack.ss_flags);
+ __put_user(target_sigaltstack_used.ss_size, &frame->uc.uc_stack.ss_size);
+ save_sigregs(env, &frame->uc.uc_mcontext);
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
+ __put_user((abi_ulong)set->sig[i],
+ (abi_ulong *)&frame->uc.uc_sigmask.sig[i]);
+ }
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa_flags & TARGET_SA_RESTORER) {
+ env->regs[14] = (unsigned long) ka->sa_restorer | PSW_ADDR_AMODE;
+ } else {
+ env->regs[14] = (unsigned long) frame->retcode | PSW_ADDR_AMODE;
+ if (__put_user(S390_SYSCALL_OPCODE | TARGET_NR_rt_sigreturn,
+ (uint16_t *)(frame->retcode))) {
+ goto give_sigsegv;
+ }
+ }
+
+ /* Set up backchain. */
+ if (__put_user(env->regs[15], (abi_ulong *) frame)) {
+ goto give_sigsegv;
+ }
+
+ /* Set up registers for signal handler */
+ env->regs[15] = (target_ulong)(unsigned long) frame;
+ env->psw.addr = (target_ulong) ka->_sa_handler | PSW_ADDR_AMODE;
+
+ env->regs[2] = sig; //map_signal(sig);
+ env->regs[3] = (target_ulong)(unsigned long) &frame->info;
+ env->regs[4] = (target_ulong)(unsigned long) &frame->uc;
+ return;
+
+give_sigsegv:
+ qemu_log("%s: give_sigsegv\n", __FUNCTION__);
+ unlock_user_struct(frame, frame_addr, 1);
+ force_sig(TARGET_SIGSEGV);
+}
+
+static int
+restore_sigregs(CPUState *env, target_sigregs *sc)
+{
+ int err = 0;
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ err |= __get_user(env->regs[i], &sc->regs.gprs[i]);
+ }
+
+ err |= __get_user(env->psw.mask, &sc->regs.psw.mask);
+ qemu_log("%s: sc->regs.psw.addr 0x%llx env->psw.addr 0x%llx\n",
+ __FUNCTION__, (unsigned long long)sc->regs.psw.addr,
+ (unsigned long long)env->psw.addr);
+ err |= __get_user(env->psw.addr, &sc->regs.psw.addr);
+ /* FIXME: 31-bit -> | PSW_ADDR_AMODE */
+
+ for (i = 0; i < 16; i++) {
+ err |= __get_user(env->aregs[i], &sc->regs.acrs[i]);
+ }
+ for (i = 0; i < 16; i++) {
+ err |= __get_user(env->fregs[i].ll, &sc->fpregs.fprs[i]);
+ }
+
+ return err;
+}
+
+long do_sigreturn(CPUState *env)
+{
+ sigframe *frame;
+ abi_ulong frame_addr = env->regs[15];
+ qemu_log("%s: frame_addr 0x%llx\n", __FUNCTION__,
+ (unsigned long long)frame_addr);
+ target_sigset_t target_set;
+ sigset_t set;
+
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
+ goto badframe;
+ }
+ if (__get_user(target_set.sig[0], &frame->sc.oldmask[0])) {
+ goto badframe;
+ }
+
+ target_to_host_sigset_internal(&set, &target_set);
+ sigprocmask(SIG_SETMASK, &set, NULL); /* ~_BLOCKABLE? */
+
+ if (restore_sigregs(env, &frame->sregs)) {
+ goto badframe;
+ }
+
+ unlock_user_struct(frame, frame_addr, 0);
+ return env->regs[2];
+
+badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+ return 0;
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ rt_sigframe *frame;
+ abi_ulong frame_addr = env->regs[15];
+ qemu_log("%s: frame_addr 0x%llx\n", __FUNCTION__,
+ (unsigned long long)frame_addr);
+ sigset_t set;
+
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
+ goto badframe;
+ }
+ target_to_host_sigset(&set, &frame->uc.uc_sigmask);
+
+ sigprocmask(SIG_SETMASK, &set, NULL); /* ~_BLOCKABLE? */
+
+ if (restore_sigregs(env, &frame->uc.uc_mcontext)) {
+ goto badframe;
+ }
+
+ if (do_sigaltstack(frame_addr + offsetof(rt_sigframe, uc.uc_stack), 0,
+ get_sp_from_cpustate(env)) == -EFAULT) {
+ goto badframe;
+ }
+ unlock_user_struct(frame, frame_addr, 0);
+ return env->regs[2];
+
+badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+ return 0;
+}
+
#elif defined(TARGET_PPC) && !defined(TARGET_PPC64)
/* FIXME: Many of the structures are defined for both PPC and PPC64, but
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 6e7d88e..5cb27c7 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -5548,7 +5548,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
ret = get_errno(settimeofday(&tv, NULL));
}
break;
-#ifdef TARGET_NR_select
+#if defined(TARGET_NR_select) && !defined(TARGET_S390X) && !defined(TARGET_S390)
case TARGET_NR_select:
{
struct target_sel_arg_struct *sel;
@@ -5659,7 +5659,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
#endif
#ifdef TARGET_NR_mmap
case TARGET_NR_mmap:
-#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_CRIS) || defined(TARGET_MICROBLAZE)
+#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || \
+ defined(TARGET_M68K) || defined(TARGET_CRIS) || defined(TARGET_MICROBLAZE) \
+ || defined(TARGET_S390X)
{
abi_ulong *v;
abi_ulong v1, v2, v3, v4, v5, v6;
@@ -6155,6 +6157,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg5, arg4));
#elif defined(TARGET_CRIS)
ret = get_errno(do_fork(cpu_env, arg2, arg1, arg3, arg4, arg5));
+#elif defined(TARGET_S390X)
+ ret = get_errno(do_fork(cpu_env, arg2, arg1, arg3, arg5, arg4));
#else
ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg4, arg5));
#endif
@@ -6363,8 +6367,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
}
break;
#endif /* TARGET_NR_getdents64 */
-#ifdef TARGET_NR__newselect
+#if defined(TARGET_NR__newselect) || defined(TARGET_S390X)
+#ifdef TARGET_S390X
+ case TARGET_NR_select:
+#else
case TARGET_NR__newselect:
+#endif
ret = do_select(arg1, arg2, arg3, arg4, arg5);
break;
#endif
@@ -6681,7 +6689,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
case TARGET_NR_sigaltstack:
#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_MIPS) || \
defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_ALPHA) || \
- defined(TARGET_M68K)
+ defined(TARGET_M68K) || defined(TARGET_S390X)
ret = do_sigaltstack(arg1, arg2, get_sp_from_cpustate((CPUState *)cpu_env));
break;
#else
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index e05ddf9..04c268d 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -58,7 +58,8 @@
#endif
#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SH4) \
- || defined(TARGET_M68K) || defined(TARGET_CRIS) || defined(TARGET_UNICORE32)
+ || defined(TARGET_M68K) || defined(TARGET_CRIS) || defined(TARGET_UNICORE32) \
+ || defined(TARGET_S390X)
#define TARGET_IOC_SIZEBITS 14
#define TARGET_IOC_DIRBITS 2
@@ -321,7 +322,8 @@ int do_sigaction(int sig, const struct target_sigaction *act,
#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) \
|| defined(TARGET_PPC) || defined(TARGET_MIPS) || defined(TARGET_SH4) \
|| defined(TARGET_M68K) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) \
- || defined(TARGET_MICROBLAZE) || defined(TARGET_UNICORE32)
+ || defined(TARGET_MICROBLAZE) || defined(TARGET_UNICORE32) \
+ || defined(TARGET_S390X)
#if defined(TARGET_SPARC)
#define TARGET_SA_NOCLDSTOP 8u
@@ -1688,6 +1690,27 @@ struct target_stat {
abi_long __unused[3];
};
+#elif defined(TARGET_S390X)
+struct target_stat {
+ abi_ulong st_dev;
+ abi_ulong st_ino;
+ abi_ulong st_nlink;
+ unsigned int st_mode;
+ unsigned int st_uid;
+ unsigned int st_gid;
+ unsigned int __pad1;
+ abi_ulong st_rdev;
+ abi_ulong st_size;
+ abi_ulong target_st_atime;
+ abi_ulong target_st_atime_nsec;
+ abi_ulong target_st_mtime;
+ abi_ulong target_st_mtime_nsec;
+ abi_ulong target_st_ctime;
+ abi_ulong target_st_ctime_nsec;
+ abi_ulong st_blksize;
+ abi_long st_blocks;
+ abi_ulong __unused[3];
+};
#else
#error unsupported CPU
#endif
@@ -1774,6 +1797,34 @@ struct target_statfs64 {
abi_long f_frsize;
abi_long f_spare[5];
};
+#elif defined(TARGET_S390X)
+struct target_statfs {
+ int32_t f_type;
+ int32_t f_bsize;
+ abi_long f_blocks;
+ abi_long f_bfree;
+ abi_long f_bavail;
+ abi_long f_files;
+ abi_long f_ffree;
+ kernel_fsid_t f_fsid;
+ int32_t f_namelen;
+ int32_t f_frsize;
+ int32_t f_spare[5];
+};
+
+struct target_statfs64 {
+ int32_t f_type;
+ int32_t f_bsize;
+ abi_long f_blocks;
+ abi_long f_bfree;
+ abi_long f_bavail;
+ abi_long f_files;
+ abi_long f_ffree;
+ kernel_fsid_t f_fsid;
+ int32_t f_namelen;
+ int32_t f_frsize;
+ int32_t f_spare[5];
+};
#else
struct target_statfs {
uint32_t f_type;
diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh
index c50beb7..83a44d8 100644
--- a/scripts/qemu-binfmt-conf.sh
+++ b/scripts/qemu-binfmt-conf.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-# enable automatic i386/ARM/M68K/MIPS/SPARC/PPC program execution by the kernel
+# enable automatic i386/ARM/M68K/MIPS/SPARC/PPC/s390 program execution by the kernel
# load the binfmt_misc module
if [ ! -d /proc/sys/fs/binfmt_misc ]; then
@@ -63,4 +63,6 @@ fi
if [ $cpu != "sh" ] ; then
echo ':sh4:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a\x00:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-sh4:' > /proc/sys/fs/binfmt_misc/register
echo ':sh4eb:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-sh4eb:' > /proc/sys/fs/binfmt_misc/register
+if [ $cpu != "s390x" ] ; then
+ echo ':s390x:M::\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x16:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-s390x:' > /proc/sys/fs/binfmt_misc/register
fi
commit 7a86d29a7e16e738d749cfece8857d8902790875
Author: Alexander Graf <agraf at suse.de>
Date: Wed Apr 13 13:08:44 2011 +0200
tcg: extend max tcg opcodes when using 64-on-32bit
When running a 64 bit guest on a 32 bit host, we tend to use more TCG ops
than on a 64 bit host. Reflect that in the reserved opcode amount constant.
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/exec-all.h b/exec-all.h
index 7c2d29f..14b0895 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -43,7 +43,11 @@ typedef ram_addr_t tb_page_addr_t;
typedef struct TranslationBlock TranslationBlock;
/* XXX: make safe guess about sizes */
+#if (HOST_LONG_BITS == 32) && (TARGET_LONG_BITS == 64)
+#define MAX_OP_PER_INSTR 128
+#else
#define MAX_OP_PER_INSTR 96
+#endif
#if HOST_LONG_BITS == 32
#define MAX_OPC_PARAM_PER_ARG 2
commit 71b12d31971751f46c546d693c1e216f38ba2053
Author: Christian Borntraeger <borntraeger at de.ibm.com>
Date: Thu May 5 09:29:57 2011 +0200
s390x: fix smp support for kvm
Currently smp support for kvm does not work. Qemu does a kvm run even on
secondary CPUs which dont have a sane state (initial psw == 0)
triggering some program faults. Architecturally these cpus are in the stopped
state, so we should not do the kvm run ioctl. (these CPUs will be started
by a SIGP restart later during the boot process)
We need to tell the loop that this cpu should not run. Jan Kiszka pointed
out that kvm_arch_process_async_events is the right place to do.
Signed-off-by: Christian Borntraeger <borntraeger at de.ibm.com>
Signed-off-by: Alexander Graf <agraf at suse.de>
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index 2643460..3155693 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -179,7 +179,7 @@ void kvm_arch_post_run(CPUState *env, struct kvm_run *run)
int kvm_arch_process_async_events(CPUState *env)
{
- return 0;
+ return env->halted;
}
void kvm_s390_interrupt_internal(CPUState *env, int type, uint32_t parm,
commit 1fddfba129f5435c80eda14e8bc23fdb888c7187
Author: Alexander Graf <agraf at suse.de>
Date: Thu May 19 11:57:09 2011 +0200
ahci: Fix non-NCQ accesses for LBA > 16bits
AHCI provides two ways of reading/writing data:
1) NCQ
2) ATA commands with the LBA in the command FIS
In the second code path, we didn't handle any LBAs that were bigger than
16 bits, so whenever a guest that used high LBA numbers wanted to access
data, the LBA got truncated down to 16 bits, giving the guest garbage.
This patch adds support for LBAs higher than 16 bits. I've tested that it
works just fine with SeaBIOS and Linux guests. This patch also unbreaks
the often reported grub errors people have seen with AHCI.
Signed-off-by: Alexander Graf <agraf at suse.de>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 744d19d..1f008a3 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -884,8 +884,31 @@ static int handle_cmd(AHCIState *s, int port, int slot)
}
if (ide_state->drive_kind != IDE_CD) {
- ide_set_sector(ide_state, (cmd_fis[6] << 16) | (cmd_fis[5] << 8) |
- cmd_fis[4]);
+ /*
+ * We set the sector depending on the sector defined in the FIS.
+ * Unfortunately, the spec isn't exactly obvious on this one.
+ *
+ * Apparently LBA48 commands set fis bytes 10,9,8,6,5,4 to the
+ * 48 bit sector number. ATA_CMD_READ_DMA_EXT is an example for
+ * such a command.
+ *
+ * Non-LBA48 commands however use 7[lower 4 bits],6,5,4 to define a
+ * 28-bit sector number. ATA_CMD_READ_DMA is an example for such
+ * a command.
+ *
+ * Since the spec doesn't explicitly state what each field should
+ * do, I simply assume non-used fields as reserved and OR everything
+ * together, independent of the command.
+ */
+ ide_set_sector(ide_state, ((uint64_t)cmd_fis[10] << 40)
+ | ((uint64_t)cmd_fis[9] << 32)
+ /* This is used for LBA48 commands */
+ | ((uint64_t)cmd_fis[8] << 24)
+ /* This is used for non-LBA48 commands */
+ | ((uint64_t)(cmd_fis[7] & 0xf) << 24)
+ | ((uint64_t)cmd_fis[6] << 16)
+ | ((uint64_t)cmd_fis[5] << 8)
+ | cmd_fis[4]);
}
/* Copy the ACMD field (ATAPI packet, if any) from the AHCI command
commit 505597e4476a6bc219d0ec1362b760d71cb4fdca
Author: Gerd Hoffmann <kraxel at redhat.com>
Date: Wed Apr 27 11:05:34 2011 +0200
Ignore pci unplug requests for unpluggable devices (CVE-2011-1751)
This patch makes qemu ignore unplug requests from the guest for pci
devices which are tagged as non-hotpluggable. Trouble spot is the
piix4 chipset with the ISA bridge. Requests to unplug that one will
make it go away together with all ISA bus devices, which are not
prepared to be unplugged and thus don't cleanup, leaving active
qemu timers behind in free'ed memory.
Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index 96f5222..6c908ff 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -471,11 +471,13 @@ static void pciej_write(void *opaque, uint32_t addr, uint32_t val)
BusState *bus = opaque;
DeviceState *qdev, *next;
PCIDevice *dev;
+ PCIDeviceInfo *info;
int slot = ffs(val) - 1;
QLIST_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
dev = DO_UPCAST(PCIDevice, qdev, qdev);
- if (PCI_SLOT(dev->devfn) == slot) {
+ info = container_of(qdev->info, PCIDeviceInfo, qdev);
+ if (PCI_SLOT(dev->devfn) == slot && !info->no_hotplug) {
qdev_free(qdev);
}
}
commit 8d278467ff698de351a136e71f352e611e728579
Author: Markus Armbruster <armbru at redhat.com>
Date: Mon May 16 15:04:57 2011 +0200
block: Remove type hint, it's guest matter, doesn't belong here
No users of bdrv_get_type_hint() left. bdrv_set_type_hint() can make
the media removable by side effect. Make that explicit.
Signed-off-by: Markus Armbruster <armbru at redhat.com>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/block.c b/block.c
index 9de7450..effa86f 100644
--- a/block.c
+++ b/block.c
@@ -1305,13 +1305,6 @@ void bdrv_set_geometry_hint(BlockDriverState *bs,
bs->secs = secs;
}
-void bdrv_set_type_hint(BlockDriverState *bs, int type)
-{
- bs->type = type;
- bs->removable = ((type == BDRV_TYPE_CDROM ||
- type == BDRV_TYPE_FLOPPY));
-}
-
void bdrv_set_translation_hint(BlockDriverState *bs, int translation)
{
bs->translation = translation;
@@ -1428,11 +1421,6 @@ void bdrv_get_floppy_geometry_hint(BlockDriverState *bs, int *nb_heads,
}
}
-int bdrv_get_type_hint(BlockDriverState *bs)
-{
- return bs->type;
-}
-
int bdrv_get_translation_hint(BlockDriverState *bs)
{
return bs->translation;
diff --git a/block.h b/block.h
index 52e9cad..da7d39c 100644
--- a/block.h
+++ b/block.h
@@ -152,9 +152,6 @@ int bdrv_has_zero_init(BlockDriverState *bs);
int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
int *pnum);
-#define BDRV_TYPE_HD 0
-#define BDRV_TYPE_CDROM 1
-#define BDRV_TYPE_FLOPPY 2
#define BIOS_ATA_TRANSLATION_AUTO 0
#define BIOS_ATA_TRANSLATION_NONE 1
#define BIOS_ATA_TRANSLATION_LBA 2
@@ -163,7 +160,6 @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
void bdrv_set_geometry_hint(BlockDriverState *bs,
int cyls, int heads, int secs);
-void bdrv_set_type_hint(BlockDriverState *bs, int type);
void bdrv_set_translation_hint(BlockDriverState *bs, int translation);
void bdrv_get_geometry_hint(BlockDriverState *bs,
int *pcyls, int *pheads, int *psecs);
@@ -177,7 +173,6 @@ typedef enum FDriveType {
void bdrv_get_floppy_geometry_hint(BlockDriverState *bs, int *nb_heads,
int *max_track, int *last_sect,
FDriveType drive_in, FDriveType *drive);
-int bdrv_get_type_hint(BlockDriverState *bs);
int bdrv_get_translation_hint(BlockDriverState *bs);
void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error,
BlockErrorAction on_write_error);
diff --git a/block_int.h b/block_int.h
index 545ad11..fa91337 100644
--- a/block_int.h
+++ b/block_int.h
@@ -194,7 +194,6 @@ struct BlockDriverState {
/* NOTE: the following infos are only hints for real hardware
drivers. They are not used by the block driver */
int cyls, heads, secs, translation;
- int type;
BlockErrorAction on_read_error, on_write_error;
char device_name[32];
unsigned long *dirty_bitmap;
diff --git a/blockdev.c b/blockdev.c
index 28727df..6e0eb83 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -487,7 +487,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
}
break;
case MEDIA_CDROM:
- bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_CDROM);
+ bdrv_set_removable(dinfo->bdrv, 1);
dinfo->media_cd = 1;
break;
}
@@ -496,7 +496,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
/* FIXME: This isn't really a floppy, but it's a reasonable
approximation. */
case IF_FLOPPY:
- bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_FLOPPY);
+ bdrv_set_removable(dinfo->bdrv, 1);
break;
case IF_PFLASH:
case IF_MTD:
commit 95b5edcd92d64c7b8fe9f2e3e0725fdf84be0dfa
Author: Markus Armbruster <armbru at redhat.com>
Date: Mon May 16 15:04:56 2011 +0200
blockdev: Store -drive option media in DriveInfo
DriveInfo is closely tied to -drive, and like -drive, it mixes
information about host and guest part of the block device. Unlike
DriveInfo, BlockDriverState should be about the host part only.
One of the remaining guest bits there is the "type hint". -drive
option media sets it, and qdevs "ide-drive", "scsi-disk" and non-qdev
IF_XEN devices check it to pick HD vs. CD.
Communicate -drive option media via new DriveInfo member media_cd
instead.
Signed-off-by: Markus Armbruster <armbru at redhat.com>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/blockdev.c b/blockdev.c
index 5429621..28727df 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -488,6 +488,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
break;
case MEDIA_CDROM:
bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_CDROM);
+ dinfo->media_cd = 1;
break;
}
break;
diff --git a/blockdev.h b/blockdev.h
index 2c9e780..3587786 100644
--- a/blockdev.h
+++ b/blockdev.h
@@ -33,6 +33,7 @@ struct DriveInfo {
int bus;
int unit;
int auto_del; /* see blockdev_mark_auto_del() */
+ int media_cd;
QemuOpts *opts;
char serial[BLOCK_SERIAL_STRLEN + 1];
QTAILQ_ENTRY(DriveInfo) next;
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 542ed65..45410e8 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -1731,8 +1731,7 @@ void ide_init2_with_non_qdev_drives(IDEBus *bus, DriveInfo *hd0,
ide_init1(bus, i);
if (dinfo) {
if (ide_init_drive(&bus->ifs[i], dinfo->bdrv,
- bdrv_get_type_hint(dinfo->bdrv) == BDRV_TYPE_CDROM ? IDE_CD : IDE_HD,
- NULL,
+ dinfo->media_cd ? IDE_CD : IDE_HD, NULL,
*dinfo->serial ? dinfo->serial : NULL) < 0) {
error_report("Can't set up IDE drive %s", dinfo->id);
exit(1);
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index 3bca726..3f9dc89 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -98,9 +98,7 @@ IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive)
{
DeviceState *dev;
- dev = qdev_create(&bus->qbus,
- bdrv_get_type_hint(drive->bdrv) == BDRV_TYPE_CDROM
- ? "ide-cd" : "ide-hd");
+ dev = qdev_create(&bus->qbus, drive->media_cd ? "ide-cd" : "ide-hd");
qdev_prop_set_uint32(dev, "unit", unit);
qdev_prop_set_drive_nofail(dev, "drive", drive->bdrv);
qdev_init_nofail(dev);
@@ -165,9 +163,9 @@ static int ide_cd_initfn(IDEDevice *dev)
static int ide_drive_initfn(IDEDevice *dev)
{
- return ide_dev_initfn(dev,
- bdrv_get_type_hint(dev->conf.bs) == BDRV_TYPE_CDROM
- ? IDE_CD : IDE_HD);
+ DriveInfo *dinfo = drive_get_by_blockdev(dev->conf.bs);
+
+ return ide_dev_initfn(dev, dinfo->media_cd ? IDE_CD : IDE_HD);
}
#define DEFINE_IDE_DEV_PROPERTIES() \
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 8df8518..397b9d6 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -1295,12 +1295,13 @@ static int scsi_cd_initfn(SCSIDevice *dev)
static int scsi_disk_initfn(SCSIDevice *dev)
{
SCSIDriveKind kind;
+ DriveInfo *dinfo;
if (!dev->conf.bs) {
kind = SCSI_HD; /* will die in scsi_initfn() */
} else {
- kind = bdrv_get_type_hint(dev->conf.bs) == BDRV_TYPE_CDROM
- ? SCSI_CD : SCSI_HD;
+ dinfo = drive_get_by_blockdev(dev->conf.bs);
+ kind = dinfo->media_cd ? SCSI_CD : SCSI_HD;
}
return scsi_initfn(dev, kind);
diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c
index 8d50216..3a92155 100644
--- a/hw/xen_devconfig.c
+++ b/hw/xen_devconfig.c
@@ -96,7 +96,7 @@ int xen_config_dev_blk(DriveInfo *disk)
{
char fe[256], be[256];
int vdev = 202 * 256 + 16 * disk->unit;
- int cdrom = disk->bdrv->type == BDRV_TYPE_CDROM;
+ int cdrom = disk->media_cd;
const char *devtype = cdrom ? "cdrom" : "disk";
const char *mode = cdrom ? "r" : "w";
commit d8aeeb31d53a07a0cce36c7bcf53684953c2e445
Author: Markus Armbruster <armbru at redhat.com>
Date: Mon May 16 15:04:55 2011 +0200
block QMP: Deprecate query-block's "type", drop info block's "type="
query-block's specification documents response member "type" with
values "hd", "cdrom", "floppy", "unknown".
Its value is unreliable: a block device used as floppy has type
"floppy" if created with if=floppy, but type "hd" if created with
if=none.
That's because with if=none, the type is at best a declaration of
intent: the drive can be connected to any guest device. Its type is
really the guest device's business. Reporting it here is wrong.
No known user of QMP uses "type". It's unlikely that any unknown
users exist, because its value is useless unless you know how the
block device was created. But then you also know the true value.
Fixing the broken value risks breaking (hypothetical!) clients that
somehow rely on the current behavior. Not fixing the value risks
breaking (hypothetical!) clients that rely on the value to be
accurate. Can't entirely avoid hypothetical lossage. Change the
value to be always "unknown".
This makes "info block" always report "type=unknown". Pointless.
Change it to not report the type.
Signed-off-by: Markus Armbruster <armbru at redhat.com>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/block.c b/block.c
index f403718..9de7450 100644
--- a/block.c
+++ b/block.c
@@ -1704,9 +1704,8 @@ static void bdrv_print_dict(QObject *obj, void *opaque)
bs_dict = qobject_to_qdict(obj);
- monitor_printf(mon, "%s: type=%s removable=%d",
+ monitor_printf(mon, "%s: removable=%d",
qdict_get_str(bs_dict, "device"),
- qdict_get_str(bs_dict, "type"),
qdict_get_bool(bs_dict, "removable"));
if (qdict_get_bool(bs_dict, "removable")) {
@@ -1747,23 +1746,10 @@ void bdrv_info(Monitor *mon, QObject **ret_data)
QTAILQ_FOREACH(bs, &bdrv_states, list) {
QObject *bs_obj;
- const char *type = "unknown";
-
- switch(bs->type) {
- case BDRV_TYPE_HD:
- type = "hd";
- break;
- case BDRV_TYPE_CDROM:
- type = "cdrom";
- break;
- case BDRV_TYPE_FLOPPY:
- type = "floppy";
- break;
- }
- bs_obj = qobject_from_jsonf("{ 'device': %s, 'type': %s, "
+ bs_obj = qobject_from_jsonf("{ 'device': %s, 'type': 'unknown', "
"'removable': %i, 'locked': %i }",
- bs->device_name, type, bs->removable,
+ bs->device_name, bs->removable,
bs->locked);
if (bs->drv) {
diff --git a/qmp-commands.hx b/qmp-commands.hx
index fbd98ee..a9f109a 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -1039,7 +1039,8 @@ Each json-object contain the following:
- "device": device name (json-string)
- "type": device type (json-string)
- - Possible values: "hd", "cdrom", "floppy", "unknown"
+ - deprecated, retained for backward compatibility
+ - Possible values: "unknown"
- "removable": true if the device is removable, false otherwise (json-bool)
- "locked": true if the device is locked, false otherwise (json-bool)
- "inserted": only present if the device is inserted, it is a json-object
@@ -1070,25 +1071,25 @@ Example:
"encrypted":false,
"file":"disks/test.img"
},
- "type":"hd"
+ "type":"unknown"
},
{
"device":"ide1-cd0",
"locked":false,
"removable":true,
- "type":"cdrom"
+ "type":"unknown"
},
{
"device":"floppy0",
"locked":false,
"removable":true,
- "type": "floppy"
+ "type":"unknown"
},
{
"device":"sd0",
"locked":false,
"removable":true,
- "type":"floppy"
+ "type":"unknown"
}
]
}
commit af6bf1328ef90fae617857c02697e0174b84d596
Author: Markus Armbruster <armbru at redhat.com>
Date: Wed May 18 18:31:02 2011 +0200
defaults: ide-cd, ide-hd and scsi-cd devices suppress default CD-ROM
ide-hd has to suppress the default CD-ROM, or else you can't put one
on secondary master without -nodefaults.
Unlike legacy scsi-disk, scsi-cd suppresses default CD-ROM.
Signed-off-by: Markus Armbruster <armbru at redhat.com>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/vl.c b/vl.c
index bffba69..b362871 100644
--- a/vl.c
+++ b/vl.c
@@ -279,7 +279,10 @@ static struct {
{ .driver = "isa-serial", .flag = &default_serial },
{ .driver = "isa-parallel", .flag = &default_parallel },
{ .driver = "isa-fdc", .flag = &default_floppy },
+ { .driver = "ide-cd", .flag = &default_cdrom },
+ { .driver = "ide-hd", .flag = &default_cdrom },
{ .driver = "ide-drive", .flag = &default_cdrom },
+ { .driver = "scsi-cd", .flag = &default_cdrom },
{ .driver = "virtio-serial-pci", .flag = &default_virtcon },
{ .driver = "virtio-serial-s390", .flag = &default_virtcon },
{ .driver = "virtio-serial", .flag = &default_virtcon },
commit b443ae67130d32ad06b06fc9aa6d04d05ccd93ce
Author: Markus Armbruster <armbru at redhat.com>
Date: Mon May 16 15:04:53 2011 +0200
scsi: Split qdev "scsi-disk" into "scsi-hd" and "scsi-cd"
A "scsi-disk" is either a hard disk or a CD-ROM, depending on the
associated BlockDriverState's type hint. Unclean; disk vs. CD belongs
to the guest part, not the host part.
Have separate qdevs "scsi-hd" and "scsi-cd" to model disk vs. CD in
the guest part.
Keep scsi-disk for backward compatibility.
Don't copy scsi-disk property removable to scsi-cd. It's not used and
always zero(!) there.
Signed-off-by: Markus Armbruster <armbru at redhat.com>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index b05e654..8df8518 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -65,6 +65,8 @@ typedef struct SCSIDiskReq {
uint32_t status;
} SCSIDiskReq;
+typedef enum { SCSI_HD, SCSI_CD } SCSIDriveKind;
+
struct SCSIDiskState
{
SCSIDevice qdev;
@@ -78,6 +80,7 @@ struct SCSIDiskState
char *version;
char *serial;
SCSISense sense;
+ SCSIDriveKind drive_kind;
};
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type);
@@ -406,7 +409,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
return -1;
}
- if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) {
+ if (s->drive_kind == SCSI_CD) {
outbuf[buflen++] = 5;
} else {
outbuf[buflen++] = 0;
@@ -424,7 +427,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
outbuf[buflen++] = 0x00; // list of supported pages (this page)
outbuf[buflen++] = 0x80; // unit serial number
outbuf[buflen++] = 0x83; // device identification
- if (bdrv_get_type_hint(s->bs) != BDRV_TYPE_CDROM) {
+ if (s->drive_kind == SCSI_HD) {
outbuf[buflen++] = 0xb0; // block limits
outbuf[buflen++] = 0xb2; // thin provisioning
}
@@ -477,7 +480,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
unsigned int opt_io_size =
s->qdev.conf.opt_io_size / s->qdev.blocksize;
- if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) {
+ if (s->drive_kind == SCSI_CD) {
DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
page_code);
return -1;
@@ -547,7 +550,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
return buflen;
}
- if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) {
+ if (s->drive_kind == SCSI_CD) {
outbuf[0] = 5;
outbuf[1] = 0x80;
memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
@@ -678,7 +681,7 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p,
return p[1] + 2;
case 0x2a: /* CD Capabilities and Mechanical Status page. */
- if (bdrv_get_type_hint(bdrv) != BDRV_TYPE_CDROM)
+ if (s->drive_kind != SCSI_CD)
return 0;
p[0] = 0x2a;
p[1] = 0x14;
@@ -905,7 +908,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
goto illegal_request;
break;
case START_STOP:
- if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM && (req->cmd.buf[4] & 2)) {
+ if (s->drive_kind == SCSI_CD && (req->cmd.buf[4] & 2)) {
/* load/eject medium */
bdrv_eject(s->bs, !(req->cmd.buf[4] & 1));
}
@@ -1232,10 +1235,9 @@ static void scsi_destroy(SCSIDevice *dev)
blockdev_mark_auto_del(s->qdev.conf.bs);
}
-static int scsi_disk_initfn(SCSIDevice *dev)
+static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
- int is_cd;
DriveInfo *dinfo;
if (!s->qdev.conf.bs) {
@@ -1243,9 +1245,9 @@ static int scsi_disk_initfn(SCSIDevice *dev)
return -1;
}
s->bs = s->qdev.conf.bs;
- is_cd = bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM;
+ s->drive_kind = kind;
- if (!is_cd && !bdrv_is_inserted(s->bs)) {
+ if (kind == SCSI_HD && !bdrv_is_inserted(s->bs)) {
error_report("Device needs media, but drive is empty");
return -1;
}
@@ -1265,7 +1267,7 @@ static int scsi_disk_initfn(SCSIDevice *dev)
return -1;
}
- if (is_cd) {
+ if (kind == SCSI_CD) {
s->qdev.blocksize = 2048;
} else {
s->qdev.blocksize = s->qdev.conf.logical_block_size;
@@ -1275,35 +1277,103 @@ static int scsi_disk_initfn(SCSIDevice *dev)
s->qdev.type = TYPE_DISK;
qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s);
- bdrv_set_removable(s->bs, is_cd);
+ bdrv_set_removable(s->bs, kind == SCSI_CD);
add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0");
return 0;
}
-static SCSIDeviceInfo scsi_disk_info = {
- .qdev.name = "scsi-disk",
- .qdev.fw_name = "disk",
- .qdev.desc = "virtual scsi disk or cdrom",
- .qdev.size = sizeof(SCSIDiskState),
- .qdev.reset = scsi_disk_reset,
- .init = scsi_disk_initfn,
- .destroy = scsi_destroy,
- .send_command = scsi_send_command,
- .read_data = scsi_read_data,
- .write_data = scsi_write_data,
- .cancel_io = scsi_cancel_io,
- .get_buf = scsi_get_buf,
- .qdev.props = (Property[]) {
- DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf),
- DEFINE_PROP_STRING("ver", SCSIDiskState, version),
- DEFINE_PROP_STRING("serial", SCSIDiskState, serial),
- DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
- DEFINE_PROP_END_OF_LIST(),
- },
+static int scsi_hd_initfn(SCSIDevice *dev)
+{
+ return scsi_initfn(dev, SCSI_HD);
+}
+
+static int scsi_cd_initfn(SCSIDevice *dev)
+{
+ return scsi_initfn(dev, SCSI_CD);
+}
+
+static int scsi_disk_initfn(SCSIDevice *dev)
+{
+ SCSIDriveKind kind;
+
+ if (!dev->conf.bs) {
+ kind = SCSI_HD; /* will die in scsi_initfn() */
+ } else {
+ kind = bdrv_get_type_hint(dev->conf.bs) == BDRV_TYPE_CDROM
+ ? SCSI_CD : SCSI_HD;
+ }
+
+ return scsi_initfn(dev, kind);
+}
+
+#define DEFINE_SCSI_DISK_PROPERTIES() \
+ DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), \
+ DEFINE_PROP_STRING("ver", SCSIDiskState, version), \
+ DEFINE_PROP_STRING("serial", SCSIDiskState, serial)
+
+static SCSIDeviceInfo scsi_disk_info[] = {
+ {
+ .qdev.name = "scsi-hd",
+ .qdev.fw_name = "disk",
+ .qdev.desc = "virtual SCSI disk",
+ .qdev.size = sizeof(SCSIDiskState),
+ .qdev.reset = scsi_disk_reset,
+ .init = scsi_hd_initfn,
+ .destroy = scsi_destroy,
+ .send_command = scsi_send_command,
+ .read_data = scsi_read_data,
+ .write_data = scsi_write_data,
+ .cancel_io = scsi_cancel_io,
+ .get_buf = scsi_get_buf,
+ .qdev.props = (Property[]) {
+ DEFINE_SCSI_DISK_PROPERTIES(),
+ DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+ },{
+ .qdev.name = "scsi-cd",
+ .qdev.fw_name = "disk",
+ .qdev.desc = "virtual SCSI CD-ROM",
+ .qdev.size = sizeof(SCSIDiskState),
+ .qdev.reset = scsi_disk_reset,
+ .init = scsi_cd_initfn,
+ .destroy = scsi_destroy,
+ .send_command = scsi_send_command,
+ .read_data = scsi_read_data,
+ .write_data = scsi_write_data,
+ .cancel_io = scsi_cancel_io,
+ .get_buf = scsi_get_buf,
+ .qdev.props = (Property[]) {
+ DEFINE_SCSI_DISK_PROPERTIES(),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+ },{
+ .qdev.name = "scsi-disk", /* legacy -device scsi-disk */
+ .qdev.fw_name = "disk",
+ .qdev.desc = "virtual SCSI disk or CD-ROM (legacy)",
+ .qdev.size = sizeof(SCSIDiskState),
+ .qdev.reset = scsi_disk_reset,
+ .init = scsi_disk_initfn,
+ .destroy = scsi_destroy,
+ .send_command = scsi_send_command,
+ .read_data = scsi_read_data,
+ .write_data = scsi_write_data,
+ .cancel_io = scsi_cancel_io,
+ .get_buf = scsi_get_buf,
+ .qdev.props = (Property[]) {
+ DEFINE_SCSI_DISK_PROPERTIES(),
+ DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+ }
};
static void scsi_disk_register_devices(void)
{
- scsi_qdev_register(&scsi_disk_info);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(scsi_disk_info); i++) {
+ scsi_qdev_register(&scsi_disk_info[i]);
+ }
}
device_init(scsi_disk_register_devices)
commit 1f56e32a7f4b3a60bbabc499a63f3b858f472306
Author: Markus Armbruster <armbru at redhat.com>
Date: Mon May 16 15:04:52 2011 +0200
ide: Split qdev "ide-drive" into "ide-hd" and "ide-cd"
An "ide-drive" is either a hard disk or a CD-ROM, depending on the
associated BlockDriverState's type hint. Unclean; disk vs. CD belongs
to the guest part, not the host part.
Have separate qdevs "ide-hd" and "ide-cd" to model disk vs. CD in
the guest part.
Keep ide-drive for backward compatibility.
"ide-disk" would perhaps be a nicer name than "ide-hd", but there's
already "scsi-disk", which is like "ide-drive", and will be likewise
split in the next commit. {ide,scsi}-{hd,cd} is the best consistent
set of names I could find within the backward compatibility
straightjacket.
Signed-off-by: Markus Armbruster <armbru at redhat.com>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 90f553b..542ed65 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -1592,13 +1592,15 @@ void ide_bus_reset(IDEBus *bus)
bus->dma->ops->reset(bus->dma);
}
-int ide_init_drive(IDEState *s, BlockDriverState *bs,
+int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind,
const char *version, const char *serial)
{
int cylinders, heads, secs;
uint64_t nb_sectors;
s->bs = bs;
+ s->drive_kind = kind;
+
bdrv_get_geometry(bs, &nb_sectors);
bdrv_guess_geometry(bs, &cylinders, &heads, &secs);
if (cylinders < 1 || cylinders > 16383) {
@@ -1623,8 +1625,7 @@ int ide_init_drive(IDEState *s, BlockDriverState *bs,
s->smart_autosave = 1;
s->smart_errors = 0;
s->smart_selftest_count = 0;
- if (bdrv_get_type_hint(bs) == BDRV_TYPE_CDROM) {
- s->drive_kind = IDE_CD;
+ if (kind == IDE_CD) {
bdrv_set_change_cb(bs, cdrom_change_cb, s);
bs->buffer_alignment = 2048;
} else {
@@ -1729,7 +1730,9 @@ void ide_init2_with_non_qdev_drives(IDEBus *bus, DriveInfo *hd0,
dinfo = i == 0 ? hd0 : hd1;
ide_init1(bus, i);
if (dinfo) {
- if (ide_init_drive(&bus->ifs[i], dinfo->bdrv, NULL,
+ if (ide_init_drive(&bus->ifs[i], dinfo->bdrv,
+ bdrv_get_type_hint(dinfo->bdrv) == BDRV_TYPE_CDROM ? IDE_CD : IDE_HD,
+ NULL,
*dinfo->serial ? dinfo->serial : NULL) < 0) {
error_report("Can't set up IDE drive %s", dinfo->id);
exit(1);
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index aa198b6..c2b35ec 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -558,7 +558,7 @@ uint32_t ide_data_readw(void *opaque, uint32_t addr);
void ide_data_writel(void *opaque, uint32_t addr, uint32_t val);
uint32_t ide_data_readl(void *opaque, uint32_t addr);
-int ide_init_drive(IDEState *s, BlockDriverState *bs,
+int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind,
const char *version, const char *serial);
void ide_init2(IDEBus *bus, qemu_irq irq);
void ide_init2_with_non_qdev_drives(IDEBus *bus, DriveInfo *hd0,
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index 2bb5c27..3bca726 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -98,7 +98,9 @@ IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive)
{
DeviceState *dev;
- dev = qdev_create(&bus->qbus, "ide-drive");
+ dev = qdev_create(&bus->qbus,
+ bdrv_get_type_hint(drive->bdrv) == BDRV_TYPE_CDROM
+ ? "ide-cd" : "ide-hd");
qdev_prop_set_uint32(dev, "unit", unit);
qdev_prop_set_drive_nofail(dev, "drive", drive->bdrv);
qdev_init_nofail(dev);
@@ -118,7 +120,7 @@ typedef struct IDEDrive {
IDEDevice dev;
} IDEDrive;
-static int ide_drive_initfn(IDEDevice *dev)
+static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
{
IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus);
IDEState *s = bus->ifs + dev->unit;
@@ -134,7 +136,7 @@ static int ide_drive_initfn(IDEDevice *dev)
}
}
- if (ide_init_drive(s, dev->conf.bs, dev->version, serial) < 0) {
+ if (ide_init_drive(s, dev->conf.bs, kind, dev->version, serial) < 0) {
return -1;
}
@@ -151,22 +153,69 @@ static int ide_drive_initfn(IDEDevice *dev)
return 0;
}
-static IDEDeviceInfo ide_drive_info = {
- .qdev.name = "ide-drive",
- .qdev.fw_name = "drive",
- .qdev.size = sizeof(IDEDrive),
- .init = ide_drive_initfn,
- .qdev.props = (Property[]) {
- DEFINE_PROP_UINT32("unit", IDEDrive, dev.unit, -1),
- DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf),
- DEFINE_PROP_STRING("ver", IDEDrive, dev.version),
- DEFINE_PROP_STRING("serial", IDEDrive, dev.serial),
- DEFINE_PROP_END_OF_LIST(),
+static int ide_hd_initfn(IDEDevice *dev)
+{
+ return ide_dev_initfn(dev, IDE_HD);
+}
+
+static int ide_cd_initfn(IDEDevice *dev)
+{
+ return ide_dev_initfn(dev, IDE_CD);
+}
+
+static int ide_drive_initfn(IDEDevice *dev)
+{
+ return ide_dev_initfn(dev,
+ bdrv_get_type_hint(dev->conf.bs) == BDRV_TYPE_CDROM
+ ? IDE_CD : IDE_HD);
+}
+
+#define DEFINE_IDE_DEV_PROPERTIES() \
+ DEFINE_PROP_UINT32("unit", IDEDrive, dev.unit, -1), \
+ DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf), \
+ DEFINE_PROP_STRING("ver", IDEDrive, dev.version), \
+ DEFINE_PROP_STRING("serial", IDEDrive, dev.serial)
+
+static IDEDeviceInfo ide_dev_info[] = {
+ {
+ .qdev.name = "ide-hd",
+ .qdev.fw_name = "drive",
+ .qdev.desc = "virtual IDE disk",
+ .qdev.size = sizeof(IDEDrive),
+ .init = ide_hd_initfn,
+ .qdev.props = (Property[]) {
+ DEFINE_IDE_DEV_PROPERTIES(),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+ },{
+ .qdev.name = "ide-cd",
+ .qdev.fw_name = "drive",
+ .qdev.desc = "virtual IDE CD-ROM",
+ .qdev.size = sizeof(IDEDrive),
+ .init = ide_cd_initfn,
+ .qdev.props = (Property[]) {
+ DEFINE_IDE_DEV_PROPERTIES(),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+ },{
+ .qdev.name = "ide-drive", /* legacy -device ide-drive */
+ .qdev.fw_name = "drive",
+ .qdev.desc = "virtual IDE disk or CD-ROM (legacy)",
+ .qdev.size = sizeof(IDEDrive),
+ .init = ide_drive_initfn,
+ .qdev.props = (Property[]) {
+ DEFINE_IDE_DEV_PROPERTIES(),
+ DEFINE_PROP_END_OF_LIST(),
+ }
}
};
-static void ide_drive_register(void)
+static void ide_dev_register(void)
{
- ide_qdev_register(&ide_drive_info);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ide_dev_info); i++) {
+ ide_qdev_register(&ide_dev_info[i]);
+ }
}
-device_init(ide_drive_register);
+device_init(ide_dev_register);
commit 77a5a0001bd9eaee9da7dc8f0b69702d56b0cc67
Author: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
Date: Mon May 16 13:56:53 2011 +0100
qed: support for growing images
The .bdrv_truncate() operation resizes images and growing is easy to
implement in QED. Simply check that the new size is valid and then
update the image_size header field to reflect the new size.
Signed-off-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/block/qed.c b/block/qed.c
index d8d6ea2..da0bf31 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -1333,7 +1333,27 @@ static BlockDriverAIOCB *bdrv_qed_aio_flush(BlockDriverState *bs,
static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset)
{
- return -ENOTSUP;
+ BDRVQEDState *s = bs->opaque;
+ uint64_t old_image_size;
+ int ret;
+
+ if (!qed_is_image_size_valid(offset, s->header.cluster_size,
+ s->header.table_size)) {
+ return -EINVAL;
+ }
+
+ /* Shrinking is currently not supported */
+ if ((uint64_t)offset < s->header.image_size) {
+ return -ENOTSUP;
+ }
+
+ old_image_size = s->header.image_size;
+ s->header.image_size = offset;
+ ret = qed_write_header_sync(s);
+ if (ret < 0) {
+ s->header.image_size = old_image_size;
+ }
+ return ret;
}
static int64_t bdrv_qed_getlength(BlockDriverState *bs)
commit f6a00aa150ec685966ca9453601328fdb2f87f34
Author: Dmitry Konishchev <konishchev at gmail.com>
Date: Wed May 18 15:03:59 2011 +0400
qemu_img: is_not_zero() optimization
I run qemu-img under profiler and realized, that most of CPU time is
consumed by is_not_zero() function. I had made a couple of optimizations
on it and got the following output for `time qemu-img convert -O qcow2
volume.qcow2 snapshot.qcow2`:
Original qemu-img:
real 0m56.159s
user 0m34.670s
sys 0m12.079s
Patched qemu-img:
real 0m34.805s
user 0m18.445s
sys 0m12.552s
Signed-off-by: Dmitry Konishchev <konishchev at gmail.com>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/qemu-img.c b/qemu-img.c
index 1da5484..4f162d1 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -496,14 +496,37 @@ static int img_commit(int argc, char **argv)
return 0;
}
+/*
+ * Checks whether the sector is not a zero sector.
+ *
+ * Attention! The len must be a multiple of 4 * sizeof(long) due to
+ * restriction of optimizations in this function.
+ */
static int is_not_zero(const uint8_t *sector, int len)
{
+ /*
+ * Use long as the biggest available internal data type that fits into the
+ * CPU register and unroll the loop to smooth out the effect of memory
+ * latency.
+ */
+
int i;
- len >>= 2;
- for(i = 0;i < len; i++) {
- if (((uint32_t *)sector)[i] != 0)
+ long d0, d1, d2, d3;
+ const long * const data = (const long *) sector;
+
+ len /= sizeof(long);
+
+ for(i = 0; i < len; i += 4) {
+ d0 = data[i + 0];
+ d1 = data[i + 1];
+ d2 = data[i + 2];
+ d3 = data[i + 3];
+
+ if (d0 || d1 || d2 || d3) {
return 1;
+ }
}
+
return 0;
}
commit 6f321e93abb27b4e7ceb228b4204aa304e95daad
Author: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
Date: Mon May 9 16:45:40 2011 +0100
qed: Periodically flush and clear need check bit
One strategy to limit the startup delay of consistency check when
opening image files is to ensure that the file is marked dirty for as
little time as possible.
QED currently marks the image dirty when the first allocating write
request is issued and clears the dirty bit again when the image is
cleanly closed. In practice that means the image is marked dirty for
most of a guest's lifetime and prone to being in a dirty state upon
crash or power failure.
It is safe to clear the dirty bit after all allocating write requests
have completed and a flush has been performed. This patch adds a timer
after the last allocating write request completes. When the timer fires
it will flush and then clear the dirty bit. The timer is set to 5
seconds and is cancelled upon arrival of a new allocating write request.
Signed-off-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/block/qed.c b/block/qed.c
index c8c5930..d8d6ea2 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -12,6 +12,7 @@
*
*/
+#include "qemu-timer.h"
#include "trace.h"
#include "qed.h"
#include "qerror.h"
@@ -291,6 +292,88 @@ static CachedL2Table *qed_new_l2_table(BDRVQEDState *s)
static void qed_aio_next_io(void *opaque, int ret);
+static void qed_plug_allocating_write_reqs(BDRVQEDState *s)
+{
+ assert(!s->allocating_write_reqs_plugged);
+
+ s->allocating_write_reqs_plugged = true;
+}
+
+static void qed_unplug_allocating_write_reqs(BDRVQEDState *s)
+{
+ QEDAIOCB *acb;
+
+ assert(s->allocating_write_reqs_plugged);
+
+ s->allocating_write_reqs_plugged = false;
+
+ acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs);
+ if (acb) {
+ qed_aio_next_io(acb, 0);
+ }
+}
+
+static void qed_finish_clear_need_check(void *opaque, int ret)
+{
+ /* Do nothing */
+}
+
+static void qed_flush_after_clear_need_check(void *opaque, int ret)
+{
+ BDRVQEDState *s = opaque;
+
+ bdrv_aio_flush(s->bs, qed_finish_clear_need_check, s);
+
+ /* No need to wait until flush completes */
+ qed_unplug_allocating_write_reqs(s);
+}
+
+static void qed_clear_need_check(void *opaque, int ret)
+{
+ BDRVQEDState *s = opaque;
+
+ if (ret) {
+ qed_unplug_allocating_write_reqs(s);
+ return;
+ }
+
+ s->header.features &= ~QED_F_NEED_CHECK;
+ qed_write_header(s, qed_flush_after_clear_need_check, s);
+}
+
+static void qed_need_check_timer_cb(void *opaque)
+{
+ BDRVQEDState *s = opaque;
+
+ /* The timer should only fire when allocating writes have drained */
+ assert(!QSIMPLEQ_FIRST(&s->allocating_write_reqs));
+
+ trace_qed_need_check_timer_cb(s);
+
+ qed_plug_allocating_write_reqs(s);
+
+ /* Ensure writes are on disk before clearing flag */
+ bdrv_aio_flush(s->bs, qed_clear_need_check, s);
+}
+
+static void qed_start_need_check_timer(BDRVQEDState *s)
+{
+ trace_qed_start_need_check_timer(s);
+
+ /* Use vm_clock so we don't alter the image file while suspended for
+ * migration.
+ */
+ qemu_mod_timer(s->need_check_timer, qemu_get_clock_ns(vm_clock) +
+ get_ticks_per_sec() * QED_NEED_CHECK_TIMEOUT);
+}
+
+/* It's okay to call this multiple times or when no timer is started */
+static void qed_cancel_need_check_timer(BDRVQEDState *s)
+{
+ trace_qed_cancel_need_check_timer(s);
+ qemu_del_timer(s->need_check_timer);
+}
+
static int bdrv_qed_open(BlockDriverState *bs, int flags)
{
BDRVQEDState *s = bs->opaque;
@@ -406,7 +489,10 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
BdrvCheckResult result = {0};
ret = qed_check(s, &result, true);
- if (!ret && !result.corruptions && !result.check_errors) {
+ if (ret) {
+ goto out;
+ }
+ if (!result.corruptions && !result.check_errors) {
/* Ensure fixes reach storage before clearing check bit */
bdrv_flush(s->bs);
@@ -416,6 +502,9 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
}
}
+ s->need_check_timer = qemu_new_timer_ns(vm_clock,
+ qed_need_check_timer_cb, s);
+
out:
if (ret) {
qed_free_l2_cache(&s->l2_cache);
@@ -428,6 +517,9 @@ static void bdrv_qed_close(BlockDriverState *bs)
{
BDRVQEDState *s = bs->opaque;
+ qed_cancel_need_check_timer(s);
+ qemu_free_timer(s->need_check_timer);
+
/* Ensure writes reach stable storage */
bdrv_flush(bs->file);
@@ -809,6 +901,8 @@ static void qed_aio_complete(QEDAIOCB *acb, int ret)
acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs);
if (acb) {
qed_aio_next_io(acb, 0);
+ } else if (s->header.features & QED_F_NEED_CHECK) {
+ qed_start_need_check_timer(s);
}
}
}
@@ -1014,11 +1108,17 @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
{
BDRVQEDState *s = acb_to_s(acb);
+ /* Cancel timer when the first allocating request comes in */
+ if (QSIMPLEQ_EMPTY(&s->allocating_write_reqs)) {
+ qed_cancel_need_check_timer(s);
+ }
+
/* Freeze this request if another allocating write is in progress */
if (acb != QSIMPLEQ_FIRST(&s->allocating_write_reqs)) {
QSIMPLEQ_INSERT_TAIL(&s->allocating_write_reqs, acb, next);
}
- if (acb != QSIMPLEQ_FIRST(&s->allocating_write_reqs)) {
+ if (acb != QSIMPLEQ_FIRST(&s->allocating_write_reqs) ||
+ s->allocating_write_reqs_plugged) {
return; /* wait for existing request to finish */
}
diff --git a/block/qed.h b/block/qed.h
index 1d1421f..388fdb3 100644
--- a/block/qed.h
+++ b/block/qed.h
@@ -78,6 +78,9 @@ enum {
QED_MIN_TABLE_SIZE = 1, /* in clusters */
QED_MAX_TABLE_SIZE = 16,
QED_DEFAULT_TABLE_SIZE = 4,
+
+ /* Delay to flush and clean image after last allocating write completes */
+ QED_NEED_CHECK_TIMEOUT = 5, /* in seconds */
};
typedef struct {
@@ -157,6 +160,10 @@ typedef struct {
/* Allocating write request queue */
QSIMPLEQ_HEAD(, QEDAIOCB) allocating_write_reqs;
+ bool allocating_write_reqs_plugged;
+
+ /* Periodic flush and clear need check flag */
+ QEMUTimer *need_check_timer;
} BDRVQEDState;
enum {
diff --git a/trace-events b/trace-events
index a00b63c..385cb00 100644
--- a/trace-events
+++ b/trace-events
@@ -220,6 +220,9 @@ disable qed_write_table(void *s, uint64_t offset, void *table, unsigned int inde
disable qed_write_table_cb(void *s, void *table, int flush, int ret) "s %p table %p flush %d ret %d"
# block/qed.c
+disable qed_need_check_timer_cb(void *s) "s %p"
+disable qed_start_need_check_timer(void *s) "s %p"
+disable qed_cancel_need_check_timer(void *s) "s %p"
disable qed_aio_complete(void *s, void *acb, int ret) "s %p acb %p ret %d"
disable qed_aio_setup(void *s, void *acb, int64_t sector_num, int nb_sectors, void *opaque, int is_write) "s %p acb %p sector_num %"PRId64" nb_sectors %d opaque %p is_write %d"
disable qed_aio_next_io(void *s, void *acb, int ret, uint64_t cur_pos) "s %p acb %p ret %d cur_pos %"PRIu64""
commit 5fc09ca5c315b87539db882fba175a7955783530
Author: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
Date: Mon May 9 16:45:39 2011 +0100
qemu-tool: Stub out qemu-timer functions
Block drivers may use timers for flushing metadata to disk or
reconnecting to a network drive. Stub out the following functions in
qemu-tool.c:
QEMUTimer *qemu_new_timer_ns(QEMUClock *clock, int scale,
QEMUTimerCB *cb, void *opaque)
void qemu_free_timer(QEMUTimer *ts)
void qemu_del_timer(QEMUTimer *ts)
void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time)
int64_t qemu_get_clock_ns(QEMUClock *clock)
They will result in timers never firing when linked against qemu-tool.o.
Signed-off-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/qemu-tool.c b/qemu-tool.c
index f4a6ad0..41e5c41 100644
--- a/qemu-tool.c
+++ b/qemu-tool.c
@@ -19,6 +19,7 @@
#include <sys/time.h>
QEMUClock *rt_clock;
+QEMUClock *vm_clock;
FILE *logfile;
@@ -71,3 +72,27 @@ int qemu_set_fd_handler2(int fd,
void qemu_notify_event(void)
{
}
+
+QEMUTimer *qemu_new_timer(QEMUClock *clock, int scale,
+ QEMUTimerCB *cb, void *opaque)
+{
+ return qemu_malloc(1);
+}
+
+void qemu_free_timer(QEMUTimer *ts)
+{
+ qemu_free(ts);
+}
+
+void qemu_del_timer(QEMUTimer *ts)
+{
+}
+
+void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time)
+{
+}
+
+int64_t qemu_get_clock_ns(QEMUClock *clock)
+{
+ return 0;
+}
commit 4d29b50a41810684ad34e44352a630eb1dd94b58
Author: Jan Kiszka <jan.kiszka at siemens.com>
Date: Mon May 9 17:48:19 2011 +0200
ahci: Fix crashes on duplicate BH registration
If ahci_dma_set_inactive is called a while there is still a pending BH
from a previous run, we will crash on the second run of
ahci_check_cmd_bh as it overwrites AHCIDevice::check_bh. Avoid this
broken and redundant duplicate registration.
Signed-off-by: Jan Kiszka <jan.kiszka at siemens.com>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index c6e0c77..744d19d 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -1066,9 +1066,11 @@ static int ahci_dma_set_inactive(IDEDMA *dma)
ad->dma_cb = NULL;
- /* maybe we still have something to process, check later */
- ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad);
- qemu_bh_schedule(ad->check_bh);
+ if (!ad->check_bh) {
+ /* maybe we still have something to process, check later */
+ ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad);
+ qemu_bh_schedule(ad->check_bh);
+ }
return 0;
}
commit 3bfe4dbf92d98d8c119b74f6c90b42fd8f289b58
Author: Jes Sorensen <Jes.Sorensen at redhat.com>
Date: Mon May 9 17:32:20 2011 +0200
Add documentation for qemu_progress_{init,print}()
Signed-off-by: Jes Sorensen <Jes.Sorensen at redhat.com>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/qemu-common.h b/qemu-common.h
index bba8dfe..b851b20 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -341,7 +341,7 @@ void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count,
void qemu_progress_init(int enabled, float min_skip);
void qemu_progress_end(void);
-void qemu_progress_print(float percent, int max);
+void qemu_progress_print(float delta, int max);
#define QEMU_FILE_TYPE_BIOS 0
#define QEMU_FILE_TYPE_KEYMAP 1
diff --git a/qemu-progress.c b/qemu-progress.c
index a4894c0..8ebe8ef 100644
--- a/qemu-progress.c
+++ b/qemu-progress.c
@@ -96,6 +96,13 @@ static void progress_dummy_init(void)
state.end = progress_dummy_end;
}
+/*
+ * Initialize progress reporting.
+ * If @enabled is false, actual reporting is suppressed. The user can
+ * still trigger a report by sending a SIGUSR1.
+ * Reports are also suppressed unless we've had at least @min_skip
+ * percent progress since the last report.
+ */
void qemu_progress_init(int enabled, float min_skip)
{
state.min_skip = min_skip;
@@ -111,14 +118,25 @@ void qemu_progress_end(void)
state.end();
}
-void qemu_progress_print(float percent, int max)
+/*
+ * Report progress.
+ * @delta is how much progress we made.
+ * If @max is zero, @delta is an absolut value of the total job done.
+ * Else, @delta is a progress delta since the last call, as a fraction
+ * of @max. I.e. the delta is @delta * @max / 100. This allows
+ * relative accounting of functions which may be a different fraction of
+ * the full job, depending on the context they are called in. I.e.
+ * a function might be considered 40% of the full job if used from
+ * bdrv_img_create() but only 20% if called from img_convert().
+ */
+void qemu_progress_print(float delta, int max)
{
float current;
if (max == 0) {
- current = percent;
+ current = delta;
} else {
- current = state.current + percent / 100 * max;
+ current = state.current + delta / 100 * max;
}
if (current > 100) {
current = 100;
commit 2860e3eb9695d7596507e82b98074937fbd7fa18
Author: Kevin Wolf <kwolf at redhat.com>
Date: Mon May 9 11:42:03 2011 +0200
ide: Turn debug messages into assertions
These printfs aren't really debug messages, but clearly indicate a bug if they
ever become effective. Noone uses DEBUG_IDE, let's re-enable the check
unconditionally and make it an assertion instead of printfs in the device
emulation.
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha at linux.vnet.ibm.com>
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index f5ac932..a4726ad 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -296,12 +296,8 @@ void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val)
*/
if (bm->bus->dma->aiocb) {
qemu_aio_flush();
-#ifdef DEBUG_IDE
- if (bm->bus->dma->aiocb)
- printf("ide_dma_cancel: aiocb still pending\n");
- if (bm->status & BM_STATUS_DMAING)
- printf("ide_dma_cancel: BM_STATUS_DMAING still pending\n");
-#endif
+ assert(bm->bus->dma->aiocb == NULL);
+ assert((bm->status & BM_STATUS_DMAING) == 0);
}
} else {
bm->cur_addr = bm->addr;
commit 1e71db3087ae9256fcfd8e9e322ad86edce48a96
Author: Stefan Weil <weil at mail.berlios.de>
Date: Sun May 8 09:01:52 2011 +0200
hw/xen_disk: Remove unused local variable
cppcheck report:
hw/xen_disk.c:309: style:
Variable 'len' is assigned a value that is never used
Cc: Kevin Wolf <kwolf at redhat.com>
Signed-off-by: Stefan Weil <weil at mail.berlios.de>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/hw/xen_disk.c b/hw/xen_disk.c
index 233c8c9..0c298af 100644
--- a/hw/xen_disk.c
+++ b/hw/xen_disk.c
@@ -316,7 +316,7 @@ static int ioreq_map(struct ioreq *ioreq)
static int ioreq_runio_qemu_sync(struct ioreq *ioreq)
{
struct XenBlkDev *blkdev = ioreq->blkdev;
- int i, rc, len = 0;
+ int i, rc;
off_t pos;
if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) {
@@ -339,7 +339,6 @@ static int ioreq_runio_qemu_sync(struct ioreq *ioreq)
ioreq->v.iov[i].iov_len);
goto err;
}
- len += ioreq->v.iov[i].iov_len;
pos += ioreq->v.iov[i].iov_len;
}
break;
@@ -359,7 +358,6 @@ static int ioreq_runio_qemu_sync(struct ioreq *ioreq)
ioreq->v.iov[i].iov_len);
goto err;
}
- len += ioreq->v.iov[i].iov_len;
pos += ioreq->v.iov[i].iov_len;
}
break;
commit 4ee964184fcbca430f8851bad1df8f966c0faad2
Author: Jes Sorensen <Jes.Sorensen at redhat.com>
Date: Fri May 6 11:39:11 2011 +0200
qemu-img.c: Remove superfluous parenthesis
Signed-off-by: Jes Sorensen <Jes.Sorensen at redhat.com>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/qemu-img.c b/qemu-img.c
index e825123..1da5484 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -785,7 +785,7 @@ static int img_convert(int argc, char **argv)
nb_sectors = total_sectors;
local_progress = (float)100 /
- (nb_sectors / MIN(nb_sectors, (cluster_sectors)));
+ (nb_sectors / MIN(nb_sectors, cluster_sectors));
for(;;) {
int64_t bs_num;
@@ -856,7 +856,7 @@ static int img_convert(int argc, char **argv)
sector_num = 0; // total number of sectors converted so far
nb_sectors = total_sectors - sector_num;
local_progress = (float)100 /
- (nb_sectors / MIN(nb_sectors, (IO_BUF_SIZE / 512)));
+ (nb_sectors / MIN(nb_sectors, IO_BUF_SIZE / 512));
for(;;) {
nb_sectors = total_sectors - sector_num;
@@ -1331,7 +1331,7 @@ static int img_rebase(int argc, char **argv)
bdrv_get_geometry(bs, &num_sectors);
local_progress = (float)100 /
- (num_sectors / MIN(num_sectors, (IO_BUF_SIZE / 512)));
+ (num_sectors / MIN(num_sectors, IO_BUF_SIZE / 512));
for (sector = 0; sector < num_sectors; sector += n) {
/* How many sectors can we handle with the next read? */
commit 5be4aab7015d347b7506167c516f04c3ad171e4c
Author: Kevin Wolf <kwolf at redhat.com>
Date: Mon May 2 17:32:54 2011 +0200
posix-aio-compat: Fix idle_threads counter
A thread should only be counted as idle when it really is waiting for new
requests. Without this patch, sometimes too few threads are started as busy
threads are counted as idle.
Not sure if it makes a difference in practice outside some artificial
qemu-io/qemu-img tests, but I think the change makes sense in any case.
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/posix-aio-compat.c b/posix-aio-compat.c
index 6d4df9d..f3cc868 100644
--- a/posix-aio-compat.c
+++ b/posix-aio-compat.c
@@ -322,7 +322,9 @@ static void *aio_thread(void *unused)
while (QTAILQ_EMPTY(&request_list) &&
!(ret == ETIMEDOUT)) {
+ idle_threads++;
ret = cond_timedwait(&cond, &lock, &ts);
+ idle_threads--;
}
if (QTAILQ_EMPTY(&request_list))
@@ -331,7 +333,6 @@ static void *aio_thread(void *unused)
aiocb = QTAILQ_FIRST(&request_list);
QTAILQ_REMOVE(&request_list, aiocb, node);
aiocb->active = 1;
- idle_threads--;
mutex_unlock(&lock);
switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) {
@@ -353,13 +354,11 @@ static void *aio_thread(void *unused)
mutex_lock(&lock);
aiocb->ret = ret;
- idle_threads++;
mutex_unlock(&lock);
if (kill(pid, aiocb->ev_signo)) die("kill failed");
}
- idle_threads--;
cur_threads--;
mutex_unlock(&lock);
@@ -371,7 +370,6 @@ static void spawn_thread(void)
sigset_t set, oldset;
cur_threads++;
- idle_threads++;
/* block all signals */
if (sigfillset(&set)) die("sigfillset");
commit 086cf4d3bde08ca7785a9e09b38ee20636ee4f78
Author: Andrea Arcangeli <aarcange at redhat.com>
Date: Tue May 3 22:03:39 2011 +0200
ide: cleanup warnings
Add \n.
Signed-off-by: Andrea Arcangeli <aarcange at redhat.com>
Signed-off-by: Kevin Wolf <kwolf at redhat.com>
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index 65cb56c..f5ac932 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -298,9 +298,9 @@ void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val)
qemu_aio_flush();
#ifdef DEBUG_IDE
if (bm->bus->dma->aiocb)
- printf("ide_dma_cancel: aiocb still pending");
+ printf("ide_dma_cancel: aiocb still pending\n");
if (bm->status & BM_STATUS_DMAING)
- printf("ide_dma_cancel: BM_STATUS_DMAING still pending");
+ printf("ide_dma_cancel: BM_STATUS_DMAING still pending\n");
#endif
}
} else {
More information about the Spice-commits
mailing list